2017-10-23 65 views
5

Próbuję znaleźć najbliższą wartość dla każdej traktowanej obserwacji. Dane wyglądać następująco (dane z częściowym 1.2M OB):Znajdowanie najbliższej wartości w data.table

> dta 
    id treatment  score 
1: 5   0 0.02381024 
2: 10   0 0.05428605 
3: 22   0 0.02118124 
4: 27   0 0.01495214 
5: 45   0 0.01877916 
6: 50   0 0.02120360 
7: 58   0 0.02207263 
8: 60   0 0.02807019 
9: 61   0 0.05432927 
10: 65   1 0.59612077 
11: 68   0 0.02482168 
12: 72   1 0.14582400 
13: 73   0 0.02371670 
14: 77   0 0.02608826 
15: 87   0 0.06852409 
16: 88   0 0.07473471 
17: 94   0 0.07160314 
18: 97   0 0.02040747 
19: 104   1 0.09878789 
20: 108   0 0.02421807 

dla każdej poddanych obserwacji (tj leczenie = 1) chciałbym uzyskać nieleczoną obserwacji (tj leczenie = 0) z najbliższy wynik i oznaczyć wybraną obserwację jako nieosiągalną dla innych poddanych obserwacji obserwacji.

Na przykład pierwsza traktowana obserwacja (wiersz 10) zostanie dopasowana do id = 88 (wiersz 16), wiersz 12 do wiersza 17 i tak dalej. Obecnie używam pętlę floowing:

smpl_treated = dta[treatment == 1] 
smpl_untreated = dta[treatment == 0] 

n_tmp = nrow(smpl_treated) 
matched_id = matrix(0, n_tmp, 1) 

smpl_tmp = smpl_untreated 

for (i in 1:nrow(smpl_treated)) { 

    x = smpl_treated[i]$score 

    setkey(smpl_tmp, score) 
    tmp = smpl_tmp[J(x), roll = "nearest"] 
    matched_id[i] = tmp[[1]] 
    smpl_tmp = smpl_tmp[id != tmp[[1]]] 

} 

matched_smpl = smpl_untreated[id %in% matched_id] 

> matched_smpl 
    id treatment  score 
1: 87   0 0.06852409 
2: 94   0 0.07160314 
3: 88   0 0.07473471 

Wszelkie sugestie, aby to nastąpić w przeciągu data.table lub zrobić pętlę szybciej? Z oryginalnym 1.2M obs pętla trwa ponad 2 godziny. Dzięki za pomoc z góry!

+0

Załóżmy, że 5 następujących próbek: {(id = 1, leczenia = 0, wynik = 0), (id = 2 leczenie = 1, wynik = 0,1), (id = 3, leczenie = 1, wynik = 0,2), (id = 4, leczenie = 1, wynik = 0,3), (id = 5, leczenie = 0, wynik = 0,4)}. Innymi słowy, masz 3 traktowane obserwacje zaklinowane pomiędzy dwoma nieleczonymi obserwacjami. W tym przypadku, co mapuje do czego? – Ben

+0

W moim kontekście tak się nie stanie. Jeśli tak się jednak stanie, prawdopodobnie powinienem postąpić odwrotnie - głównym celem jest uzyskanie zrównoważonej próbki traktowanych i nieleczonych obserwacji. – jayc

Odpowiedz

1

ta rozwija the already accepted answer of denis pomocą rzeczywiste możliwości składnia możliwości, np., Użyj on zamiast setkey() podczas dołączania.

# determine the minimum number of treated and untreated cases 
n <- min(dta[treatment == 0L, .N], dta[treatment == 1L, .N]) 
# order by descending score 
mdt <- dta[order(-score)][ 
    # and pick the ids of the top n treated and untreated cases 
    # so that the highest untreated score match the highest treated score, 
    # the 2nd highest untreated the 2nd highest treated and so forth 
    , .(id0 = head(.SD[treatment == 0L, id], n), id1 = head(.SD[treatment == 1L, id], n))] 
mdt 
id0 id1 
1: 88 65 
2: 94 72 
3: 87 104 
# join the ids two times to show the data of the treated and untreated cases 
dta[dta[mdt, on = .(id==id0)], on = .(id = id1)] 
id treatment  score i.id i.treatment i.score 
1: 65   1 0.59612077 88   0 0.07473471 
2: 72   1 0.14582400 94   0 0.07160314 
3: 104   1 0.09878789 87   0 0.06852409 
+0

To rozwiązanie jest o wiele lepsze niż moje i faktycznie spełnia swoją funkcję (nie zakłada, że ​​dane dotyczące leczenia i nieleczenia mają inny zakres punktacji). Dziękujemy za udostępnienie – denis

+0

@denis Dziękujemy za miły komentarz. Twoje rozwiązanie wskazało jednak właściwy kierunek! – Uwe

1

Mogę mieć rozwiązanie, jeśli zamówisz tabelę danych, utworzysz podzbiór i użyjesz możliwości scalania. Nie jestem pewien, że jest to najlepsze rozwiązanie, ale wydaje się, aby pracować na co zrozumiałem chcesz zrobić, i będzie to na pewno szybciej niż pętli:

library(data.table) 
dta <- data.table(id = c(5,10,22,27,45,50,58,60,61,65,68,72,73,77,87,88,94,97,104,108), 
        treatment = c(0, 0 ,0 ,0, 0, 0, 0 ,0 , 0 , 1, 0 ,1 ,0, 0 ,0 ,0 ,0 ,0 ,1 ,0), 
        score = c(0.02381024, 0.05428605, 0.02118124, 0.01495214, 0.01877916, 0.02120360, 
          0.02207263, 0.02807019, 0.05432927, 0.59612077, 0.02482168, 0.14582400, 
          0.02371670, 0.02608826, 0.06852409, 0.07473471, 0.07160314, 0.02040747, 
          0.09878789, 0.02421807)) 

setkey(dta, score) # order by score 
treated_nbr <- dta[treatment == 1, .N] # just to simplify the next line 

selecteddata <- 
    dta[treatment == 0, 
     .SD[(.N - treated_nbr + 1):.N, 
      .(correspid = id, 
      correspscore = score, 
      id = dta[treatment == 1, id])]] 

tutaj bierzemy taką samą liczbę zamówił non traktowana osoba (.N-treated_nbr+1):.N) tak, że mają najbliższy wynik do zamówionej jeden, a my scalić identyfikator id leczonego jednego (id = dta[,.SD[treatment == 1,id]])

setkey(selecteddata, id) 
setkey(dta, id) 
selecteddata[dta] # do the merging 

Nie wiem, to jest dokładnie to, co chcesz, bo zdałem sobie sprawę, że działa tylko wtedy, gdy twoje wyniki leczenia są wyższe niż te, które nie są traktowane (co jest przykładem w twoim przykładzie). Można dodać warunek korzystania z rozwiązania zaproponowanego tylko dla leczonej osoby z wynikiem wyższym niż te spoza leczona, a zajmiemy się resztą inaczej (nie widzę bezpośredniego proste rozwiązanie inaczej)

+0

To bardzo pomaga - dzięki, Denis! – jayc