R 有效地检查data.table中其他行的值
注意:这是我最初发布到data.table帮助组的一个问题。马特·道尔(Matt Dowle)要求提供一个更详细的示例,我发布了这个示例,但我在电子邮件格式方面遇到了问题。我已经知道如何在SO上格式化,所以我想我会把它贴在这里 基本上,我要做的是根据data.table中的值以及前一行或后一行中的值对该行进行子集。现在,我正在为将来和过去的行创建新列,然后在这些列上键入data.table,但这是资源密集型的,而且非常繁重 下面的示例说明了我现在使用的方法。该示例使用文档中的单词(我使用数字索引)。我想为一个特定单词创建子集,但前提是它前面或后面有另一个单词或一组单词: 我首先创建一个虚拟数据集,其中包含10个包含一百万个单词的文档。集合中有三个独特的单词R 有效地检查data.table中其他行的值,r,data.table,R,Data.table,注意:这是我最初发布到data.table帮助组的一个问题。马特·道尔(Matt Dowle)要求提供一个更详细的示例,我发布了这个示例,但我在电子邮件格式方面遇到了问题。我已经知道如何在SO上格式化,所以我想我会把它贴在这里 基本上,我要做的是根据data.table中的值以及前一行或后一行中的值对该行进行子集。现在,我正在为将来和过去的行创建新列,然后在这些列上键入data.table,但这是资源密集型的,而且非常繁重 下面的示例说明了我现在使用的方法。该示例使用文档中的单词(我使用数字索引
library(data.table)
set.seed(1000)
DT<-data.table(wordindex=sample(1:3,1000000,replace=T),docindex=sample(1:10,1000000,replace=T))
setkey(DT,docindex)
DT[,position:=seq.int(1:.N),by=docindex]
wordindex docindex position
1: 1 1 1
2: 1 1 2
3: 3 1 3
4: 3 1 4
5: 1 1 5
---
999996: 2 10 99811
999997: 2 10 99812
999998: 3 10 99813
999999: 1 10 99814
1000000: 3 10 99815
库(data.table)
种子集(1000)
DT听起来你只是想:
DT[, sum(wordindex == 1 & c(tail(wordindex, -1), 2) != 2), by = docindex]
我看不出通过连接使它复杂化的意义
顺便说一句,在某些情况下,你会得到与你不同的答案,这要么是因为我不明白你想要什么,要么是因为你的方法在某些边缘情况下失败。例如,两种方法都可以尝试
DT = data.table(wordindex = c(1,1,2,1,1,2), docindex = c(1,1,2,2,3,3))
只需创建一个lead
函数,并在j表达式中使用它即可:
lead <- function(x, n)
if (n == 0) x else c(tail(x, -n), rep.int(NA, n))
如果您想在i
处获得wordindex
为1L、i+1
为2L、i+2
为3L的计数,则:
DT[, sum(wordindex == 1L & lead(wordindex, 1L) == 2L &
lead(wordindex, 2L) == 3L, na.rm=TRUE), by=docindex]
# docindex V1
# 1: 1 3684
# 2: 2 3746
# 3: 3 3717
# 4: 4 3727
# 5: 5 3700
# 6: 6 3779
# 7: 7 3702
# 8: 8 3756
# 9: 9 3702
# 10: 10 3744
请注意,此处不需要设置键adhoc by
应该很有效
针对该评论:
此解决方案在j
中使用矢量扫描,而不是二进制搜索方法。但这里有不同的权衡。与二进制搜索版本相比,该代码相对优雅,易于阅读,可以扩展到多个滞后和条件,并且可以维护(因为我想不出一种不创建额外列的方法)。这需要更少的内存,这也是您的情况中的一个限制因素
你说的是“大数据”,但别再说了。是的,对整个数据(比如2000万行或2亿行)进行矢量扫描的成本很高。但是,对每个组进行操作,即使不能提供二进制搜索的性能,也不会慢很多。当然,这同样取决于你所观察到的群体数量和每个群体的观察数量。但最好对这些东西进行基准测试并找出答案
我让你去做吧。祝你好运:)。我突然想到另一个主意。它只需要再创建一列,并对子集使用二进制搜索
在根据数据生成的DT
上,首先我们将添加额外的列:
# the extra column:
DT[, I := .I]
我们需要这个,因为我们将在docindex
和wordindex
上设置setkey
。这是我们在不创建额外列的情况下创建子集的唯一方法(至少是我能想到的)。因此,我们以后需要一种方法来提取“原始”位置,以检查您的状况(因此是I
)
添加额外列后,让我们在上面提到的两列上设置键:
setkey(DT, docindex, wordindex)
太好了!这里的想法是提取所需单词匹配的位置-这里的值是1L
。然后,在正确的位置提取你可能(或可能不)想出现在这个单词后面的所有其他单词。然后,我们简单地保留(或删除)那些满足条件的索引
这里有一个函数可以处理这个问题。它绝不是完整的,但应该给你一个想法
foo <- function(DT, doc_key, word_key, rest_key=NULL, match=FALSE) {
## note that I'm using 1.9.3, where this results in a vector
## if you're using 1.9.2, you'll have to change the joins accordingly
idx1 = DT[J(doc_key, word_key), I]
for (i in seq_along(rest_key)) {
this_key = rest_key[i]
idx2 = DT[J(doc_key, this_key), I]
if (match) idx1 = idx1[which((idx1+i) %in% idx2)]
else idx1 = idx1[which(!(idx1+i) %in% idx2)]
}
DT[idx1, .N, by=c(key(DT)[1L])]
}
第二种情况如何?在这里,我们希望i+1
th和i+2
th位置为2L和3L,而不是前面案例中的不相等情况。因此,我们在这里设置match=TRUE
system.time(ans2 <- foo(DT, 1:10, 1L, 2:3,TRUE))
# user system elapsed
# 0.080 0.011 0.090
# old method took 0.22 seconds
# docindex N
# 1: 1 3684
# 2: 2 3746
# 3: 3 3717
# 4: 4 3727
# 5: 5 3700
# 6: 6 3779
# 7: 7 3702
# 8: 8 3756
# 9: 9 3702
# 10: 10 3744
system.time(ans2)这是一个既适用于你的答案又适用于eddi的问题。我曾想过类似的事情(虽然不是那么优雅),但我避免了它,因为(我想)这是一个向量扫描。这是正确的方法吗?重新更新:也许你可以尝试一种混合策略,将它过滤到你感兴趣的单词集(如果你已经有了一个键集,也许可以使用一个简单的连接),然后在上面运行向量扫描(你需要保存位置并区分它们)谢谢eddi,我也可以试一试。另外,对于字符向量,%chin%
要比%
中的%快得多。谢谢你,Arun,我今天和明天都会玩这个,看看我能想出什么。这真的很好,也很快。我仍然有一些游戏要做,以使它在我的设置中正常工作,但我非常肯定不会他解决了我的问题。非常感谢!
DT[, sum(wordindex == 1L & lead(wordindex, 1L) == 2L &
lead(wordindex, 2L) == 3L, na.rm=TRUE), by=docindex]
# docindex V1
# 1: 1 3684
# 2: 2 3746
# 3: 3 3717
# 4: 4 3727
# 5: 5 3700
# 6: 6 3779
# 7: 7 3702
# 8: 8 3756
# 9: 9 3702
# 10: 10 3744
# the extra column:
DT[, I := .I]
setkey(DT, docindex, wordindex)
foo <- function(DT, doc_key, word_key, rest_key=NULL, match=FALSE) {
## note that I'm using 1.9.3, where this results in a vector
## if you're using 1.9.2, you'll have to change the joins accordingly
idx1 = DT[J(doc_key, word_key), I]
for (i in seq_along(rest_key)) {
this_key = rest_key[i]
idx2 = DT[J(doc_key, this_key), I]
if (match) idx1 = idx1[which((idx1+i) %in% idx2)]
else idx1 = idx1[which(!(idx1+i) %in% idx2)]
}
DT[idx1, .N, by=c(key(DT)[1L])]
}
system.time(ans1 <- foo(DT, 1:10, 1L, 2L, FALSE))
# user system elapsed
# 0.066 0.019 0.085
# old method took 0.12 seconds
# docindex N
# 1: 1 22301
# 2: 2 21836
# 3: 3 22491
# 4: 4 21831
# 5: 5 22218
# 6: 6 21914
# 7: 7 22370
# 8: 8 22265
# 9: 9 22211
# 10: 10 22190
system.time(ans2 <- foo(DT, 1:10, 1L, 2:3,TRUE))
# user system elapsed
# 0.080 0.011 0.090
# old method took 0.22 seconds
# docindex N
# 1: 1 3684
# 2: 2 3746
# 3: 3 3717
# 4: 4 3727
# 5: 5 3700
# 6: 6 3779
# 7: 7 3702
# 8: 8 3756
# 9: 9 3702
# 10: 10 3744