如何在条件下自行加入data.table

uua*_*zed 15 join r data.table

我想在data.table中添加一个新列.此列应包含满足特定条件的所有行的另一列的总和.一个例子:我的data.table看起来像这样:

require(data.table)
DT <- data.table(n=c("a", "a", "a", "a", "a", "a", "b", "b", "b"),
             t=c(10, 20, 33, 40, 50, 22, 25, 34, 11),
             v=c(20, 15, 16, 17, 11, 12, 20, 22, 10)
             )
DT
   n  t  v
1: a 10 20
2: a 20 15
3: a 33 16
4: a 40 17
5: a 50 11
6: a 22 12
7: b 25 20
8: b 34 22
9: b 11 10
Run Code Online (Sandbox Code Playgroud)

对于每行x和每行i,其中abs(t [i] - t [x])<= 10,我想计算

foo = sum( v[i] * abs(t[i] - t[x]) )
Run Code Online (Sandbox Code Playgroud)

在SQL中,我会使用自联接来解决这个问题.在RI中,能够使用for循环执行此操作:

for (i in 1:nrow(DT))
    DT[i, foo:=DT[n==DT[i]$n & abs(t-DT[i]$t)<=10, sum(v * abs(t-DT[i]$t) )]]

DT
   n  t  v foo
1: a 10 20 150
2: a 20 15 224
3: a 33 16 119
4: a 40 17 222
5: a 50 11 170
6: a 22 12  30
7: b 25 20 198
8: b 34 22 180
9: b 11 10   0
Run Code Online (Sandbox Code Playgroud)

不幸的是,我必须经常这样做,我使用的表格相当大.for循环方法有效,但速度太慢.我玩了sqldf包,没有真正的突破.我很乐意使用一些data.table魔法来做到这一点,我需要你的帮助:-).我认为需要的是某种自连接,条件是t值的差异小于阈值.

跟进: 我有一个跟进问题:在我的应用程序中,这个连接一次又一次地完成.v的变化,但t和n总是一样的.所以我想以某种方式存储哪些行属于一起.有任何想法如何以聪明的方式做到这一点?

Mat*_*wle 12

好问题.这个答案与里卡多的答案真的很合适.

理想情况下,我们希望避免大型笛卡尔自连接以提高效率.遗憾的是,范围连接(FR#203)尚未实现.在此期间,使用最新的v1.8.7(未经测试):

setkey(DT,n,t)
DT[,from:=DT[.(n,t-10),which=TRUE,roll=-Inf,rollends=TRUE]]
DT[,to:=DT[.(n,t+10),which=TRUE,roll=+Inf,rollends=TRUE]]
DT[,foo:=0L]
for (i in 1:nrow(DT)) {
    s = seq.int(DT$from[i],DT$to[i])
    set(DT, i, "foo", DT[,sum(v[s]*abs(t[s]-t[i]))] )
}
Run Code Online (Sandbox Code Playgroud)

一旦FR#203完成,上面的逻辑将被内置,它应该成为一个班轮:

setkey(DT,n,t)
DT[.(n,.(t-10,t+10),t), foo:=sum(v*abs(t-i.t))]
Run Code Online (Sandbox Code Playgroud)

i表的第二列有一个2列的列(表示连接之间).这应该很快,因为像往常一样,j将对每一行进行评估,i而无需创建巨大的笛卡尔自连接表.

无论如何,那是当前的想法.


Ric*_*rta 6

请尝试以下方法:

unique(merge(DT, DT, by="n")[abs(t.x - t.y) <= 10, list(n, sum(v.x * abs(t.x - t.y))), by=list(t.x, v.x)])
Run Code Online (Sandbox Code Playgroud)

以上细分的细分:

您可以将表与自身合并,输出也将是data.table.请注意,列名称的后缀为.x.y

merge(DT, DT, by="n")
Run Code Online (Sandbox Code Playgroud)

...你可以像任何DT一样过滤和计算

# this will give you your desired rows
[abs(t.x - t.y), ]

# this is the expression you outlined
[ ... , sum(v.x * abs(t.x - t.y)) ]

# summing by t.x and v.x
[ ... , ... , by=list(t.x, v.x)]) ]
Run Code Online (Sandbox Code Playgroud)

然后最终将其全部包装unique以删除任何重复的行.


更新:这应该是一个评论,但太长了

下面的行与您的输出相匹配.在这个答案的顶部这和一个唯一的区别是长期v.ysum(v.y * ...),不过by声明中仍然使用v.x.这是故意的吗?

unique(merge(DT, DT, by="n")[abs(t.x - t.y) <= 10, list(n, sum(v.y * abs(t.x - t.y))), by=list(t.x, v.x)])
Run Code Online (Sandbox Code Playgroud)