2015-10-12 9 views
8

TłoWewnętrzna przyłączyć za pomocą wyrażenia nierówność

(nie jest wymagany na pytanie, ale może być przydatny do odczytu)

Rolling join on data.table with duplicate keys

Odd behaviour when joining with multiple conditions

danych

library(data.table) ## using version 1.9.6 
## arrival timetable 
dt_arrive <- structure(list(txn_id = c(1L, 1L, 1L, 1L, 1L), place = c("place_a", 
"place_a", "place_a", "place_a", "place_a"), arrival_minutes = c(515, 
534, 547, 561, 581), journey_id = 1:5), .Names = c("txn_id", 
"place", "arrival_minutes", "journey_id"), class = c("data.table", 
"data.frame"), row.names = c(NA, -5L), sorted = c("txn_id", 
"place")) 

## departure timetable 
dt_depart <- structure(list(txn_id = c(1L, 1L, 1L, 1L), place = c("place_a", 
"place_a", "place_a", "place_a"), arrival_minutes = c(489, 507, 
519, 543), journey_id = 10:13), .Names = c("txn_id", "place", 
"arrival_minutes", "journey_id"), sorted = c("txn_id", "place" 
), class = c("data.table", "data.frame"), row.names = c(NA, -4L 
)) 

> dt_arrive 
    txn_id place arrival_minutes journey_id 
1:  1 place_a    515   1 
2:  1 place_a    534   2 
3:  1 place_a    547   3 
4:  1 place_a    561   4 
5:  1 place_a    581   5 

> dt_depart 
    txn_id place arrival_minutes journey_id 
1:  1 place_a    489   10 
2:  1 place_a    507   11 
3:  1 place_a    519   12 
4:  1 place_a    543   13 

Pytanie

ja przyłączyć się do przybyłych do pójścia do tylko tych dt_depart$journey_id które występują podt_arrive$journey_id pod względem arrival_minutes (tj sprzężenie wewnętrzne na txn_id & place)

Na przykład, to wyjście ja jak to:

txn_id place journey_in_id journey_out_id journey_place_arrive journey_place_depart 
     1  place_a   1    12     515     519 
     1  place_a   1    13     515     543 
     1  place_a   2    13     534     543 

Próby

Stosując metodę z dwóch połączonych pytań, skonstruowaliśmy

setkey(dt_arrive, txn_id, place) 
setkey(dt_depart, txn_id, place) 

dt_join <- dt_arrive[dt_depart, 
      { 
       idx = (i.arrival_minutes > arrival_minutes) 
       .(journey_in_id = journey_id[idx], 
       journey_out_id = i.journey_id, 
       journey_place_arrive = arrival_minutes[idx], 
       journey_place_depart = i.arrival_minutes 
      ) 
      }, 
      by=.EACHI] 

Ale to daje wszystko od dt_depart, a więc obejmuje NA S w rezultacie - co sugeruje „prawo dołączyć”:

txn_id place journey_in_id journey_out_id journey_place_arrive journey_place_depart 
1:  1 place_a   NA    10     NA     489 
2:  1 place_a   NA    11     NA     507 
3:  1 place_a   1    12     515     519 
4:  1 place_a   1    13     515     543 
5:  1 place_a   2    13     534     543 

Próbowałem została, używając nomatch=0 aby zmusić go do „sprzężenia wewnętrznego”, ale to nie działało.

Mogę użyć complete.cases, aby usunąć wiersze NA, ale zastanawiałem się, czy istnieje sposób na wykonanie tego w samym zapytaniu?

+0

Dlaczego twoje wyjście "Na przykład" wykluczenia 534 do 543 linii? – Frank

+0

@Frank litero - należy dołączyć. – tospig

+0

@Frank Czy usunąłeś swoją odpowiedź? Pomyślałem, że w każdym razie zawiera trzy wymagane linie? – tospig

Odpowiedz

3

Oto nieczyste podejście: weź krzyż/kartezjańskie połączenie, a następnie filtruj.

merge(dt_arrive, dt_depart, allow.cartesian=TRUE)[arrival_minutes.y > arrival_minutes.x] 

# txn_id place arrival_minutes.x journey_id.x arrival_minutes.y journey_id.y 
# 1:  1 place_a    515   1    519   12 
# 2:  1 place_a    515   1    543   13 
# 3:  1 place_a    534   2    543   13 

Biorąc kartezjański łącznik, jesteśmy gotowi zjeść dużo pamięci.

+1

Przeczytałem szybko i pomyślałem, że powiedziałeś "tutaj jest sprytne podejście". Pomyślałem "nie, to nie jest sprytne!", A potem zrozumiałem mój błąd. Sprytna czy nie, to jedyna działająca odpowiedź do tej pory :) – Gregor

+0

Ta metoda sprawdza testy szybciej niż moja metoda. Teraz muszę przetestować, jak dobrze skaluje się do moich rzeczywistych danych. – tospig

+0

gra z inną zmienną data.table (inspirowaną tą odpowiedzią) 'dt_arrive [dt_depart, nomatch = 0, allow.cartesian = TRUE] [i.arrival_minutes> arrival_minutes]' wydaje się jeszcze szybciej. – tospig

1

Potencjalnym rozwiązaniem jest użycie foverlaps poprzez wprowadzenie pewnych arbitralnych interwału kolumny

setDT(dt_arrive) 
setDT(dt_depart) 

dt_arrive[, `:=`(arrival_minutes_copy = arrival_minutes)] 
## reorder columns 
dt_arrive <- dt_arrive[, .(txn_id, place, journey_id, arrival_minutes, arrival_minutes_copy)] 

dt_depart[, `:=`(arrival_minutes_copy = min(arrival_minutes))] 
## reorder columns 
dt_depart <- dt_depart[, .(txn_id, place, journey_id, arrival_minutes_copy, arrival_minutes)] 

setkey(dt_arrive, arrival_minutes, arrival_minutes_copy) 
setkey(dt_depart, arrival_minutes_copy, arrival_minutes) 

foverlaps(dt_arrive, 
      dt_depart, 
      type = "within", 
      nomatch=0L) 


#  place txn_id journey_id arrival_minutes_copy arrival_minutes i.txn_id i.journey_id i.arrival_minutes i.arrival_minutes_copy 
# 1: place_a  1   12     489    519  1   1    515     515 
# 2: place_a  1   13     489    543  1   1    515     515 
# 3: place_a  1   13     489    543  1   2    534     534 

Benchmarking

library(microbenchmark) 

fun_foverlap <- function(dt_a, dt_d){ 
    dt <- foverlaps(dt_a, 
            dt_d, 
         type = "within", 
         nomatch=0L) 
    return(dt) 
} 

fun_merge <- function(dt_a, dt_d){ 
    dt <- merge(dt_a, dt_d, allow.cartesian=TRUE)[arrival_minutes.y > arrival_minutes.x] 
    return(dt) 
} 

fun_nomatch <- function(dt_a, dt_d){ 
    dt <- dt_a[dt_d, nomatch=0, allow.cartesian=TRUE][i.arrival_minutes > arrival_minutes] 
    return(dt) 
} 

microbenchmark(fun_foverlap(dt_arrive_foverlap, dt_depart_foverlap), 
          fun_merge(dt_arrive_merge, dt_depart_merge), 
          fun_nomatch(dt_arrive_nomatch, dt_depart_nomatch)) 

# Unit: microseconds 
                expr  min  lq  mean median  uq  max neval cld 
# fun_foverlap(dt_arrive_foverlap, dt_depart_foverlap) 3538.189 3717.077 3967.6648 3872.586 4006.7205 5812.355 100 c 
#   fun_merge(dt_arrive_merge, dt_depart_merge) 883.697 925.655 980.4159 942.877 967.9745 2223.147 100 b 
# fun_nomatch(dt_arrive_nomatch, dt_depart_nomatch) 593.082 625.471 682.8975 643.034 665.4125 2077.748 100 a