2013-02-20 31 views
13

Chcę dodać nową kolumnę do mojego data.table. Ta kolumna powinna zawierać sumę innej kolumny wszystkich wierszy spełniających pewien warunek. Przykład: My data.table wygląda następująco:Jak samodzielnie dołączyć do data.table pod warunkiem

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 

dla każdego wiersza i każdej x rzędu i, gdzie ABS (t [b] t - [X]) < = 10, że aby obliczyć

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

W SQL rozwiązałem to za pomocą samołączenia. W badania udało mi się to zrobić za pomocą pętli 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 

Niestety muszę to robić dość często i tabela pracuję jest raczej większa. Podejście pętli for działa, ale jest zbyt wolne. Grałem z pakietem sqldf, bez prawdziwego przełomu. Chciałbym to zrobić za pomocą magii danych.table i potrzebuję twojej pomocy :-). Myślę, że to, co jest potrzebne, to pewnego rodzaju sprzężenie własne pod warunkiem, że różnica wartości t jest mniejsza niż próg.

Kontynuacja: Mam kontynuacji pytanie: W mojej aplikacji tego sprzężenia odbywa się w kółko. Zmiana v, ale t's i n są zawsze takie same. Zastanawiam się więc, w jaki sposób przechowywać, które wiersze należą do siebie. Jakieś pomysły, jak to zrobić w sprytny sposób?

+0

z twojego wyjścia, wygląda na to, że masz też warunek "i! = X", czy to prawda? –

+0

Nie. Dla wiersza 9 foo = 0, ponieważ pojęcie abs (t-DT [i] $ t) == 0. Ale i! = X nie powinno być wykluczone, ponieważ obliczenia w mojej aplikacji są nieco bardziej skomplikowane, jak w tym przykładzie i potrzebuję tam wiersza x. – uuazed

Odpowiedz

5

Spróbuj wykonać następujące czynności:

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

Podział na powyższej linii:

Można łączyć ze sobą stolik, wyjście będzie również data.table. Zauważ, że nazwy kolumn zostaną podane przyrostek .x i .y

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

... można po prostu odfiltrować i obliczyć jak każdy 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)]) ] 

wreszcie owijając to wszystko w celu usunięcia unique dowolne zduplikowane wiersze.


UPDATE: powinno to być komentarz, ale jest zbyt długi

Poniższa linia jest co pasuje do wyjścia. Jedyną różnicą między tą i tą na początku tej odpowiedzi jest termin v.y w sum(v.y * ...) jednak instrukcja by nadal używa v.x. Czy to jest zamierzone?

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

Dzięki! Jest to około 6 razy szybsze, niż moje podejście do pętli w próbce. Spróbuję tego na prawdziwych danych teraz ... – uuazed

+0

Cieszę się, że pomogę ... Co ciekawe, używam 'v.x' i wygląda na to, że używasz' v.y' Który z nich jest twoim oczekiwaniem? –

+0

Odnośnie scalania. Używasz standardowej funkcji scalania. Tabela data.table jak DT [DT] powinna być szybsza, prawda? – uuazed

11

Świetne pytanie. Ta odpowiedź jest po prostu prawdziwą próbą wraz z odpowiedzią Ricarda.

Idealnie chcemy uniknąć dużego cartesian self join dla wydajności. Niestety range joins (FR#203) nie zostały jeszcze zaimplementowane. W międzyczasie, używając najnowszego v1.8.7 (niesprawdzone):

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

Po FR nr 203 odbywa logika powyżej byłyby wbudowane, i powinno stać się jednym liner:

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

Druga kolumna tabeli tam i to kolumna 2-kolumnowa (oznaczająca pomiędzy łączeniem). To powinno być szybkie, ponieważ, jak zwykle, j byłby oceniany dla każdego wiersza i bez konieczności tworzenia ogromnej kartezjańskiej tabeli sprzężenia.

Takie jest obecne myślenie.

+0

Spróbuję, gdy wydana zostanie data.table v1.8.7 – uuazed

+0

Czy FR # 203 już jest zaimplementowane? Złożyłem następujące pytanie w SO i myślę, że skorzystałoby ono z propozycji, którą złożyłeś. http://stackoverflow.com/questions/29100911/r-data-table-self-join-on-condition-using-a-matrix – Picarus

Powiązane problemy