R,带有ifelse和grepl函数的for循环没有给出预期的结果

R, for loop with ifelse and grepl function does not give expected results

我正在尝试查找具有 my_list 和数据框 (df) 的匹配字符串,并且根据 TRUE/FALSE 我需要在 df 中填充 new_name 列,并在匹配中使用第一个字符串列表 (my_list[[i]][1]) 如果 TRUE ,或 "cat" 列值如果不匹配。

我的数据框如下:

name <- c("NETFLIX.COM", "BlueTV", "smv", "trafi", "alkatel")
cat<- c("none", "none", "none", "transportation", "communication")
df<-data.frame(name, cat)

我的名单:

travel<- c("travel","air_com", "AIRCAT", "tivago")
leasure<- c("leasure","MTV", "NETFLIX.COM")
my_list<- list(travel, leasure)

我使用 ifelse 和 grepl 的 for 循环如下:

for (j in 1:nrow(df)) {
      for (i in 1:length(my_list)) {
        df[j, "new_name"]<- ifelse( 
        grepl(paste(my_list[[i]], collapse="|"), tolower(df[j, "name"])),
          my_list[[i]][1], 
          df[j, "cat"])

预期输出为:

df["new_name"]<- c("leasure", "none", "none", "transportation", "communication")
df

name            cat       new_name
1 NETFLIX.COM           none        leasure
2      BlueTV           none           none
3         smv           none           none
4       trafi transportation transportation
5     alkatel  communication  communication

目前,通过我编写的 for 循环,我获得了“cat”列的精确副本,这意味着所有情况在 ifelse 函数中都被视为不匹配 (FALSE)。我注意到这里出了什么问题...... 如有任何帮助,我们将不胜感激!

这是使用 stringr::str_replace_all -

的一种方法
travel<- c("travel","air_com", "AIRCAT", "tivago")
leasure<- c("leasure","MTV", "NETFLIX.COM")
#Create a named list
my_list<- dplyr::lst(travel, leasure)


result <- stringr::str_replace_all(df$name, setNames(names(my_list), 
          sapply(my_list, paste0, collapse = '|')))

#If the result is same as original value keep the previous cat.
df$new_name <- ifelse(result == df$name, df$cat, result)
df

#         name            cat       new_name
#1 NETFLIX.COM           none        leasure
#2      BlueTV           none           none
#3         smv           none           none
#4       trafi transportation transportation
#5     alkatel  communication  communication

这里重要的部分是这段代码-

setNames(names(my_list), sapply(my_list, paste0, collapse = '|'))

#travel|air_com|AIRCAT|tivago      leasure|MTV|NETFLIX.COM 
#                    "travel"                    "leasure" 

这意味着每当在字符串中遇到模式 travel|air_com|AIRCAT|tivago 时,它将 return "travel" 作为输出,与 "leasure".

相同

在这种情况下使用 ifelse() 没有意义:它用于矢量化选择。但是如果你有正确的模式匹配,你的代码就会工作。不幸的是,对于 j == 1i == 2(当您期望匹配时),您的模式是

"leasure|MTV|NETFLIX.COM"

并且您正在尝试将其匹配到 tolower(df[j, "name"]),即

"netflix.com"

您应该将两个字符串都映射为小写,或者在 grepl() 调用中设置 ignore.case = TRUE。例如,

name <- c("NETFLIX.COM", "BlueTV", "smv", "trafi", "alkatel")
cat<- c("none", "none", "none", "transportation", "communication")
df<-data.frame(name, cat)

travel<- c("travel","air_com", "AIRCAT", "tivago")
leasure<- c("leasure","MTV", "NETFLIX.COM")
my_list<- list(travel, leasure)

for (j in 1:nrow(df)) {
  for (i in 1:length(my_list)) {
    df[j, "new_name"] <- 
      if( grepl(paste(my_list[[i]], collapse="|"), df[j, "name"],
            ignore.case = TRUE))
        my_list[[i]][1] 
      else df[j, "cat"]
  }
}
df
#>          name            cat       new_name
#> 1 NETFLIX.COM           none        leasure
#> 2      BlueTV           none           none
#> 3         smv           none           none
#> 4       trafi transportation transportation
#> 5     alkatel  communication  communication

reprex package (v2.0.0)

于 2021-08-10 创建

一般来说,使用模式匹配来查找字符串是否在列表中是很棘手的;请务必小心,不要让 my_list 中的字符串包含任何 grepl() 在正则表达式中视为特殊的字符。对于您的示例,您将获得与 grepl() 使用测试

给出的结果相同的结果
tolower(df[j, "name"]) %in% tolower(my_list[[i]])

但这并非适用于所有可能的 name 值:grepl() 代码将允许部分匹配(例如 df[i, "name"] 等于 "netflix.com in a long string")和 %in% 不会。