在replace_na中使用选择器
请看下面的示例:
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
显然,::-引用可以根据所包含的包导入来减少。