使用dplyr按标识符将纵向数据折叠为时间周期的技术问询
按ID分组周期汇总数据解决方案
问题背景
需要将包含ID、day、X、Y、Z、C列的每日观测数据,按ID折叠为以下格式:
- 第一天为基线期
- 之后每3天为一个周期(最后一个周期可不足3天)
- 每个周期需计算:
- Xmax/Ymax:周期内对应列的最大值
- Zany:周期内是否存在非NA的"1"(用0/1表示)
- Cmean:周期内C列的均值
已尝试使用dplyr的summarise和across函数,但遇到两个问题:
- 如何将每个ID的第一行单独作为基线期,后续行按每3天分组
- 如何用统一语法处理多列不同的汇总逻辑,无需拆分数据框单独处理
示例输入数据
df <- structure(list(ID = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), day = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), X = c(1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), Y = c(0, 0, 1, 2, 1, 1, 0, 1, 3, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3), Z = c("NA", "NA", "NA", "NA", "1", "NA", "NA", "NA", "NA", "NA", "1", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "1", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA"), C = c(22, 22, 23, 22, 24, 23, 22, 25, 25, 24, 25, 25, 24, 26, 18, 17, 17, 18, 18, 19, 19, 19, 20, 21, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35)), class = c("tbl_df", "tbl", "data.frame" ), row.names = c(NA, -36L))
目标输出格式
target <- structure(list(ID = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3), day = c("1", "2 to 4", "5 to 7", "8 to 10", "11 to 13", "14", "1", "2 to 4", "5 to 7", "8 to 10", "1", "2 to 4", "5 to 7", "8 to 10", "11 to 12"), Xmax = c(1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1), Ymax = c(0, 2, 1, 3, 0, 1, 0, 1, 3, 0, 0, 0, 0, 1, 3), Zany = c(0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0), Cmean = c(22, 22.3333333333333, 23, 24.6666666666667, 24.6666666666667, 26, 18, 17.3333333333333, 18.6666666666667, 20, 34, 34, 34.6666666666667, 35, 35)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -15L))
解决方案代码
library(dplyr) result <- df %>% group_by(ID) %>% # 生成周期分组标记:第一天为第1周期,后续每3天为一个周期 mutate(period = case_when( day == 1 ~ 1, TRUE ~ 1 + ceiling((day - 1)/3) )) %>% group_by(ID, period) %>% summarise( # 生成周期的day标签 day = case_when( n() == 1 ~ as.character(min(day)), TRUE ~ paste(min(day), "to", max(day)) ), # 按要求汇总各列 Xmax = max(X), Ymax = max(Y), Zany = as.integer(any(Z == "1")), Cmean = mean(C), .groups = "drop" ) # 验证结果是否匹配目标 all.equal(result, target)
代码说明
周期分组逻辑:
- 先按
ID分组,为每个ID的天数生成period标记:第一天单独标记为1,后续天数通过ceiling((day-1)/3)计算分组序号,再加上1得到周期编号,确保每3天为一个周期。 - 此方式无需单独跳过第一行,直接将基线期和后续周期统一纳入分组体系。
- 先按
多列汇总处理:
- 在同一个
summarise步骤中完成所有列的计算:Xmax/Ymax直接调用max()函数Zany通过any(Z == "1")判断是否存在目标值,再转成整数型0/1Cmean调用mean()计算均值
- 全程无需拆分数据框,一次完成所有汇总操作。
- 在同一个
内容的提问来源于stack exchange,提问作者Statistix
相关产品推荐
相关产品推荐

