如何在R/dplyr中按日期序列间隔拆分分组(Gaps and Islands问题)
R语言处理季度连续的Gaps and Islands问题
问题场景
我要解决SQL中常提及的「缺口与岛屿(Gaps and Islands)」问题,不过是用R语言实现核心逻辑:按id分组,为季度连续的日期数据块添加专属分组标签(另有一个场景是年度连续的处理)。
模拟测试数据
使用tidyverse和zoo包构造的测试数据集:
library(tidyverse) library(zoo) df <- tibble('id' = c('A','A','A', 'B', 'B'), 'year' = as.yearqtr(c("01/01/2001","01/04/2001","01/10/2002", "01/09/2001","01/12/2001"),"%d/%m/%Y"), 'var1' = c(1,3,2,5,3), 'var2' = c(10,20,5,8,9))
对应的数据表展示:
id year var1 var2 <chr> <yearqtr> <dbl> <dbl> 1 A 2001 Q1 1 10 2 A 2001 Q2 3 20 3 A 2002 Q4 2 5 4 B 2001 Q3 5 8 5 B 2001 Q4 3 9
需求说明
按id分组后,为连续的季度数据块分配唯一分组标签:
- ID为A的前两行是连续季度(2001 Q1→2001 Q2),归为分组1;第三行2002 Q4和前一行间隔多个季度,归为分组2。
- ID为B的两行是连续季度(2001 Q3→2001 Q4),归为分组1。
期望输出结果:
id year var1 var2 group <chr> <yearqtr> <dbl> <dbl> <dbl> 1 A 2001 Q1 1 10 1 2 A 2001 Q2 3 20 1 3 A 2002 Q4 2 5 2 4 B 2001 Q3 5 8 1 5 B 2001 Q4 3 9 1
最终解决方案
通过dplyr分组后,利用diff()计算季度间隔,结合cumsum()生成分组标签:
df %>% group_by(id) %>% mutate(group = cumsum(c(TRUE, 4 * diff(year) > 1)))
代码逻辑解释
group_by(id):按ID分组处理每个个体的日期序列diff(year):计算相邻行yearqtr类型的日期差,yearqtr的数值格式为「年份+季度/4」(比如2001 Q1是2001.0,2001 Q2是2001.25)4 * diff(year):将差值转换为季度数,连续季度的差值为1,若间隔超过1个季度则结果大于1c(TRUE, ...):为第一行添加初始值,保证分组从1开始计数cumsum():累计求和,每次遇到间隔超过1的情况就生成新的分组编号
内容的提问来源于stack exchange,提问作者DataMonkey
相关产品推荐
相关产品推荐

