我正在运行一个函数,mutate该函数采用预定义的列名称列表(在字符串向量中)的按行平均值。
例如,假设我们有以下数据:
library(tidyverse)
df <- data.frame(x=1:10, y=21:30)
A <- c("x", "y")
Run Code Online (Sandbox Code Playgroud)
c_across没有/的情况下执行此操作的方法all_of是:
df %>% rowwise() %>%
mutate(M = sum(!!!syms(A)))
Run Code Online (Sandbox Code Playgroud)
但是,使用c_across/ all_of,我们可以写:
df %>% rowwise() %>%
mutate(M = sum(c_across(all_of(A))))
Run Code Online (Sandbox Code Playgroud)
这两者在功能上是等效的,但是在我的真实数据(有数千个观察值)上,后者非常慢(比基本 R 解决方案慢 10 倍以上)。
我喜欢c_across/all_of解决方案,因为它看起来比 更好!!!syms,但是将其与我的数据一起使用是不可行的,因为它太慢了。为什么慢这么多?
长话短说,调用c_across+时all_of,特别是按行运行时,开销非常高。事实是,您以某种rowwise()方式运行,并且必须一遍又一遍地c_across调用+ 。all_of
c_across从和的源代码all_of(参见附图)我们可以看到,在获得 中列出的所需列之前A,我们有很多中间步骤,这比直接计算符号 的计算量要大得多syms(A)。但是,您应该意识到这syms也不够高效,因为它相当于map(x, sym)使用maptosym不像直接选择列那么简单(同样的原因,许多中间步骤,syms然后您需要运行!!!,请参阅源代码syms和sym)。
一种更直接、更有效的方法是“矢量化”您的操作,而不是“按行”执行它。更具体地说,您可以在A开头选择列,例如df[A](一次性解决方案,这样您就不需要在所有行上重复执行相同的操作),然后rowSums为您的目标运行。@Gki 的回答中强调了这种方法。
c_across+all_of源代码> c_across
function (cols)
{
mask <- peek_mask()
cols <- enquo(cols)
if (quo_is_missing(cols)) {
c_across_missing_cols_deprecate_warn()
cols <- quo_set_expr(cols, expr(everything()))
}
vars <- c_across_setup(!!cols, mask = mask)
cols <- mask$current_cols(vars)
vec_c(!!!cols, .name_spec = zap())
}
<bytecode: 0x0000016758a08778>
<environment: namespace:dplyr>
> all_of
function (x)
{
if (!has_vars()) {
lifecycle::deprecate_soft("1.2.0", I("Using `all_of()` outside of a selecting function"),
details = paste("See details at", peek_vars_link()))
return(x)
}
vars <- peek_vars(fn = "all_of")
as_indices_impl(x, vars = vars, strict = TRUE)
}
<bytecode: 0x00000167585374c0>
<environment: namespace:tidyselect>
Run Code Online (Sandbox Code Playgroud)
syms+sym源代码> syms
function (x)
{
map(x, sym)
}
<bytecode: 0x0000016758740e70>
<environment: namespace:rlang>
> sym
function (x)
{
if (is_symbol(x)) {
return(x)
}
if (identical(x, "")) {
return(missing_arg())
}
if (!is_string(x)) {
abort_coercion(x, "a symbol")
}
.Call(ffi_symbol, x)
}
<bytecode: 0x0000016758747ed0>
<environment: namespace:rlang>
Run Code Online (Sandbox Code Playgroud)