如何在R中按多分组对多列应用自定义函数并生成新列
解决方案:分组批量计算修正Z分数并生成新列
核心思路
使用dplyr包的group_by()完成分组,结合across()(dplyr 1.0.0+推荐用法)批量处理指定列,同时通过命名规则自动生成带_zscore后缀的新列。
完整代码实现
先加载依赖包,再运行以下代码:
# 加载dplyr包 library(dplyr) # 保留自定义修正Z分数函数 mod.zscore <- function(df, i) { vec <- df[[i]] median.vec <- median(vec, na.rm = TRUE) absolute.diff <- abs(vec - median.vec) MAD <- median(absolute.diff, na.rm = TRUE) mod.z <- (0.6745 * (vec - median.vec)) / MAD return(mod.z) } # 保留示例数据 data <- data.frame(id = seq(1, 100, by = 1), sex = sample(c('M', 'F'), 100, replace = TRUE), age_class = sample(c('A', 'S'), 100, replace = TRUE), # A = adult, S = subadult weight = sample(80:600, 100, replace = TRUE), offspring = sample(c('Y','N'), 100, replace = TRUE), albumin = rnorm(100, 5, 2), cortisol = rnorm(100, 30, 12), calcium = rnorm(100, 0.3, 0.005), globulin = rnorm(100, 1.9, 0.3), insulin = rnorm(100, 3, 0.13)) # 分组计算并生成新列 data <- data %>% group_by(age_class, sex) %>% mutate( # 处理albumin到insulin的所有目标列 across(albumin:insulin, ~mod.zscore(cur_data(), cur_column()), # 设置新列命名规则:原列名 + _zscore .names = "{.col}_zscore") ) %>% # 取消分组,避免后续操作受分组状态影响 ungroup()
代码细节解释
- 分组逻辑:
group_by(age_class, sex)将数据按年龄组和性别拆分为独立子集,确保后续计算基于分组内的数据 - 批量列处理:
across(albumin:insulin, ...)指定要处理的列范围,无需逐个列名手动输入 - 函数适配:
cur_data()获取当前分组的数据集,cur_column()获取当前处理的列名,完美匹配自定义函数的参数要求 - 自动命名:
.names = "{.col}_zscore"将原列名替换到占位符位置,自动生成如albumin_zscore的规范新列名 - 取消分组:
ungroup()恢复数据的非分组状态,避免后续操作出现分组相关的意外问题
旧版dplyr兼容方案(<1.0.0)
如果你的dplyr版本较低无法使用across(),可以用mutate_at()替代:
data <- data %>% group_by(age_class, sex) %>% mutate_at(vars(albumin:insulin), ~mod.zscore(cur_data(), cur_column())) %>% rename_at(vars(albumin:insulin), ~paste0(., "_zscore")) %>% ungroup()
内容的提问来源于stack exchange,提问作者burphound
相关产品推荐
相关产品推荐

