2016-07-26 13 views
3

Gibt es eine Möglichkeit, Datenrahmen (linker äußerer Join) durch mehrere Spalten zusammenzuführen, aber mit ODER-Bedingung?linker äußerer Join in R mit Bedingungen

Beispiel: Es gibt zwei Datenrahmen df1 und df2 mit Säulen x, y, num. Ich hätte gerne einen Datenrahmen mit allen Zeilen von df1, aber nur mit den Zeilen von df2, die die Bedingungen erfüllen: df1$x == df2$x OR df2$y == df2y.

Hier sind Beispieldaten:

df1 <- data.frame(x = LETTERS[1:5], 
        y = 1:5, 
        num = rnorm(5), stringsAsFactors = F) 
df1 
    x y  num 
1 A 1 0.4209480 
2 B 2 0.4687401 
3 C 3 0.3018787 
4 D 4 0.0669793 
5 E 5 0.9231559 

df2 <- data.frame(x = LETTERS[3:7], 
        y = 3:7, 
        num = rnorm(5), stringsAsFactors = F) 
df2$x[4] <- NA 
df2$y[3] <- NA 
df2 
    x y  num 
1 C NA -0.7160824 
2 <NA> 4 -0.3283618 
3 E 5 -1.8775298 
4 F 6 -0.9821082 
5 G 7 1.8726288 

Dann wird das Ergebnis zu erwarten sein:

x y  num x y  num 
1 A 1 0.4209480 <NA> NA   NA 
2 B 2 0.4687401 <NA> NA   NA 
3 C 3 0.3018787 C NA -0.7160824 
4 D 4 0.0669793 <NA> 4 -0.3283618 
5 E 5 0.9231559 E 5 -1.8775298 

Die naheliegendste Lösung ist die sqldf Paket zu verwenden:

mergedData <- sqldf::sqldf("SELECT * FROM df1 
          LEFT OUTER JOIN df2 
          ON df1.x = df2.x 
          OR df1.y = df2.y") 

Leider ist diese einfache Lösung extrem langsam und es wird ewig dauern, Datenrahmen mit m zu verschmelzen Erz als 100k Reihen jeder.

Eine weitere Option ist das Aufteilen des richtigen Datenrahmens und das Zusammenführen nach Teilen, aber gibt es dort eine elegantere oder gar "out of the box" Lösung?

+3

Warum der Downvote? Dies ist eine gut geschriebene Frage. –

+1

Ich habe es nicht heruntergestimmt, aber es klingt wie Sie eine funktionierende Lösung haben und Sie wollen nur herausfinden, wie man es schneller macht. Dies ist der Fall, diese Frage wäre besser geeignet für http://codereview.stackexchange.com –

+3

@ Hack-R nicht zustimmen: Code Überprüfung ist nicht (in erster Linie) zur Verbesserung der Leistung, es ist für die Verbesserung der * Code-Qualität *. Die Frage, wie sie ist, ist perfekt für Stack Overflow geeignet: Es gibt ein technisches Problem, das gelöst werden muss. –

Antwort

1

Hier ist ein Ansatz mit data.table. Für jede Spalte führen wir einen Join durch, aber extrahieren nur die Indizes (im Gegensatz zum Materialieren des gesamten Joins). Dann können wir diese Indizes aus allen Spalten kombinieren (dieser Teil würde einige Änderungen benötigen, wenn es mehrere Übereinstimmungen geben könnte) .

require(data.table) 
setDT(df1) 
setDT(df2) 

foo <- function(dx, dy, cols) { 
    ix = lapply(cols, function(col) { 
     dy[dx, on=col, which=TRUE] # for each row in dx, get matching indices of dy 
            # by matching on column specified in "col" 
    }) 
    ix = do.call(function(...) pmax(..., na.rm=TRUE), ix) 
} 
ix = foo(df1, df2, c("x", "y")) # obtain matching indices of df2 for each row in df1 
df1[, paste0("col", 1:3) := df2[ix]] # update df1 by reference 
df1 
# x y   num col1 col2  col3 
# 1: A 1 2.09611034 NA NA   NA 
# 2: B 2 -1.06795571 NA NA   NA 
# 3: C 3 1.38254433 C 3 1.0173476 
# 4: D 4 -0.09367922 D 4 -0.6379496 
# 5: E 5 0.47552072 E NA -0.1962038 

können Sie setDF(df1) verwenden, um es zu konvertieren zurück zu einem data.frame, falls erforderlich.

+1

danke für die Lösung, es ist in der Tat viel schneller als sqldf. Es dauerte 23,59 Sekunden für sqldf, um zwei Datenframes von 10k Zeilen zusammenzuführen, während Ihre Lösung in 0,011 Sekunden abgeschlossen war – MikeTexnik

Verwandte Themen