R语言数据框中按列名后缀与time条件分组求均值的高效方法
高效批量计算分组列均值的通用解法
需求说明
当数据框中time==1时,对每组名称以...1S和...2S结尾的列(如ex1S、ex2S)求均值;当time==2时,对每组名称以...1C和...2C结尾的列(如ex1C、ex2C)求均值,最终生成以ave_为前缀的均值列。实际数据中存在多组此类列,需要无需手动逐个定义的通用解法。
当前低效解法
library(tidyverse) Data %>% mutate(ave_ex = case_when( time == 1 ~ mean(c(ex1S, ex2S)), time == 2 ~ mean(c(ex1C, ex2C)) ), ave_id = case_when( time == 1 ~ mean(c(id1S, id2S)), time == 2 ~ mean(c(id1C, id2C)) )) %>% select(-c(ex1S:id2C))
数据示例
Data = read.table(text = " order DV score time ex1S ex2S ex1C ex2C id1S id2S id1C id2C k t s-c ac 1 1 8 5 6 1 2 4 3 7 400 30 s-c bc 2 1 8 5 6 1 2 4 3 7 400 30 s-c ac 3 2 8 5 6 1 2 4 3 7 600 50 s-c bc 4 2 8 5 6 1 2 4 3 7 600 50 ", header = TRUE)
期望输出
order time DV score k t ave_ex ave_id s-c 1 ac 1 400 30 6.5 3 s-c 1 bc 2 400 30 6.5 3 s-c 2 ac 3 600 50 3.5 5 s-c 2 bc 4 600 50 3.5 5
通用解法一:基于长格式数据的批量处理
通过拆分列名提取前缀,按time筛选对应后缀的列,批量计算均值后再转回宽格式,自动适配任意多组目标列:
library(tidyverse) compute_group_means <- function(df) { # 筛选所有符合*1S/*2S/*1C/*2C模式的列 target_cols <- colnames(df)[str_detect(colnames(df), "\\d(S|C)$")] df %>% # 拆分列名为前缀(如ex、id)和后缀(如1S、2C) pivot_longer(cols = all_of(target_cols), names_to = c("prefix", "suffix"), names_pattern = "(.*)(\\d[S|C])$") %>% # 根据time匹配对应的后缀类型 filter((time == 1 & str_ends(suffix, "S")) | (time == 2 & str_ends(suffix, "C"))) %>% # 按行标识和前缀分组计算均值 group_by(across(-c(prefix, suffix, value)), prefix) %>% summarise(ave_value = mean(value), .groups = "drop") %>% # 转回宽格式并添加ave_前缀 pivot_wider(names_from = prefix, values_from = ave_value, names_prefix = "ave_") %>% # 合并原数据的非目标列 right_join(df %>% select(-all_of(target_cols)), by = intersect(colnames(.), colnames(df %>% select(-all_of(target_cols))))) %>% # 调整列顺序与期望输出一致 select(order, time, DV, score, k, t, starts_with("ave_")) } # 运行函数得到结果 Desired_output <- compute_group_means(Data) print(Desired_output)
通用解法二:基于across的动态列计算
利用dplyr的across函数动态匹配列名,无需转换长格式,代码更简洁:
library(tidyverse) Data %>% rowwise() %>% mutate( # 自动提取所有目标列的前缀(如ex、id) across( unique(str_remove(colnames(.)[str_detect(colnames(.), "\\d(S|C)$")], "\\d(S|C)$")), # 根据time选择对应后缀的列计算均值 ~ mean(c( pick(paste0(cur_column(), if_else(time == 1, "1S", "1C"))), pick(paste0(cur_column(), if_else(time == 1, "2S", "2C"))) )), .names = "ave_{.col}" ) ) %>% ungroup() %>% # 删除原有的*1S/*2S/*1C/*2C列 select(-matches("\\d(S|C)$"))
内容的提问来源于stack exchange,提问作者Simon Harmel
相关产品推荐
相关产品推荐

