考虑下面的data.table
s。第一个定义了一组区域,每个组"x"都有起始位置和结束位置:
library(data.table)
d1 <- data.table(x = letters[1:5], start = c(1,5,19,30, 7), end = c(3,11,22,39,25))
setkey(d1, x, start)
# x start end
# 1: a 1 3
# 2: b 5 11
# 3: c 19 22
# 4: d 30 39
# 5: e 7 25
第二个数据集具有相同的分组变量"x",以及每个组内的位置"pos":
d2 <- data.table(x = letters[c(1,1,2,2,3:5)], pos = c(2,3,3,12,20,52,10))
setkey(d2, x, pos)
# x pos
# 1: a 2
# 2: a 3
# 3: b 3
# 4: b 12
# 5: c 20
# 6: d 52
# 7: e 10
最后,我想提取"d2"中的行,其中"pos"在每个组x
中的"start"和"end"定义的范围内。
# x pos start end
# 1: a 2 1 3
# 2: a 3 1 3
# 3: c 20 19 22
# 4: e 10 7 25
任何组x
的开始/结束位置将永远不会重叠,但是可能存在不在任何区域中的值的间隙。
现在,我相信我应该使用滚动连接。据我所知,我不能在连接中使用"结束"列。
我试过了
d1[d2, roll = TRUE, nomatch = 0, mult = "all"][start <= end]
并且得到了
# x start end
# 1: a 2 3
# 2: a 3 3
# 3: c 20 22
# 4: e 10 25
这是我想要的正确的行集合但是"pos"已经变成了"start",原来的"start"已经丢失了。有没有办法保留所有带滚动连接的列,这样我就可以按需要报告"start","pos","end"了?
5条答案
按热度按时间8ljdwjyq1#
Overlap joins是在data.table v1.9.3中使用commit 1375实现的,并且在current stable release, v1.9.4中可用。函数名为
foverlaps
。从NEWS中:29)
Overlap joins
#528现在终于来了!!除了type="equal"
、maxgap
和minoverlap
参数之外,其他的都实现了。查看?foverlaps
及其用法示例。这是data.table
的一个主要特性。让我们考虑x,一个定义为
[a, b]
的区间,其中a <= b
,和y,另一个定义为[c, d]
的区间,其中c <= d
。区间y被认为 * 完全重叠 * x,当且仅当d >= a
和c <= b
1。并且y完全包含 * 在 * x内,iffa <= c,d <= b
2.对于实现的不同类型的重叠,请查看?foverlaps
。您的问题是重叠联接的一个特例:在
d1
中,你有start
和end
位置的真实物理间隔。另一方面,在d2
中,只有位置(pos
),而不是interval。为了能够进行重叠连接,我们还需要在d2
中创建interval。这可以通过创建一个额外的变量pos2
来实现,这与pos
(d2[, pos2 := pos]
)相同。因此,我们现在在d2
中有一个区间,尽管具有相同的 start 和 end 坐标。d2
中的这个“虚拟的零宽度区间”然后可以在foverlap
中使用,以便与d1
进行重叠连接:by.y
默认为key(y)
,所以我们跳过它。by.x
默认为如果key(x)
存在,则使用key(x)
,如果d2
不存在,则使用key(y)
。但是d2
不存在键,我们无法设置y
中的列,因为它们的名称不相同。因此,我们显式设置by.x
。重叠的 * 类型 * 是 within,我们希望有 * 所有 * 匹配,只有在有匹配的情况下。
注意:
foverlaps
在底层使用了data.table的二分查找特性(必要时沿着使用了roll
),但一些函数参数(重叠类型、maxgap、minoverlap等)受到Bioconductor包IRanges
中的findOverlaps()
函数的启发,这是一个优秀的包(GenomicRanges
也是如此,它为Genomics扩展了IRanges
)。那么优势是什么呢?
对上述代码进行基准测试,您的数据结果比Gabor的答案慢x1m42 n1倍(计时:Gabor的数据。表格解决方案= 0.004 vs foverlaps = 0.021秒)。但在这个粒度下,这真的很重要吗?
真正有趣的是看看它在 * 速度 * 和 * 内存 * 方面的伸缩性如何。在Gabor的答案中,我们基于键列
x
进行连接。* 然后 * 过滤结果。如果
d1
有大约4万行,d2
有10万行(或更多),那么对于d2
中与d1
中的x
匹配的 * 每一行 , 所有 * 这些行都将匹配并返回,只是在稍后进行过滤。下面是一个Q值略微调整的示例:生成数据:
折叠:
这总共占用了大约1GB的内存,其中
ans1
是420 MB。这里花费的大部分时间实际上是在子集上。你可以通过设置参数verbose=TRUE
来检查它。Gabor解决方案:
而这总共花了~ 3. 5GB。
我刚刚注意到Gabor已经提到了中间结果所需的内存。所以,尝试一下
sqldf
:总共花了~ 1. 4GB。所以,它肯定比上面显示的使用更少的内存。
[The从
ans1
中删除pos2
并在两个答案上设置密钥后,验证答案相同。]请注意,此重叠连接在设计时会遇到一些问题,即
d2
不一定具有相同的起始坐标和结束坐标(例如:基因组学,我来自的领域,其中d2
通常是大约30-150百万或更多行)。foverlaps()
是稳定的,但仍在开发中,这意味着一些参数和名称可能会更改。NB:既然我在上面提到了
GenomicRanges
,它也完全有能力解决这个问题。它在引擎盖下使用interval trees,而且内存效率也相当高。在我对基因组数据的基准测试中,foverlaps()
更快。但这是另一篇(博客)文章,改天吧。vyswwuz22#
data.table v1.9.8+
有一个新特性-- non-equi joins。有了这个特性,这个操作就变得更加简单了:oogrdqng3#
给出:
给出:
请注意,这确实有一个缺点,即可能会形成较大的中间结果
d1[d2]
,而SQL可能不会这样做。其余的解决方案也可能存在这个问题。between
:给出:
给出:
axr492tv4#
重叠连接在
dplyr 1.1.0
中通过函数join_by
可用。对于
join_by
,您可以使用between
执行重叠连接,或者手动使用>=
和<=
执行重叠连接:7kjnsjlb5#
使用
fuzzyjoin
:由于
fuzzyjoin
返回了所有列,因此我们可能需要进行一些清理以保留所需的列。