合并具有重叠值的行
Merging rows with overlapping values
我有一个 data.table
,其中每一行都有一个 UniqueID,一个年龄列,以及 2 个带有年龄置信区间的列。我想要做的是合并具有重叠 CI 的行,因此返回的 CI 是重叠的 min/max。
ID Age AgeMax AgeMin
1: 2 48073 49213 46933
2: 3 49002 49638 48366
3: 7 44297 44706 43888
此示例的返回结果将是:
ID Age AgeMax AgeMin
2 48409 49638 46933
7 44297 44706 43888
因为 ID 2 和 3 在 AgeMax 和 AgeMin 中有重叠的值。 ID == 2 的 AgeMax 小于 ID == 3 的 AgeMax,但大于 ID == 3 的 AgeMin,因此它们重叠。 ID 7 不与其他行重叠,因此按原样返回。
实际上我不介意返回的 ID
和 Age
是什么,只要它来自重叠的 ID
之一
我的尝试如下,但我没有得到正确的结果
library(data.table)
# sequence of years
step <- 10
window <- 30
startYear <- -60000+(0.5*window)
endYear <- 0-(0.5*window)
yrSeq <- abs(seq(startYear, endYear, step))
# Example DT
DT <- structure(list(ID = c(2L, 3L, 7L), Age = c(48073L, 49002L, 44297L
), AgeMax = c(49213L, 49638L, 44706L), AgeMin = c(46933L, 48366L,
43888L)), row.names = c(NA, -3L), class = c("data.table", "data.frame"
))
# split into a list to expand the CI's
s <- split(DT, DT$ID)
# Expand the CI's, to the nearest year in the seq
# merge back into a DT
d_seq <- rbindlist(lapply(s, function(x) {
data.table(ID = x$ID, Yr = yrSeq[between(yrSeq, x$AgeMin, x$AgeMax)])}))
# remove duplicated years and return min and max years for each ID
d_seq <- d_seq[!duplicated(d_seq$Yr),]
d_seq <- d_seq[, .(AgeMin = min(Yr), AgeMax = max(Yr)), by = ID]
# merge with the original DT and select columns
DT <- merge(DT, d_seq, by = "ID")
DT <- DT[, c(1,2,5,6)]
不幸的是,这不起作用,因为即使 ID == 3 与 ID == 2 重叠(如上所示),也会返回 ID == 3,现在 ID == 2 的 AgeMin 和 AgeMax 不涵盖该 ID 的年龄!
ID Age AgeMin AgeMax
1: 2 48073 46935 49205
2: 3 49002 49215 49635
3: 7 44297 43895 44705
我敢肯定我想多了,肯定有一种简单的方法可以满足我的需要 returns,不幸的是我还没有找到任何解决方案。
我已经尝试修改示例 and 。
这里有一个额外的例子data.table
来测试。
testDT <- structure(list(ID = c(54L, 57L, 58L, 60L, 61L, 62L, 64L, 180L
), Age = c(14219L, 13989L, 13883L, 13482L, 13403L, 13383L, 13340L,
13994L), AgeMax = c(14343L, 14087L, 13972L, 13540L, 13465L, 13442L,
13407L, 14083L), AgeMin = c(14095L, 13891L, 13794L, 13424L, 13341L,
13324L, 13273L, 13905L)), row.names = c(NA, -8L), class = c("data.table",
"data.frame"))
我认为您需要 data.table
和 igraph
的组合,因为重叠可能会无限链接,即使最后一个 ID 可能与链中的第一个 ID 没有重叠。
这里有一个选项:
#find overlapping intervals using data.table::foverlaps
setkey(setDT(testDT), AgeMin, AgeMax)
d <- unique(foverlaps(testDT, testDT)[, .(x=pmin(ID, i.ID), y=pmax(ID, i.ID))])
#find clusters of IDs with overlapping intervals
library(igraph)
g <- graph_from_data_frame(d, directed=FALSE)
m <- setDT(stack(clusters(g)$membership))[, ind := as.integer(as.character(ind))]
#lookup grouping using update join
testDT[m, on=.(ID=ind), cls := values]
#output
testDT[order(ID), .(ID=ID[1L], Age=Age[1L], AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
cls]
输出:
cls ID Age AgeMin AgeMax
1: 3 54 14219 14095 14343
2: 2 57 13989 13794 14087
3: 1 60 13482 13273 13540
编辑:
Frank Zhang 的方法让我想起了 中的 David Aurenburg 方法。因此是这样的:
setDT(testDT)[order(AgeMin, AgeMax), g :=
cumsum(c(0L, (shift(AgeMin, -1L) > cummax(AgeMax))[-.N]))
]
testDT[order(ID), .(ID=ID[1L], Age=Age[1L],
AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
g]
这应该会更快。
这是一个data.table
解决方案
library(data.table)
setDT(testDT)
testDT[order(AgeMin)
][, .(AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
by=.(group=cumsum(c(1, tail(AgeMin, -1) > head(AgeMax, -1))))]
#> group AgeMin AgeMax
#> 1: 1 13273 13540
#> 2: 2 13794 14087
#> 3: 3 14095 14343
此解决方案的关键是获取重叠周期的 group
。
假设我们有两个范围 p1
和 p2
。它们的开始和结束命名为 start1
,end1
,start2
,end2
.
只有两种情况p1
和p2
没有重叠。
start1
> end2
或者
end1
< start2
由于我们已经Agemin
升序排列,所以只需要考虑条件1即可。
然后我们可以使用 cumsum
来获取组标识符。
此解决方案非常易读且灵活,因此请根据您的需要进行调整。
示例数据
testDT <- structure(list(ID = c(54L, 57L, 58L, 60L, 61L, 62L, 64L, 180L
), Age = c(14219L, 13989L, 13883L, 13482L, 13403L, 13383L, 13340L,
13994L), AgeMax = c(14343L, 14087L, 13972L, 13540L, 13465L, 13442L,
13407L, 14083L), AgeMin = c(14095L, 13891L, 13794L, 13424L, 13341L,
13324L, 13273L, 13905L)), row.names = c(NA, -8L), class = c("data.table",
"data.frame"))
代码
library( data.table )
library( intervals )
#set testDT as data.table
setDT(testDT)
#assuming you want to merge all overlapping intervals to one long interval...
# create a table with joined intervals
# since inertvals need the min-col before max, we switch cols 3 and 4
DT.int <- as.data.table(
intervals::interval_union(
intervals::Intervals( as.matrix( testDT[, 4:3] ) ) ,
check_valid = TRUE ) )
#set colnames
setnames( DT.int, names(DT.int), c("AgeMin", "AgeMax" ) )
#add interval id's
DT.int[, interval_id := .I ][]
# AgeMin AgeMax interval_id
# 1: 13273 13540 1
# 2: 13794 14087 2
# 3: 14095 14343 3
#now you can join the intervals back to DT, whatever/however you like
testDT[ DT.int,
`:=`( AgeMin.interval = i.AgeMin, AgeMax.interval = i.AgeMax,
interval.id = i.interval_id ),
on = .( AgeMin <= AgeMax, AgeMax >= AgeMin ) ]
输出
testDT
# ID Age AgeMax AgeMin AgeMin.interval AgeMax.interval interval.id
# 1: 54 14219 14343 14095 14095 14343 3
# 2: 57 13989 14087 13891 13794 14087 2
# 3: 58 13883 13972 13794 13794 14087 2
# 4: 60 13482 13540 13424 13273 13540 1
# 5: 61 13403 13465 13341 13273 13540 1
# 6: 62 13383 13442 13324 13273 13540 1
# 7: 64 13340 13407 13273 13273 13540 1
# 8: 180 13994 14083 13905 13794 14087 2
现在 process/summarise 更进一步,例如:获取 interval.id
...
的非重复行
testDT[ !duplicated( interval.id ), .(ID, Age, AgeMax, AgeMin) ]
# ID Age AgeMax AgeMin
# 1: 54 14219 14343 14095
# 2: 57 13989 14087 13891
# 3: 60 13482 13540 13424
我有一个 data.table
,其中每一行都有一个 UniqueID,一个年龄列,以及 2 个带有年龄置信区间的列。我想要做的是合并具有重叠 CI 的行,因此返回的 CI 是重叠的 min/max。
ID Age AgeMax AgeMin
1: 2 48073 49213 46933
2: 3 49002 49638 48366
3: 7 44297 44706 43888
此示例的返回结果将是:
ID Age AgeMax AgeMin
2 48409 49638 46933
7 44297 44706 43888
因为 ID 2 和 3 在 AgeMax 和 AgeMin 中有重叠的值。 ID == 2 的 AgeMax 小于 ID == 3 的 AgeMax,但大于 ID == 3 的 AgeMin,因此它们重叠。 ID 7 不与其他行重叠,因此按原样返回。
实际上我不介意返回的 ID
和 Age
是什么,只要它来自重叠的 ID
之一
我的尝试如下,但我没有得到正确的结果
library(data.table)
# sequence of years
step <- 10
window <- 30
startYear <- -60000+(0.5*window)
endYear <- 0-(0.5*window)
yrSeq <- abs(seq(startYear, endYear, step))
# Example DT
DT <- structure(list(ID = c(2L, 3L, 7L), Age = c(48073L, 49002L, 44297L
), AgeMax = c(49213L, 49638L, 44706L), AgeMin = c(46933L, 48366L,
43888L)), row.names = c(NA, -3L), class = c("data.table", "data.frame"
))
# split into a list to expand the CI's
s <- split(DT, DT$ID)
# Expand the CI's, to the nearest year in the seq
# merge back into a DT
d_seq <- rbindlist(lapply(s, function(x) {
data.table(ID = x$ID, Yr = yrSeq[between(yrSeq, x$AgeMin, x$AgeMax)])}))
# remove duplicated years and return min and max years for each ID
d_seq <- d_seq[!duplicated(d_seq$Yr),]
d_seq <- d_seq[, .(AgeMin = min(Yr), AgeMax = max(Yr)), by = ID]
# merge with the original DT and select columns
DT <- merge(DT, d_seq, by = "ID")
DT <- DT[, c(1,2,5,6)]
不幸的是,这不起作用,因为即使 ID == 3 与 ID == 2 重叠(如上所示),也会返回 ID == 3,现在 ID == 2 的 AgeMin 和 AgeMax 不涵盖该 ID 的年龄!
ID Age AgeMin AgeMax
1: 2 48073 46935 49205
2: 3 49002 49215 49635
3: 7 44297 43895 44705
我敢肯定我想多了,肯定有一种简单的方法可以满足我的需要 returns,不幸的是我还没有找到任何解决方案。
我已经尝试修改示例
这里有一个额外的例子data.table
来测试。
testDT <- structure(list(ID = c(54L, 57L, 58L, 60L, 61L, 62L, 64L, 180L
), Age = c(14219L, 13989L, 13883L, 13482L, 13403L, 13383L, 13340L,
13994L), AgeMax = c(14343L, 14087L, 13972L, 13540L, 13465L, 13442L,
13407L, 14083L), AgeMin = c(14095L, 13891L, 13794L, 13424L, 13341L,
13324L, 13273L, 13905L)), row.names = c(NA, -8L), class = c("data.table",
"data.frame"))
我认为您需要 data.table
和 igraph
的组合,因为重叠可能会无限链接,即使最后一个 ID 可能与链中的第一个 ID 没有重叠。
这里有一个选项:
#find overlapping intervals using data.table::foverlaps
setkey(setDT(testDT), AgeMin, AgeMax)
d <- unique(foverlaps(testDT, testDT)[, .(x=pmin(ID, i.ID), y=pmax(ID, i.ID))])
#find clusters of IDs with overlapping intervals
library(igraph)
g <- graph_from_data_frame(d, directed=FALSE)
m <- setDT(stack(clusters(g)$membership))[, ind := as.integer(as.character(ind))]
#lookup grouping using update join
testDT[m, on=.(ID=ind), cls := values]
#output
testDT[order(ID), .(ID=ID[1L], Age=Age[1L], AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
cls]
输出:
cls ID Age AgeMin AgeMax
1: 3 54 14219 14095 14343
2: 2 57 13989 13794 14087
3: 1 60 13482 13273 13540
编辑:
Frank Zhang 的方法让我想起了
setDT(testDT)[order(AgeMin, AgeMax), g :=
cumsum(c(0L, (shift(AgeMin, -1L) > cummax(AgeMax))[-.N]))
]
testDT[order(ID), .(ID=ID[1L], Age=Age[1L],
AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
g]
这应该会更快。
这是一个data.table
解决方案
library(data.table)
setDT(testDT)
testDT[order(AgeMin)
][, .(AgeMin=min(AgeMin), AgeMax=max(AgeMax)),
by=.(group=cumsum(c(1, tail(AgeMin, -1) > head(AgeMax, -1))))]
#> group AgeMin AgeMax
#> 1: 1 13273 13540
#> 2: 2 13794 14087
#> 3: 3 14095 14343
此解决方案的关键是获取重叠周期的 group
。
假设我们有两个范围 p1
和 p2
。它们的开始和结束命名为 start1
,end1
,start2
,end2
.
只有两种情况p1
和p2
没有重叠。
start1
>end2
或者end1
<start2
由于我们已经Agemin
升序排列,所以只需要考虑条件1即可。
然后我们可以使用 cumsum
来获取组标识符。
此解决方案非常易读且灵活,因此请根据您的需要进行调整。
示例数据
testDT <- structure(list(ID = c(54L, 57L, 58L, 60L, 61L, 62L, 64L, 180L
), Age = c(14219L, 13989L, 13883L, 13482L, 13403L, 13383L, 13340L,
13994L), AgeMax = c(14343L, 14087L, 13972L, 13540L, 13465L, 13442L,
13407L, 14083L), AgeMin = c(14095L, 13891L, 13794L, 13424L, 13341L,
13324L, 13273L, 13905L)), row.names = c(NA, -8L), class = c("data.table",
"data.frame"))
代码
library( data.table )
library( intervals )
#set testDT as data.table
setDT(testDT)
#assuming you want to merge all overlapping intervals to one long interval...
# create a table with joined intervals
# since inertvals need the min-col before max, we switch cols 3 and 4
DT.int <- as.data.table(
intervals::interval_union(
intervals::Intervals( as.matrix( testDT[, 4:3] ) ) ,
check_valid = TRUE ) )
#set colnames
setnames( DT.int, names(DT.int), c("AgeMin", "AgeMax" ) )
#add interval id's
DT.int[, interval_id := .I ][]
# AgeMin AgeMax interval_id
# 1: 13273 13540 1
# 2: 13794 14087 2
# 3: 14095 14343 3
#now you can join the intervals back to DT, whatever/however you like
testDT[ DT.int,
`:=`( AgeMin.interval = i.AgeMin, AgeMax.interval = i.AgeMax,
interval.id = i.interval_id ),
on = .( AgeMin <= AgeMax, AgeMax >= AgeMin ) ]
输出
testDT
# ID Age AgeMax AgeMin AgeMin.interval AgeMax.interval interval.id
# 1: 54 14219 14343 14095 14095 14343 3
# 2: 57 13989 14087 13891 13794 14087 2
# 3: 58 13883 13972 13794 13794 14087 2
# 4: 60 13482 13540 13424 13273 13540 1
# 5: 61 13403 13465 13341 13273 13540 1
# 6: 62 13383 13442 13324 13273 13540 1
# 7: 64 13340 13407 13273 13273 13540 1
# 8: 180 13994 14083 13905 13794 14087 2
现在 process/summarise 更进一步,例如:获取 interval.id
...
testDT[ !duplicated( interval.id ), .(ID, Age, AgeMax, AgeMin) ]
# ID Age AgeMax AgeMin
# 1: 54 14219 14343 14095
# 2: 57 13989 14087 13891
# 3: 60 13482 13540 13424