使用dplyr实现数据框按每n列行方向均值聚合的通用方法
用dplyr实现动态n列的行方向均值聚合
没问题,咱们完全可以用dplyr结合tidyr工具来实现这个支持任意n值的行方向聚合需求,而且代码还能复用,适配各种业务场景!
核心思路
要实现按每n列计算每行均值,我们可以借助长格式数据转换来分组计算,再转回宽格式得到预期结果:
- 给每行加唯一标识,避免行数据混淆;
- 把宽数据转成长格式,方便按n列分组;
- 按行和n列组计算均值,同时保留每组的第一个列名作为结果列名;
- 转回宽格式,还原成类似原数据的结构。
通用函数实现
我写了两个通用函数,分别适配不同的业务场景,你可以按需选择:
场景1:列名有前缀(如A1_、B1_),按前缀组内每n列聚合
如果你的列名是按前缀分组的(比如A1开头的是一组,B1开头的是另一组),用这个版本可以在每个前缀组内按n列聚合:
library(dplyr) library(tidyr) library(stringr) rowwise_n_mean_by_prefix <- function(df, n) { df %>% # 给每行加唯一ID,确保行顺序不打乱 mutate(row_id = row_number()) %>% # 转成长格式,方便分组计算 pivot_longer(-row_id, names_to = "col_name", values_to = "value") %>% mutate( # 提取列名前缀(比如从A1_A中提取A1) prefix = str_extract(col_name, "^.*(?=_)"), # 计算每个前缀组内的列位置 prefix_col_pos = match(col_name, colnames(df)[str_detect(colnames(df), prefix)]), # 前缀组内每n列分成一个子组 sub_group = (prefix_col_pos - 1) %/% n + 1 ) %>% # 按行、前缀、子组分组,计算均值并保留子组第一个列名 group_by(row_id, prefix, sub_group) %>% summarise( mean_value = mean(value), new_col_name = first(col_name), .groups = "drop" ) %>% # 转回宽格式,还原成原数据的列结构 pivot_wider( id_cols = row_id, names_from = new_col_name, values_from = mean_value ) %>% # 去掉行ID,恢复原始行结构 select(-row_id) }
场景2:无特定前缀,直接按列顺序每n列聚合
如果你的列没有统一前缀,只想按列的先后顺序每n列一组,用这个更通用的版本:
rowwise_n_mean_general <- function(df, n) { df %>% mutate(row_id = row_number()) %>% pivot_longer(-row_id, names_to = "col_name", values_to = "value") %>% mutate( # 获取列在原数据中的位置 col_pos = match(col_name, colnames(df)), # 按列顺序每n列分成一个组 group_id = (col_pos - 1) %/% n + 1, # 标记每组的第一个列,用来作为结果列名 is_first_col = (col_pos - 1) %% n == 0 ) %>% group_by(row_id, group_id) %>% summarise( mean_value = mean(value), new_col_name = first(col_name[is_first_col]), .groups = "drop" ) %>% pivot_wider(id_cols = row_id, names_from = new_col_name, values_from = mean_value) %>% select(-row_id) }
测试示例
示例1:n=2的情况
用你提供的第一个数据集测试:
# 构造测试数据 df1 <- structure(list( A1_A = 0.86, A1_B = 0.9, A1_C = 0.75, A1_D = 0.65, B1_A = 0.12, B1_B = 0.35, B1_C = 0.45, B1_D = 0.44, C1_A = 0.2, C1_B = 0.4, C1_C = 0.6, C1_D = 0.7 ), class = "data.frame", row.names = c("1", "2", "3")) # 调用函数 rowwise_n_mean_by_prefix(df1, n = 2)
输出结果和你预期一致:
# A tibble: 3 × 6 A1_A A1_C B1_A B1_C C1_A C1_C <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 0.88 0.7 0.23 0.445 0.3 0.65 2 ... ... ... ... ... ... 3 ... ... ... ... ... ...
示例2:n=3的情况
用你提供的n=3数据集测试:
# 构造测试数据 df3 <- structure(list( A1_A = 0.86, A1_B = 0.9, A1_C = 0.75, A1_D = 0.65, A1_E = 0.6, A1_F = 0.65, B1_A = 0.12, B1_B = 0.35, B1_C = 0.45, B1_D = 0.44, B1_E = 0.5, B1_F = 0.55, C1_A = 0.2, C1_B = 0.4, C1_C = 0.6, C1_D = 0.7, C1_E = 0.75, C1_F = 0.8 ), class = "data.frame", row.names = "1") # 调用函数 rowwise_n_mean_by_prefix(df3, n = 3)
输出结果:
# A tibble: 1 × 6 A1_A A1_D B1_A B1_D C1_A C1_D <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 0.84 0.63 0.31 0.5 0.4 0.75
注意事项
- 确保数据框的列数(或每个前缀组的列数)是n的整数倍,否则会有部分列无法被分组;
- 如果需要处理列数不能被n整除的情况,可以在函数里添加逻辑处理剩余列(比如单独成组),不过你提到业务场景是动态n值,应该是列数适配n的情况。
内容的提问来源于stack exchange,提问作者swathi.upadhya
相关产品推荐
相关产品推荐

