仅已知母亲ID时在面板数据中生成子ID的dplyr实现方案
示例数据
首先是生成示例数据集的R代码:
id <- c(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5) t <- c(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3) md <- c(NA,NA,NA,1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4) dat <- data.frame(id, t, md)
运行后得到的原始数据集:
dat
id t md
1 1 1 NA
2 1 2 NA
3 1 3 NA
4 2 1 1
5 2 2 1
6 2 3 1
7 3 1 1
8 3 2 1
9 3 3 1
10 4 1 1
11 4 2 1
12 4 3 1
13 5 1 4
14 5 2 4
15 5 3 4
数据集说明
该数据集包含:
- 个人ID(
id):唯一标识每个个体 - 时间点(
t):记录个体在3个不同时间点的观测 - 母亲ID(
md):对应个体的母亲ID,其中:- ID为
1的个体无对应母亲,且是ID为2-4的个体的母亲 - ID为
4的个体是ID为5的个体的母亲
- ID为
需求目标
需要基于md字段生成子ID相关的新变量:
- 根据每个母亲的子个体数量,动态创建对应数量的新变量(如
kd1、kd2...) - 每个子个体的ID单独记录在一个变量中,无子个体的位置填充
NA
预期输出结果:
dat
id t md kd1 kd2 kd3 kd4
1 1 1 NA 2 3 4 NA
2 1 2 NA 2 3 4 NA
3 1 3 NA 2 3 4 NA
4 2 1 1 NA NA NA NA
5 2 2 1 NA NA NA NA
6 2 3 1 NA NA NA NA
7 3 1 1 NA NA NA NA
8 3 2 1 NA NA NA NA
9 3 3 1 NA NA NA NA
10 4 1 1 5 NA NA NA
11 4 2 1 5 NA NA NA
12 4 3 1 5 NA NA NA
13 5 1 4 NA NA NA NA
14 5 2 4 NA NA NA NA
15 5 3 4 NA NA NA NA
灵活解决方案(基于dplyr)
这个方案完全适配复杂数据集,不用提前知道最大子个体数量,核心思路是先构建母子映射关系,再动态生成子ID列:
library(dplyr) library(tidyr) # 第一步:构建母子映射表,自动给每个母亲的子ID编号 child_mapping <- dat %>% distinct(id, md) %>% # 去重,避免同一个个体的母子关系重复计算 filter(!is.na(md)) %>% # 只保留有明确母亲的个体 group_by(md) %>% mutate(child_num = row_number()) %>% # 给每个母亲的子个体按顺序编号 ungroup() %>% pivot_wider( names_from = child_num, values_from = id, names_prefix = "kd" # 子ID列名统一用kd前缀 ) # 第二步:将子ID信息合并回原始数据集 dat_final <- dat %>% left_join(child_mapping, by = c("id" = "md")) %>% # 用原始数据的id匹配映射表的母亲id(md) select(id, t, md, starts_with("kd")) # 调整列顺序,把子ID列放在md之后
方案细节解释
- 去重处理:
distinct(id, md)确保每个个体的母子关系只被处理一次,避免重复统计子个体数量 - 动态编号:
row_number()自动适配每个母亲的子个体数量,不管是1个还是N个都能正确编号 - 宽表转换:
pivot_wider会根据子个体的最大数量自动生成对应列,无子个体的位置自动填充NA - 数据合并:
left_join保证原始数据的每一行(包括每个时间点的观测)都能匹配到对应的子ID信息,不会丢失任何原始数据
运行上述代码后就能得到和预期一致的结果,而且不管实际数据里有多少母亲、每个母亲有多少子个体,这个方案都能自动适配,完全不需要手动调整参数。
内容的提问来源于stack exchange,提问作者C.F.

