如何通过编程方式构建dplyr的summarize汇总语句?
dplyr动态生成summarise语句问题
我在进行dplyr编程时遇到问题:希望对任意数量的变量执行group_by操作(因此用到across),随后基于三组长度相同的向量:要应用函数的目标列、待应用的统计函数、生成的新列名称,编写summarize语句。
类似map或者apply语句的效果,我想最终执行的代码逻辑如下:
data %>% group_by(group_column) %>% summarize(new_name_1 = function_1(column_1), new_name_2 = function_2(column_2))
我知道如果使用across的话可以通过names参数调整列名,但我不确定across是不是最合适的实现方式。另外我后续要应用在规模较大的dataframe上,所以不希望额外计算不需要的列。
示例场景说明
期望输出结果
mtcars %>% group_by(across(c("cyl", "carb"))) %>% summarise(across(c("disp", "hp"), list(mean = mean, sd = sd))) %>% select(cyl, carb, disp_mean, hp_sd) #> `summarise()` regrouping output by 'cyl' (override with `.groups` argument) #> # A tibble: 9 x 4 #> # Groups: cyl [3] #> cyl carb disp_mean hp_sd #> <dbl> <dbl> <dbl> <dbl> #> 1 4 1 91.4 16.1 #> 2 4 2 117. 24.9 #> 3 6 1 242. 3.54 #> 4 6 4 164. 7.51 #> 5 6 6 145 NA #> 6 8 2 346. 14.4 #> 7 8 3 276. 0 #> 8 8 4 406. 21.7 #> 9 8 8 301 NA
目前实际输出结果
mtcars %>% group_by(across(c("cyl", "carb"))) %>% summarise(across(c("disp", "hp"), list(mean = mean, sd = sd))) #> `summarise()` regrouping output by 'cyl' (override with `.groups` argument) #> # A tibble: 9 x 6 #> # Groups: cyl [3] #> cyl carb disp_mean disp_sd hp_mean hp_sd #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 4 1 91.4 21.4 77.4 16.1 #> 2 4 2 117. 27.1 87 24.9 #> 3 6 1 242. 23.3 108. 3.54 #> 4 6 4 164. 4.39 116. 7.51 #> 5 6 6 145 NA 175 NA #> 6 8 2 346. 43.4 162. 14.4 #> 7 8 3 276. 0 180 0 #> 8 8 4 406. 57.8 234 21.7 #> 9 8 8 301 NA 335 NA
核心疑问
如何实现符合要求的动态summarize语句,避免生成多余计算列?
解决方案
不要直接用across给所有目标列套用所有函数,这种方式必然会生成多余的计算列。你可以通过rlang包的动态表达式注入功能,实现三组向量的一一对应计算,完全不会产生多余运算,适配大规模数据集的性能要求。
实现步骤
- 先定义你需要的参数向量:
library(dplyr) library(rlang) # 分组列向量 group_cols <- c("cyl", "carb") # 三个等长的对应向量:目标列、要应用的函数、生成的新列名 target_cols <- c("disp", "hp") apply_funs <- list(mean, sd) new_colnames <- c("disp_mean", "hp_sd")
- 构造每个计算项的表达式:
sum_exprs <- purrr::map2( .x = target_cols, .y = apply_funs, ~ expr(!!sym(.x) %>% !!.y()) ) %>% set_names(new_colnames)
- 把表达式注入到
summarise中执行计算:
mtcars %>% group_by(across(all_of(group_cols))) %>% summarise(!!!sum_exprs, .groups = "drop_last")
运行上述代码得到的结果和你预期的输出完全一致,只会计算你指定的列组合,不会产生任何多余的中间计算结果,内存占用和运算效率都远高于先全量计算再筛选列的方案。如果不想依赖purrr,也可以用base R的mapply实现同样的表达式构造逻辑。
内容的提问来源于stack exchange,提问作者spillway18
相关产品推荐
相关产品推荐

