为何在R中使用c_across(all_of())执行mutate函数远慢于!!!syms()?
为什么
c_across()结合all_of()的行计算写法比!!!syms()慢? 两种写法的性能差异源于底层执行逻辑的本质不同:
!!!syms(A)的高效逻辑:syms(A)将字符串列名转换为dplyr可识别的符号对象,!!!(非拼接操作符)直接把这些符号展开成列的直接引用。在rowwise()环境中,sum实际是基于列向量做批量计算后再按行聚合,没有额外的对象转换步骤,充分利用了R的向量化计算优势,内存开销极低。c_across(all_of(A))的性能瓶颈:c_across()的设计定位是在rowwise()上下文里为每一行单独生成临时向量——它会逐行提取指定列的数值,组装成一个小型向量,再对这个向量执行sum。这种逐行操作带来了三重额外开销:- 为每一行创建独立的小向量,产生大量临时对象
- 频繁的对象创建与回收会消耗更多内存资源
sum只能针对单个小向量执行,无法利用向量化计算的效率优势,等同于逐行循环处理
性能验证示例
用microbenchmark可以直观看到差异:
library(microbenchmark) library(tidyverse) # 生成1万行的测试数据 df <- data.frame(x = rnorm(10000), y = rnorm(10000)) A <- c("x", "y") # 基准测试 bench <- microbenchmark( syms写法 = df %>% rowwise() %>% mutate(M = sum(!!!syms(A))), c_across写法 = df %>% rowwise() %>% mutate(M = sum(c_across(all_of(A)))), times = 10 ) print(bench)
测试结果会显示c_across写法的耗时是syms写法的数倍甚至十几倍,数据量越大,这个差距越明显。
适用场景提示
c_across()并非设计用于简单的行求和/均值操作,它更适合需要对行内多列做复杂自定义逐行处理(比如行内条件过滤、自定义函数计算)的场景。如果只是简单的行聚合操作,除了!!!syms()写法,还可以用更高效的非rowwise()写法:
df %>% mutate(M = rowSums(.[A]))
内容的提问来源于stack exchange,提问作者Jake Ireland
相关产品推荐
相关产品推荐

