2017-02-16 15 views
1

Ich habe die folgenden zwei Datenrahmen in R:Vergleichen und Zusammenführen von zwei Datenrahmen

df1 = data.frame(c("A", "A", "A", "B", "B"), c(1, 11, 21, 35, 45), c(6, 20, 30, 40, 60), c(1, 2, 3, 4, 5)) 
colnames(df1) = c("X", "Y", "Z", "score") 

df1 
    X Y Z score 
1 A 1 6  1 
2 A 11 20  2 
3 A 21 30  3 
4 B 35 40  4 
5 B 45 60  5 

df2 = data.frame(c("A", "A", "A", "A", "B", "B", "B", "C"), c(1, 6, 21, 50, 20, 31, 50, 10), c(5, 20, 30, 60, 30, 40, 60, 20), c("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8")) 
colnames(df2) = c("X", "Y", "Z", "out") 

df2 
    X Y Z out 
1 A 1 5 x1 
2 A 6 20 x2 
3 A 21 30 x3 
4 A 50 60 x4 
5 B 20 30 x5 
6 B 31 40 x6 
7 B 50 60 x7 
8 C 10 20 x8 

Für jede Zeile in df1, ich überprüfen möchten:

  • es eine Übereinstimmung mit dem Wert im ' X 'und jeder andere' X 'Wert von df2
  • wenn das obige wahr ist: Ich möchte prüfen, ob die Werte von' Y 'und' Z 'im Bereich der Werte' Y 'und' Z 'liegen df2
  • wenn beide wahr sind: dann möchte ich th hinzufügen Der Wert von 'out' nach df1.

Dies ist, wie die Ausgabe aussehen sollte:

output = data.frame(c("A", "A", "A", "B", "B"), c(1, 11, 21, 35, 45), c(6, 20, 30, 40, 60), c(1, 2, 3, 4, 5), c("x1, x2", "x2", "x3", "x4", "x5")) 
colnames(output) = c("X", "Y", "Z", "score", "out") 

    X Y Z score out 
1 A 1 6  1 x1, x2 
2 A 11 20  2  x2 
3 A 21 30  3  x3 
4 B 35 40  4  x6 
5 B 45 60  5  x7 

Die ursprüngliche df1 mit einer zusätzlichen Spalte ‚out‘ gehalten wird, die hinzugefügt wird.

Zeile 1 von 'Ausgabe', enthält 'x1, x2' in Spalte 'out'. Warum: Es gibt eine Übereinstimmung zwischen den Werten in Spalte 'X' und Bereich 1 bis 6 überlappen mit Zeilen 1 und 2 von DF2.

Ich habe diese Frage zuvor gestellt (Compare values from two dataframes and merge), wo vorgeschlagen wird, die foverlaps Funktion zu verwenden. Wegen der unterschiedlichen Spalten zwischen df1 und df2 und den zusätzlichen Zeilen in df2 kann ich es nicht funktionieren lassen.

Antwort

1
library(dplyr) 

df1 = data.frame(c("A", "A", "A", "B", "B"), c(1, 11, 21, 35, 45), 
       c(6, 20, 30, 40, 60), c(1, 2, 3, 4, 5), stringsAsFactors = F) 
colnames(df1) = c("X", "Y", "Z", "score") 

df2 = data.frame(c("A", "A", "A", "A", "B", "B", "B", "C"), c(1, 6, 21, 50, 20, 31, 50, 10), 
       c(5, 20, 30, 60, 30, 40, 60, 20), 
       c("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"), stringsAsFactors = F) 
colnames(df2) = c("X", "Y", "Z", "out") 


df1 %>% 
    left_join(df2, by="X") %>%   # join on main column 
    rowwise() %>%      # for each row 
    mutate(counter = sum(seq(Y.x, Z.x) %in% seq(Y.y, Z.y))) %>% # get how many elements of those ranges overlap 
    filter(counter > 0) %>%   # keep rows with overlap 
    group_by(X, Y.x, Z.x, score) %>% # for each combination of those columns 
    summarise(out = paste(out, collapse=", ")) %>%    # combine out column 
    ungroup() %>% 
    rename(Y = Y.x, 
     Z = Z.x) 

# # A tibble: 5 × 5 
#  X  Y  Z score out 
# <chr> <dbl> <dbl> <dbl> <chr> 
# 1  A  1  6  1 x1, x2 
# 2  A 11 20  2  x2 
# 3  A 21 30  3  x3 
# 4  B 35 40  4  x6 
# 5  B 45 60  5  x7 

Das obige Verfahren auf dplyr Paket basiert und beinhaltet eine join und eine Gruppierung und Filterung. Wenn Ihre anfänglichen Datensätze (df1, df2) extrem groß sind, erstellt der join einen noch größeren Datensatz, der einige Zeit benötigt, um erstellt zu werden.

Beachten Sie auch, dass dieser Prozess mit character und nicht factor Variablen funktioniert. Der Prozess könnte factor Variablen in character konvertieren, wenn er versucht, factor Variablen mit verschiedenen Ebenen zu verbinden.

Ich würde vorschlagen, dass Sie die verketteten Befehle Schritt für Schritt ausführen, um zu sehen, wie es funktioniert, und herausfinden, ob ich etwas verpasst habe, was zu Fehlern im Code führen könnte.

+0

Wie kann ich die Variable ‚stringAsFactors = F“ für einen bereits bestehenden Datenrahmen gesetzt? – user1987607

+0

Versuchen Sie zunächst, den gleichen Prozess mit 'factor' Variablen laufen, weil es sie umwandeln könnte zu "Charakter", wenn es versucht, Faktoren mit verschiedenen Ebenen zu verbinden. – AntoniosK

+1

@AntioniosK: meine df1 hat 9000 Zeilen, meine df2 hat 862 Zeilen. Ihr Code arbeitete fließend mit einer kleinen Teilmenge, aber mit den vollständigen Daten wird es eine ganze Weile dauern Ich nehme an ... – user1987607

0

Hier ist eine weitere Optionen sqldf

library(sqldf) 
xx=sqldf('select t1.*,t2.out from df1 t1 left join df2 t2 on t1.X=t2.X and ((t2.Y between t1.Y and t1.Z) or (t2.Z between t1.Y and t1.Z))') 
aggregate(xx[ncol(xx)], xx[-ncol(xx)], FUN = function(X) paste(unique(X), collapse=", ")) 
2

Hier sind zwei Möglichkeiten: a) mit dem neu implementierten nicht equi mit Merkmal verbindet, und b) foverlaps, wie Sie gesagt, dass erwähnt würde ..

a) nicht-equi verbindet

dt2[dt1, on=.(X, Z>=Y, Y<=Z), 
     .(score, out=paste(out, collapse=",")), 
    by=.EACHI] 

wo dt1 und dt2 sind Datentabellen entsprechend df1 und df2. Beachten Sie, dass Sie die Spaltennamen Z und Y im Ergebnis zurücksetzen müssen (da die Spaltennamen von dt2 stammen, aber die Werte von dt1.

Passende Zeilen aus dt2 zu jeder Zeile entspricht, ist dt1 gefunden unter der Bedingung an das Argument on vorgesehen basiert und .() wird für jedes dieser passenden Reihen (wegen by=.EACHI) bewertet.

b) foverlaps

setkey(dt1, X, Y, Z) 
olaps <- foverlaps(dt2, dt1, type="any", nomatch=0L) 
olaps[, .(score=score[1L], out=paste(out, collapse=",")), by=.(X,Y,Z)] 
Verwandte Themen