根据单独列中的字符串匹配,有条件地替换多列中的值

Conditionally replace values across multiple columns based on string match in a separate column

我正在尝试根据不同列中的字​​符串匹配有条件地替换多列中的值,但我希望能够使用 across() 函数在一行代码中完成此操作,但我不断收到对我来说不太有意义的错误。我觉得这可能是一个简单的解决方案,所以如果有人能指出我正确的方向,那就太棒了!

df <- data.frame("type" = c("Park", "Neighborhood", "Airport", "Park", "Neighborhood", "Neighborhood"),
               "total" = c(34, 56, 75, 89, 21, 56),
               "group_a" = c(30, 26, 45, 60, 3, 46),
               "group_b" = c(4, 30, 30, 29, 18, 10))

# working but not concise
df %>%
  mutate(total = ifelse(str_detect(type, "Park"), NA, total),
         group_a = ifelse(str_detect(type, "Park"), NA, group_a),
         group_b = ifelse(str_detect(type, "Park"), NA, group_b))

  
# concise but not working
df %>% mutate(across(total, group_a, group_b), ifelse(str_detect(type, "Park"), NA, .))

更新

我们得到了一个适用于我的虚拟数据集但不适用于我的真实数据的解决方案,因此我将分享我的真实数据框的一小段,其中更改了数字并隐藏了组织名称。当我在这些数据上 运行 这行代码 (df %>% mutate(across(c(Attempts, Canvasses, Completes)), ~ifelse(str_detect(long_name, "park-cemetery"), NA, .))) 时,我得到以下错误信息:

Error: Problem with mutate() input ..2. x Input ..2 must be a vector, not a formula object. i Input ..2 is ~ifelse(str_detect(long_name, "park-cemetery"), NA, .).

这是产生此错误的一小部分数据样本:

df <- structure(list(Org = c("OrgName", "OrgName", "OrgName", "OrgName", 
"OrgName", "OrgName", "OrgName", "OrgName", "OrgName", "OrgName"
), nCode = c("M34", "R36", "R46", "X29", "M31", "K39", "Q12", 
"Q39", "X41", "K27"), Attempts = c(100, 100, 100, 100, 100, 100, 
100, 100, 100, 100), Canvasses = c(80, 80, 80, 80, 80, 80, 80, 
80, 80, 80), Completes = c(50, 50, 50, 50, 50, 50, 50, 50, 50, 
50), van_nocc_id = c(999, 999, 999, 999, 999, 999, 999, 999, 
999, 999), van_name = c("M-Upper West Side", "SI-Rosebank", "SI-Tottenville", 
"BX-park-cemetery-etc-Bronx", "M-Stuyvesant Town-Cooper Village", 
"BK-Kensington", "Q-Broad Channel", "Q-Lindenwood", "BX-Wakefield", 
"BK-East New York"), boro_short = c("M", "SI", "SI", "BX", "M", 
"BK", "Q", "Q", "BX", "BK"), long_name = c("Upper West Side", 
"Rosebank", "Tottenville", "park-cemetery-etc-Bronx", "Stuyvesant Town-Cooper Village", 
"Kensington", "Broad Channel", "Lindenwood", "Wakefield", "East New York"
)), row.names = c(NA, -10L), class = "data.frame")

最后更新

错位右括号的诅咒!感谢大家的帮助...正确的解决方案是 df %>% mutate(across(c(Attempts, Canvasses, Completes), ~ifelse(str_detect(long_name, "park-cemetery"), NA, .)))

如果你使用新引入的函数across(这是处理这个任务的正确方法),你必须指定inside across本身您要应用的功能。在这种情况下,函数 ifelse(...) 必须是 purrr 风格的 lambda(因此从 ~ 开始)。查看 across documentation 并查找参数 .cols.fns.

df %>% 
  mutate(across(c(total, group_a, group_b), ~ifelse(str_detect(type, "Park"), NA, .)))

输出

#           type total group_a group_b
# 1         Park    NA      NA      NA
# 2 Neighborhood    56      26      30
# 3      Airport    75      45      30
# 4         Park    NA      NA      NA
# 5 Neighborhood    21       3      18
# 6 Neighborhood    56      46      10

更新:没多久就弄明白了!只需要将列放在向量中:

# concise AND working!
df %>% mutate(across(c(total, group_a, group_b)), ifelse(str_detect(type, "Park"), NA, .))

我最初尝试过此操作,但将列放在引号中...不要那样做 :)

这里有一个data.table解决方案。

require(data.table)
df <- data.frame("type" = c("Park", "Neighborhood", "Airport", "Park", "Neighborhood", "Neighborhood"),
               "total" = c(34, 56, 75, 89, 21, 56),
               "group_a" = c(30, 26, 45, 60, 3, 46),
               "group_b" = c(4, 30, 30, 29, 18, 10))

setDT(df)
df[type == "Park", c("total", "group_a", "group_b") := NA]