【问题标题】:R, for loop with ifelse and grepl function does not give expected resultsR,带有 ifelse 和 grepl 函数的 for 循环没有给出预期的结果
【发布时间】:2021-10-13 23:58:47
【问题描述】:

我正在尝试使用 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)。我注意到这里出了什么问题...... 任何帮助将不胜感激!

【问题讨论】:

  • 在该循环中使用 ifelse() 没有意义。使用if 语句进行流量控制。 ifelse() 用于矢量化选择。

标签: r for-loop if-statement grepl


【解决方案1】:

在这种情况下使用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% 不允许。

【讨论】:

  • 谢谢,您的代码在 R 中完美运行。但是现在(可能这必须是单独的问题)我在 RShiny 中遇到了类似的问题。新列“cat_new”不包含“休闲”类别。我认为这与闪亮 (DT::datatable(df)) 有关,但想知道如何修复它
【解决方案2】:

这是使用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,它都会返回"travel" 作为输出,"leasure" 也是如此。

【讨论】:

    猜你喜欢
    • 2015-01-12
    • 1970-01-01
    • 2021-12-08
    • 2012-08-31
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多