如何使用dplyr按分组计算数据框所有数值列的分位数
问题原因
代码无法运行核心是两个问题:
across()语法使用错误:across()第一个参数传列筛选规则,第二个参数传要应用的计算逻辑,你把where(is.numeric)列筛选逻辑和计算公式混写在第二个参数位,无法被正确识别。- 原自定义函数
quibble返回的分位值列固定命名为x,和原数据的x列重名,多列计算时会触发列名冲突;且多列返回的嵌套结果直接unnest会因为结构不统一报错。
正确实现方法
方法1:先转长格式再计算(逻辑最简洁,推荐)
不需要嵌套结果再解套,先把所有数值列转换为长格式,按「分组+原列名」双维度分组后直接计算分位数即可,自动过滤非数值列、自动处理缺失值:
library(tidyverse) # 原测试数据构造 df <- tibble( grp = rep(c("A","B"), each = 5), x = c(rnorm(5, -0.25, 1), rnorm(5, 0, 1.5)), y = c(rnorm(5, 0.25, 1), rnorm(5, 0, 0.5)), z = letters[1:10], ) df[3,2] <- NA # 调整自定义函数,避免列名冲突 quibble <- function(x, q = c(0.25, 0.5, 0.75), dropNA = TRUE) { tibble(value = quantile(x, q, na.rm = dropNA), q = q) } # 计算代码 df %>% pivot_longer(cols = where(is.numeric), names_to = "col_name", values_to = "calc_val") %>% group_by(grp, col_name) %>% summarise(quibble(calc_val, q = c(0.25, 0.75), dropNA = TRUE), .groups = "drop")
运行后输出结果结构如下(随机数生成数值不同,分位值会有差异):
# A tibble: 8 × 4 grp col_name value q <chr> <chr> <dbl> <dbl> 1 A x -0.353 0.25 2 A x 0.484 0.75 3 A y -0.419 0.25 4 A y 1.15 0.75 5 B x -1.27 0.25 6 B x 1.05 0.75 7 B y -0.168 0.25 8 B y 0.252 0.75
方法2:保留across写法,嵌套计算后解套
如果要保留across逐列计算的逻辑,需要把每列的计算结果存为嵌套列表,转成长格式后再统一展开:
df %>% group_by(grp) %>% summarise( across(where(is.numeric), ~list(quibble(.x, c(0.25, 0.75), dropNA = TRUE))) ) %>% pivot_longer(cols = -grp, names_to = "col_name", values_to = "res") %>% unnest(res)
输出结果和方法1完全一致。
注意:不需要额外加载其他包,所有函数都是tidyverse内置,非数值列z会被自动排除,缺失值会按照
na.rm=TRUE的设置忽略后计算。
内容的提问来源于stack exchange,提问作者greengrass62
相关产品推荐
相关产品推荐

