在replace_na中使用选择器

编程语言 2026-07-09

请看下面的示例:

df<-data.frame(x_1=c(NA,1,NA), 
               x_2=c(1,1,NA),
               y=c(NA,1,1))

replace_na 中使用选择器,所需的最少代码字符数量是多少?

这可以工作:

df %>%
  mutate(across(matches("^x_(.*)$"), \(x) {
    replace_na(x, 0)
  }))

但例如这一个就不行:

df %>%
    replace_na(matches("^x_(.*)$"), 0)

它告诉我

! matches() 必须在一个 selecting 函数中使用。 ℹ 详见 ?tidyselect::faq-selection-context以获取详情。 运行 rlang::last_trace() 以查看错误发生的位置。 调用来源: signal_abort(cnd, .file)

解决方案

你在问“最少字符数”,让我们先把这本来就多写的写法列举出来。

  • %>% -> |> (-1)
  • |> -> 内联函数调用 (-2)
  • 去掉正则表达式中未使用的分组括号,"^x_(.*)$" -> "^x_.*$"
  • 去掉正则表达式中不必要的 .*$,现在只有 "^x_"
  • 去掉单表达式匿名函数中的 {} 括号
  • ~ 表达式(λ表达式)替代并去掉 \(x)
  • 去掉 ~,改用函数及其参数(如 across(.., replace_na, 0) 的做法)(注:该行为已弃用,据称不再“受支持”,但仍然可用)
  • replace_na 转为 coalesce

表达式,从你给出的77开始一直到:

mutate(df, across(matches("^x_"), coalesce, 0)) # 47

我们也可以

  • 再去掉那些恼人的空格以节省一些字符(再省3 个)

你也可以在 tidyr 上提交一个功能请求,请求他们在 replace_na 中添加必要的标记,以满足 matches() 要求必须在可信函数内完成的要求(尽管这可能需要超过47个字符 ;-).


对于“在replace_na中使用选择器”这一点,具体可以使用

library(tidyselect) # `eval_select`; `matches` is already re-exported by dplyr
df |>
  replace_na(lapply(eval_select(matches("^x_"), df), \(ign) 0))

尽管我认为这并不符合“最少字符数”的精神(也不利于可读性)。另外,我也不想在表达式中再次使用 df

如果你只是需要 matches(.),那么我们可以稍微缩短它,使用

df |>
  replace_na(sapply(grepv("^x_", names(df)), \(ign) list(0)))

不过我猜你想要的不只是 matches(.),所以这是一个相当边缘的情况。(我可以把它再缩短到两字符,但为了强调我忽略了实际值,我保留为“ign”)

如果你想原生使用 matches(.),你需要完成以下几点:

  • 形式参数 replace_na(data, replace, ...) 要求你分配一个名字集合及其替换值的 list,这与一组列名和单一替换值不兼容,因此要让它工作你要么改变形式定义,要么滥用当前尚未使用的 ...
  • 让多态 replace 接受一组值的 list 或像 matches(.) 这样的选择器
  • 将内部 value_args 的值改为引用 ... 的第一个元素

最终,replace_na(data.frame) 方法似乎旨在要求对字段及其NA替换值进行逐对声明,这与像 replace_na(matches("^x_(.*)$"), 0) 这样的单一替换值表达式的概念并不完全一致。

为此,我提出一个更具针对性的函数以供讨论(在这次匆促的草案中尽量让它与 tidyr::replace_na 尽可能相似):

replace_na1 <- function(data, cols, replace) {
  names <- names(tidyselect::eval_select(cols, data))
  col_args <- as.character(glue::glue("data${names}"))
  for (i in seq_along(names)) {
    name <- names[[i]]
    col <- data[[name]]
    col_arg <- col_args[[i]]
    if (vctrs::vec_any_missing(col)) {
      missing <- vctrs::vec_detect_missing(col)
      data[[name]] <- vctrs::vec_assign(x = col, i = missing, value = replace, x_arg = col_arg)
    }
  }
  data
}

replace_na1(df, matches("^x_"), 0) # 34 characters!
#   x_1 x_2  y
# 1   0   1 NA
# 2   1   1  1
# 3   0   0  1

显然,::-引用可以根据所包含的包导入来减少。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章