在R语言长格式数据集中创建随时间动态变化的哑变量
实现长格式数据集中的"ever urban"哑变量
核心需求
按个体分组,当个体首次出现urban=1后,后续所有记录的ever_urban均为1(即使后续urban变回0或NA);首次出现urban=1前的记录,若原urban为0则ever_urban=0,若原urban为NA则保留NA。
示例数据集
先构造符合场景的测试数据:
library(dplyr) df <- tibble( id = rep(1:2, each = 5), time = 1:5, urban = c(0, 1, 0, NA, 0, 0, 0, NA, 1, 0) )
解决方案1:使用dplyr(推荐,逻辑清晰)
利用cumany()判断个体是否曾出现过urban=1,结合case_when()处理NA和0的情况:
df_processed <- df %>% group_by(id) %>% mutate( ever_urban = case_when( # 只要曾出现过urban=1(忽略NA),后续所有行设为1 cumany(urban == 1, na.rm = TRUE) ~ 1L, # 未出现过urban=1时,原urban为NA则保留NA is.na(urban) ~ NA_integer_, # 其余情况设为0 TRUE ~ 0L ) ) %>% ungroup()
处理后结果:
| id | time | urban | ever_urban |
|---|---|---|---|
| 1 | 1 | 0 | 0 |
| 1 | 2 | 1 | 1 |
| 1 | 3 | 0 | 1 |
| 1 | 4 | NA | 1 |
| 1 | 5 | 0 | 1 |
| 2 | 1 | 0 | 0 |
| 2 | 2 | 0 | 0 |
| 2 | 3 | NA | NA |
| 2 | 4 | 1 | 1 |
| 2 | 5 | 0 | 1 |
解决方案2:使用Base R(无需加载包)
用ave()按个体分组,结合cummax()和ifelse()处理:
df$ever_urban <- with(df, ave(urban, id, FUN = function(x) { # 先将NA替换为0,计算累积最大值(判断是否曾出现过1) cum_has_urban <- cummax(replace(x, is.na(x), 0)) # 未出现过1时,原NA位置恢复为NA;其余情况用累积最大值 ifelse(is.na(x) & cum_has_urban == 0, NA_integer_, cum_has_urban) }))
常见问题排查
之前用cummax/cumsum得到1和NA的原因:
- 未按个体分组,导致所有个体的
urban值被混在一起计算; - 未处理NA,
cummax()遇到NA会直接传递,覆盖前面的0值。
- 未按个体分组,导致所有个体的
NA保留逻辑:
- 首次出现
urban=1前的NA:保留NA(因为无法确定该记录是否为城市状态); - 首次出现
urban=1后的NA:设为1(因为个体已经有过城市记录)。
- 首次出现
内容的提问来源于stack exchange,提问作者T L
相关产品推荐
相关产品推荐

