2013-02-20 77 views
13

我想添加一個新的列到我的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 

對於每一行x和每行i,其中abs(T [1] - 噸[X])< = 10,欲計算

foo = sum(v[i] * abs(t[i] - t[x])) 

在SQL中,我會用自連接來解決這個問題。在R I是能夠做到這一點使用一個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 

不幸的是我需要頻繁地做到這一點,我的工作表是相當大的。 for-loop方法有效,但速度太慢。我玩過sqldf軟件包,沒有真正的突破。我很樂意使用一些data.table魔術來做到這一點,我需要你的幫助:-)。我認爲在t值的差值小於閾值的條件下,需要某種自加入。

追問: 我有一個跟進的問題:在我的應用程序此連接一遍又一遍做。 v的變化,但t和n都是一樣的。所以我正在考慮以某種方式存儲哪些行歸在一起。任何想法如何以聰明的方式做到這一點?

+0

從你的輸出中,它看起來像你還有一個條件'我!= x'是否正確? – 2013-02-20 16:19:56

+0

編號對於第9行foo = 0,因爲術語abs(t-DT [i] $ t)== 0。但是我不應該排除i!= x,因爲在我的應用程序中的計算有點複雜,因爲在這個例子中我需要x行。 – uuazed 2013-02-20 16:26:52

回答

5

嘗試以下操作:

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)]) 

擊穿了上述線路

可以合併一個表本身的輸出也將是一個data.table。請注意,列名稱將給予.x後綴和.y

merge(DT, DT, by="n") 

...你可以過濾和計算與任何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)]) ] 

後來終於包裝這一切在unique刪除任何重複的行。


UPDATE:這應該是一個評論,但太長

下面的線是你的輸出相匹配。與此答案頂部的唯一區別在於sum(v.y * ...)中的術語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)]) 
+0

謝謝!這比樣本上的for-loop-approach快大約6倍。我現在試試這個真實的數據...... – uuazed 2013-02-20 16:42:17

+0

很高興幫助...有趣的是,我使用'v.x',看起來你可能正在使用'v.y'你計算期望哪一個? – 2013-02-20 16:45:50

+0

關於合併。您正在使用標準合併功能。像DT [DT]這樣的data.table合併應該更快,對嗎? – uuazed 2013-02-20 16:54:10

11

偉大的問題。這個答案與李嘉圖的答案一樣真實。

理想情況下,我們希望避免大型笛卡爾自我連接的效率。不幸的是range joins (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]))]) 
} 

一旦FR#203完成後,上述邏輯將被內置,並且它應該成爲一個襯裏:

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

i表那裏的第二列是一個2列的列(表示連接之間的)。這應該很快,因爲像往常一樣,j將針對每行i進行評估,而無需創建巨大的笛卡爾自連接表。

無論如何,這就是目前的想法。

+0

我會嘗試一次data.table v1.8.7發佈 – uuazed 2013-02-20 17:42:16

+0

是否已經實施了FR#203?我在SO中提出了以下問題,我認爲它會從您提出的建議中受益。 http://stackoverflow.com/questions/29100911/r-data-table-self-join-on-condition-using-a-matrix – Picarus 2015-03-18 00:54:49