如何在pivot_wider时直接计算多列的时间点差值?
问题描述
我的数据集中每个个体对应1个或2个时间点(M0、M3),需要为多个生物指标列计算两个时间点的差值。目前已通过以下代码先使用pivot_wider转宽表,再手动计算差值:
library(tidyr) data = data.frame(id = c(1,1,2,2,3,4,5,5,6), time = c("M0","M3","M0","M3","M0","M0","M0","M3","M0"), bio1 = c(4.2, 4.8, 4, NA, 3.8, 4.4, 5, 6, 6.1), bio2 = c(12, 14, 10, 11, NA, 18, 19, 12, 15)) data data_wide <- data %>% pivot_wider(names_from="time", values_from=c("bio1","bio2")) %>% mutate(diff_bio1 = bio1_M3 - bio1_M0, diff_bio2 = bio2_M3 - bio2_M0) %>% select(id, diff_bio1, diff_bio2) data_wide
但我希望能在转宽的同时直接应用差值计算,而非后续手动处理多列,请问是否有可行方法?
解决方案
方法1:转宽后用across批量生成差值列
无需手动逐个定义差值列,用across匹配所有M0时间点的指标列,自动计算对应M3列的差值:
library(dplyr) library(tidyr) data_wide <- data %>% pivot_wider(names_from = time, values_from = starts_with("bio")) %>% mutate( # 对所有_M0结尾的列计算差值 across(ends_with("_M0"), ~ get(gsub("_M0", "_M3", cur_column())) - ., .names = "diff_{.col}" ), # 仅保留id和新生成的差值列 .keep = "unused" ) %>% # 清理差值列名,去掉多余的_M0后缀 rename_with(~ gsub("_M0", "", .), starts_with("diff_")) data_wide
代码说明:
across(ends_with("_M0"))选中所有以_M0结尾的指标列gsub("_M0", "_M3", cur_column())将当前列名的_M0替换为_M3,匹配对应的M3时间点列.names = "diff_{.col}"自动生成以diff_开头的差值列名
方法2:先分组计算差值再转宽(更高效)
跳过先转宽的步骤,直接按个体和指标分组计算差值,再转宽得到结果,适配任意数量的生物指标列:
library(dplyr) library(tidyr) data_diff <- data %>% # 将所有生物指标列转成长格式 pivot_longer(cols = starts_with("bio"), names_to = "indicator", values_to = "value") %>% # 按个体id和指标分组 group_by(id, indicator) %>% # 计算M3与M0的差值 summarise(diff = value[time == "M3"] - value[time == "M0"], .groups = "drop") %>% # 将差值转回宽格式,生成diff_bio1、diff_bio2列 pivot_wider(names_from = indicator, values_from = diff, names_prefix = "diff_") data_diff
这个方法无需关注具体有多少个生物指标列,新增指标列后代码无需修改即可自动处理。
内容的提问来源于stack exchange,提问作者BPeif
相关产品推荐
相关产品推荐

