If/else if on a text column in R

Viewed 73

I'm a new to R and need help with the following.

Have Need
Male_18_24_pn 18_24
Male_25_39_pn 25_39
Male_40_64_pn 40_64
Male_65_84_pn 65_84
Male_85_plus_pn 85_plus
Female_18_24_pn 18_24

I need to create the "Need" column using the "Have" column, wondering how I can achieve this in R. As a initial effort, I tried the following code to test but got warning message and every cell of "Need" populated with "18_24":

if (str_detect(pe_1P_new$Have,"18_24")) {pe_1P_new$Need= "18_24"}
Warning message:
In if (str_detect(pe_1P_new$Have, "18_24")) { :
  the condition has length > 1 and only the first element will be used

Your help is greatly appreciated. Thanks in advance!

4 Answers

You can do:

require(data.table)

dt = data.table(
  have = c("Male_18_24_pn", "Male_25_39_pn",
           "Male_40_64_pn", "Male_65_84_pn",
           "Male_85_plus_pn", "Female_18_24_pn")
)

dt[ , need := gsub('(Male|Female)_(.+)(_pn)', '\\2', have) ]

Base R solution:

dt$need = gsub('(Male|Female)_(.+)(_pn)', '\\2', dt$have)

No need for a loop or any conditional statements. You can extract the needed information with the help of a vectorized function, such as gsub() and a simple regex.

[Volunteer edit] This is an attempt to explain the regular expression logic of that substitution pattern (see ?regex for a very terse but complete description). You need to understand what a capture class can do.

gsub('(Male|Female)#This matches "Male" or "Female", the first "capture classes" 
       _(.+)# Second capture class, matching anything after an underscore
       (_pn)',# ... up to but not including an "_pn"
       '\\2', # replace anything matched with only the second capture class
       dt$have)

You will not be able to run this version because the carriage retruns and spaces get in the way of the regex engine process.

We could use trimws as well

dt[, need := trimws(have, whitespace = '[[:alpha:]]+_|_[[:alpha:]]+')]

We can try gsub like below

> dt[, need := gsub(".*?_(.*)_.*", "\\1", have)][]
              have    need
1:   Male_18_24_pn   18_24
2:   Male_25_39_pn   25_39
3:   Male_40_64_pn   40_64
4:   Male_65_84_pn   65_84
5: Male_85_plus_pn 85_plus
6: Female_18_24_pn   18_24

You can use the package {unglue}

df <- data.frame(
  Have = c("Male_18_24_pn", "Male_25_39_pn",
           "Male_40_64_pn", "Male_65_84_pn",
           "Male_85_plus_pn", "Female_18_24_pn")
)

library(unglue)
unglue_unnest(df, Have, "{}_{Need}_pn", remove = FALSE)
#>              Have    Need
#> 1   Male_18_24_pn   18_24
#> 2   Male_25_39_pn   25_39
#> 3   Male_40_64_pn   40_64
#> 4   Male_65_84_pn   65_84
#> 5 Male_85_plus_pn 85_plus
#> 6 Female_18_24_pn   18_24

Created on 2021-07-20 by the reprex package (v0.3.0)

Related