Having issue with extract function in {tidyr}

Viewed 98

I have a dataframe that looks like this. I named this "df_raw".

"HRHHID\t15\tHOUSEHOLD IDENTIFIER\t(Part 1)\t1- 15"
"HRMONTH\t2\tMONTH OF INTERVIEW\t16-17"
"HRYEAR4\t4\tYEAR OF INTERVIEW\t\t18-21"
"HURESPLI\t2\tLINE NUMBER OF THE RESPONDENT\tCURRENT\t22 - 23" "FILLER\t2\t\t27 - 28" ...

I am trying to extract the number range at the end, such as 1- 15, 16-17, 18-21 and so on.. I prepared my regex syntax on a regex tester (https://regexr.com/63nl1) and it seems to target what I want.

Regex: \d+(\s?)-(\s?)\d+$

Then I go to R and try to use the extract function in tidyr (this is the first time using it for me) and I get the error: "Error: regex should define 1 groups; found."

df_raw %>% 
extract(col = value, into = "", regex = "\\d+(\\s?)-(\\s?)\\d+$")

Can anyone help me understand how to group my regex so that the extract function works?

Thanks very much, David

3 Answers

We may do this with trimws by specifying the whitespace to remove everything until the \t

library(dplyr)
library(stringr)
df_raw %>%
      mutate(new = str_remove_all(trimws(value, whitespace = ".*\t"), "\\s+"))

-ouput

# A tibble: 5 x 2
  value                                                          new  
  <chr>                                                          <chr>
1 "HRHHID\t15\tHOUSEHOLD IDENTIFIER\t(Part 1)\t1- 15"            1-15 
2 "HRMONTH\t2\tMONTH OF INTERVIEW\t16-17"                        16-17
3 "HRYEAR4\t4\tYEAR OF INTERVIEW\t\t18-21"                       18-21
4 "HURESPLI\t2\tLINE NUMBER OF THE RESPONDENT\tCURRENT\t22 - 23" 22-23
5 "FILLER\t2\t\t27 - 28"                                         27-28

Or with extract, the code in OP's post shows more than one capture group whereas we want to extract only into a single column. In that case, just get the digits (\\d+) followed by any spaces (\\s*), -, then any spaces and digits, wrap it with (...) as a capture group at the end ($) of the string

library(tidyr)
df_raw %>% 
    extract(col = value, into = "new",  "(\\d+\\s*-\\s*\\d+)$")
# A tibble: 5 x 1
  new    
  <chr>  
1 1- 15  
2 16-17  
3 18-21  
4 22 - 23
5 27 - 28

Or using just base R

trimws(df_raw$value, whitespace = ".*\t")
[1] "1- 15"   "16-17"   "18-21"   "22 - 23" "27 - 28"

data

df_raw <- structure(list(value = c("HRHHID\t15\tHOUSEHOLD IDENTIFIER\t(Part 1)\t1- 15", 
"HRMONTH\t2\tMONTH OF INTERVIEW\t16-17", "HRYEAR4\t4\tYEAR OF INTERVIEW\t\t18-21", 
"HURESPLI\t2\tLINE NUMBER OF THE RESPONDENT\tCURRENT\t22 - 23", 
"FILLER\t2\t\t27 - 28")), row.names = c(NA, -5L), class = c("tbl_df", 
"tbl", "data.frame"))

I believe you have a problem because your regex must match whitespaces between the digits ("(\\s?)-(\\s?)", but your data shows whitespaces inconsistently. Perhaps you should match "none or more" whitespaces, with "\\s*", without the parenthesis or ? sign. You can also use tidyr::separate():

library(tidyr)

df_raw %>% separate(value, sep="\\d+\\s*-\\s*\\d+$")

If you want to make the new column consistent in regard to whitespaces, you can use str_extract() to mutate a new column, then pipe it to some transformation

df_raw %>% mutate(new_column = str_extract(value, "\\d+\\s*-\\s*\\d+$") %>%
                  str_replace("\\s*-\\s*", " - ")

Split your string by \t

then you get a list

then unnest the list

group and keep the last in the group:

library(dplyr)
df <- df_raw %>% 
    mutate(x = strsplit(value, "\t")) %>% 
    unnest(cols = c(x)) %>% 
    group_by(value) %>% 
    slice(n())

Output:

  value                                                          x      
  <chr>                                                          <chr>  
1 "FILLER\t2\t\t27 - 28"                                         27 - 28
2 "HRHHID\t15\tHOUSEHOLD IDENTIFIER\t(Part 1)\t1- 15"            1- 15  
3 "HRMONTH\t2\tMONTH OF INTERVIEW\t16-17"                        16-17  
4 "HRYEAR4\t4\tYEAR OF INTERVIEW\t\t18-21"                       18-21  
5 "HURESPLI\t2\tLINE NUMBER OF THE RESPONDENT\tCURRENT\t22 - 23" 22 - 23
Related