How to find the first space in a sentence with Regular Expressions within R

Viewed 1118

I have a data.frame which contains a column with values like this:

[2] wererea 187w 50HH (1)
[3] werewefrea 145y 50HH (4)
[5] weredssda 100NH (2)

I would like to separate this column into two columns and the separation should be done at the first space. How I do it now is with the help of the ] like this:

DF <- separate(DF , "X", into = c("X","Y"), sep = "\\] ", remove = TRUE)
DF$X<- gsub("\\[", "", DF$X)

The way to select the last space goes like this:

DF <- separate(DF, "X", into = c("X","Y"), sep = " (?=[^ ]*$)", remove = TRUE)

I found this RegEx:

^([^ ]*) 

But this will select the first space AND everthing before it. But how do you select only the first space?

3 Answers

You may leverage the ICU constrained-width lookbehind (i.e. allowing {n,m} quantifiers to some extent) assuming that the first non-whitespace chunk is not a too large value. Say, this will work if the number of whitespace chars at the string start is up to 1000:

separate(DF, "X", into = c("X","Y"), sep = "(?<=^\\S{0,1000})\\s+", remove = TRUE)

The (?<=^\\S{0,1000})\\s+ pattern will match 1 whitespaces that are preceded with start of string followed with 0 to 1000 non-whitespace chars.

Else, you may use tidyr::extract:

library(tidyr)
DF %>%
    extract(col="X", into = c("X","Y"), regex="^(\\S+)\\s+(.*)")

If you want to avoid the first [...] around a number, use a slight variation:

extract(col="X", into = c("X","Y"), regex="^\\[(\\d+)]\\s+(.*)")

The ^(\\S+)\\s+(.*) regex matches

  • ^ - start of string
  • (\\S+) - Capturing group 1: one or more non-whitespace chars (it will the first column value from the into list) (in the other pattern, (\\d+) will capture 1 or more digits)
  • \\s+ - 1+ whitespaces
  • (.*) - Capturing group 2: any one or more chars (it will the second column value from the into list).

The question was not specific regarding the exact output required so it is assumed that the output should be two columns X and Y such that the first column is the numeric row number and the second column is the text after the space that separates it from the row number. In that case the default regular expression used by separate should be fine if the junk is removed afterwards:

library(dplyr)
library(tidyr)

DF %>%
  separate(X, into = c("junk", "X", "Y"), 
    extra = "merge", convert = TRUE, remove = TRUE) %>%
  select(-junk)

giving:

  X                        Y
1 2    wererea 187w 50HH (1)
2 3 werewefrea 145y 50HH (4)
3 5      weredssda 100NH (2)

Alternative

An alternative to removing junk afterwards is removing the [ before:

DF %>%
  mutate(X = sub(".", "", X)) %>%
  separate(X, into = c("X", "Y"), 
     extra = "merge", convert = TRUE, remove = TRUE)
##   X                        Y
## 1 2    wererea 187w 50HH (1)
## 2 3 werewefrea 145y 50HH (4)
## 3 5      weredssda 100NH (2)

Note 1

Note that there is an open issue #397 in tidyr which if implemented would reduce the above to the following code. (Update: tidyr 0.8.2 now implements this feature)

DF %>%
  separate(X, into = c(NA, "X", "Y"), 
    extra = "merge", convert = TRUE, remove = TRUE)

Note 2

The input in reproducible form:

DF <-
structure(list(X = c("[2] wererea 187w 50HH (1)", "[3] werewefrea 145y 50HH (4)", 
"[5] weredssda 100NH (2)")), class = "data.frame", row.names = c(NA, 
-3L))

Here is another variation. It pulls the number out of the column, then gets rid of it in the first column. An advantage here is that the number that is pulled out is of class numeric (you could always convert from character with the other methods as well).

library(tidyverse)

df %>% 
  mutate(Y = parse_number(X),
         X = gsub("\\[.*\\]", "", X))
#>                           X Y
#> 1     wererea 187w 50HH (1) 2
#> 2  werewefrea 145y 50HH (4) 3
#> 3       weredssda 100NH (2) 5

Created on 2018-09-20 by the reprex package (v0.2.0).

Related