使用dplyr summarise_if多函数带参数未达预期结果的技术问询
问题原因及解决方案
原因分析
你的代码得到不符合预期的结果,核心问题出在输出结构和转置逻辑上:
summarise_if(包括现代替代语法across)生成的是宽格式单行数据框:每个列名是「原数值列名_统计量名」的复合格式,比如原列height会生成height_mean、height_sd等列,整个结果仅1行(汇总所有行的统计值)。- 直接用
t()转置后,这些复合列名会变成行名,原唯一的行则转为1列,最终得到的是「行:原列_统计量,列:单个值列」的结构,和你预期的「行:统计量,列:原数值列」结构完全不符,所以出现了不符合预期的列数。
优雅解决方案
方案1:Tidyverse标准流程(推荐)
用across替代已软弃用的summarise_if,配合pivot_longer和pivot_wider调整结构,完全贴合tidy数据原则:
library(dplyr) library(tidyr) df %>% # 对所有数值列应用统计函数,生成宽格式结果 summarise(across(where(is.numeric), list(mean = ~mean(., na.rm = TRUE), sd = ~sd(., na.rm = TRUE), median = ~median(., na.rm = TRUE), min = ~min(., na.rm = TRUE), max = ~max(., na.rm = TRUE)))) %>% # 把宽格式转成长格式,拆分复合列名为原列名和统计量 pivot_longer(everything(), names_sep = "_", names_to = c("col", "stat")) %>% # 重新转宽,让统计量作为行,原列作为列 pivot_wider(names_from = col, values_from = value) %>% # 把统计量列设为行名(可选,按需调整) column_to_rownames("stat")
方案2:基于原代码的快速调整
如果想沿用原代码的转置思路,只需在转置后拆分行名再整理:
library(dplyr) library(tidyr) df %>% summarise(across(where(is.numeric), list(mean = ~mean(., na.rm = TRUE), sd = ~sd(., na.rm = TRUE), median = ~median(., na.rm = TRUE), min = ~min(., na.rm = TRUE), max = ~max(., na.rm = TRUE)))) %>% t() %>% as.data.frame() %>% # 把转置后的行名转为列 rownames_to_column("col_stat") %>% # 拆分复合列名为原列名和统计量 separate(col_stat, into = c("col", "stat"), sep = "_") %>% # 转宽得到预期结构 pivot_wider(names_from = col, values_from = V1) %>% column_to_rownames("stat")
方案3:用专业汇总工具简化操作
如果只是需要快速生成数值列的常用统计量,skimr包可以一步到位,输出结构更直观:
library(skimr) library(tidyr) skim(df) %>% # 筛选数值列的统计结果 filter(skim_type == "numeric") %>% select(stat, value, variable) %>% # 调整为预期的宽格式 pivot_wider(names_from = variable, values_from = value) %>% column_to_rownames("stat")
内容的提问来源于stack exchange,提问作者Rootsyl
相关产品推荐
相关产品推荐

