使用data.table查找重叠区间组:如何计算Wanted.column?
用data.table给重叠/连续区间分组
嘿,这个需求我太熟悉了!你想把那些重叠或者能连成连续块的区间归为同一组,而且想用data.table的foverlaps来实现对吧?咱们分两种方法来聊,一种是高效的累积计算法,另一种是用foverlaps的直观匹配法,都能得到你要的Wanted.column。
先看你的原始数据:
library(data.table) DT <- data.table(Interval.id=c(1L, 2L, 3L, 4L, 5L, 6L), Interval.start=c(2.0, 3.0, 4.0, 4.6, 4.7, 5.5), Interval.end=c(4.5, 3.5, 4.8, 5.0, 4.9, 8.0), Wanted.column=c(1L, 1L, 1L, 1L, 1L, 2L))
方法一:高效的累积分组法(推荐大数据场景)
这个方法不用foverlaps,但用data.table的向量操作就能快速搞定,核心思路是:只要当前区间的起始点不超过前面所有区间的最大结束值,就说明它和前面的某个区间是重叠/连续的,归为同一组;否则新建组。
步骤如下:
- 先按区间起始点排序(这是所有区间操作的基础)
- 计算到每个位置为止的最大区间结束值
- 用累积求和标记分组分界点
代码实现:
# 按起始点排序 setorder(DT, Interval.start) # 计算到当前行的最大结束值 DT[, current_max_end := cummax(Interval.end)] # 标记分组:当前区间起始 > 前一个最大结束值时,新建组 DT[, group := cumsum(shift(current_max_end, fill = -Inf) < Interval.start)]
运行后看结果,group列完全和你的Wanted.column一致:
DT[, .(Interval.id, Wanted.column, group)] # Interval.id Wanted.column group # 1: 1 1 1 # 2: 2 1 1 # 3: 3 1 1 # 4: 4 1 1 # 5: 5 1 1 # 6: 6 2 2
方法二:用foverlaps实现区间匹配(适合理解逻辑)
如果你一定要用foverlaps,可以先把重叠的区间合并成“超级区间”,再把原始区间匹配到对应的超级区间,从而得到组ID。
步骤如下:
- 排序原始数据
- 初始化合并区间表,把第一个区间作为第一个组
- 遍历后续每个区间,用
foverlaps检查是否和已有的合并区间重叠:- 如果重叠,就更新合并区间的结束值为两者的最大值
- 如果不重叠,就新增一个合并区间(新组)
- 最后用
foverlaps把原始区间和合并区间匹配,得到每个区间的组ID
代码实现:
setorder(DT, Interval.start) # 初始化合并区间表 merged <- DT[1, .(start = Interval.start, end = Interval.end, group = 1L)] # 遍历剩下的区间,合并重叠部分 for (i in 2:nrow(DT)) { current <- DT[i] # 用foverlaps找当前区间和已合并区间的重叠情况 overlap <- foverlaps(current, merged, by.x = c("Interval.start", "Interval.end"), by.y = c("start", "end"), type = "any", nomatch = 0) if (nrow(overlap) > 0) { # 有重叠,更新合并区间的结束值 merged[overlap$group, end := max(end, current$Interval.end)] } else { # 无重叠,新增组 new_group <- max(merged$group) + 1L merged <- rbind(merged, .(start = current$Interval.start, end = current$Interval.end, group = new_group)) } } # 把原始区间和合并区间匹配,得到组ID result <- foverlaps(DT, merged, by.x = c("Interval.start", "Interval.end"), by.y = c("start", "end"), type = "within", nomatch = 0)[, .(Interval.id, Interval.start, Interval.end, group)] print(result)
这个方法的输出结果也和你的预期完全一致,而且能清晰看到区间合并的过程,适合理解foverlaps的用法。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

