如何用R(dplyr)按校ID分组,为连续年级块生成递增序号
用dplyr生成连续相同grade区块的分组编号new_id
需求:基于包含schoolid(校ID)和grade(年级)列的数据框,生成新列new_id。规则为:在每个schoolid分组内,对连续出现的相同grade的区块从1开始递增编号,同一区块内所有行的new_id值相同。
目标数据示例
structure(list(schoolid = c(201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 201L, 202L, 202L, 202L, 202L, 202L, 202L, 202L, 202L, 202L), grade = c(3L, 3L, 4L, 4L, 5L, 5L, 3L, 3L, 5L, 5L, 5L, 3L, 3L, 3L, 3L, 4L, 4L, 5L, 5L, 3L, 4L, 4L), new_id = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L)), row.names = c(NA, 22L), class = "data.frame")
错误尝试代码
data %>% group_by(schoolid, grade) %>% mutate(new_id = 1:n())
该代码的问题在于:同时按schoolid和grade分组会将所有同校同年级的行归为一组,忽略了「连续出现」的要求,生成的是组内行号而非连续区块的编号。
正确实现方法
核心思路是在每个schoolid分组内,先识别连续grade区块的边界,再通过累加边界标记生成连续编号:
library(dplyr) data %>% group_by(schoolid) %>% mutate( # 标记当前行与前一行grade是否不同,首行默认与自身相同(标记为FALSE) block_change = grade != lag(grade, default = first(grade)), # 累加边界标记得到区块编号,+1让编号从1开始 new_id = cumsum(block_change) + 1 ) %>% ungroup()
代码解释
group_by(schoolid):仅按校ID分组,确保每个学校内独立处理连续区块block_change:通过lag()获取前一行的grade,与当前行比较,当grade变化时标记为TRUE(即区块边界)cumsum(block_change):累加边界标记,每次遇到TRUE(边界)时数值+1,这样同一连续区块内的数值相同+1:将初始的0编号转为从1开始
内容的提问来源于stack exchange,提问作者katie burford
相关产品推荐
相关产品推荐

