Gegeben zwei Datenrahmen:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Wie kann ich Datenbankstile, dh SQL-Stile, Joins erstellen ? Das heißt, wie bekomme ich:
- Ein innerer Join von
df1
unddf2
:
Gibt nur die Zeilen zurück, in denen die linke Tabelle übereinstimmende Schlüssel in der rechten Tabelle enthält. - Ein äußerer Join von
df1
unddf2
:
Gibt alle Zeilen aus beiden Tabellen zurück, Join-Datensätze von links, die übereinstimmende Schlüssel in der rechten Tabelle haben. - Eine linke äußere Verknüpfung (oder einfach eine linke Verknüpfung) von
df1
unddf2
Gibt alle Zeilen aus der linken Tabelle sowie alle Zeilen mit übereinstimmenden Schlüsseln aus der rechten Tabelle zurück. - Ein rechter äußerer Join von
df1
unddf2
Gibt alle Zeilen aus der rechten Tabelle sowie alle Zeilen mit übereinstimmenden Schlüsseln aus der linken Tabelle zurück.
Extra Gutschrift:
Wie kann ich eine SQL-Style-Select-Anweisung ausführen?
Antworten:
Mit der
merge
Funktion und ihren optionalen Parametern:Innerer Join:
merge(df1, df2)
funktioniert für diese Beispiele, da R die Frames automatisch mit allgemeinen Variablennamen verbindet, Sie jedoch höchstwahrscheinlich angeben möchten,merge(df1, df2, by = "CustomerId")
um sicherzustellen, dass Sie nur mit den gewünschten Feldern übereinstimmen. Sie können dieParameterby.x
undauch verwendenby.y
, wenn die übereinstimmenden Variablen in den verschiedenen Datenrahmen unterschiedliche Namen haben.Äußere Verbindung:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Links außen:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Rechts außen:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Cross Join:
merge(x = df1, y = df2, by = NULL)
Genau wie beim inneren Join möchten Sie wahrscheinlich "CustomerId" explizit an R als übereinstimmende Variable übergeben.Ich denke, es ist fast immer am besten, die Bezeichner, auf denen Sie zusammenführen möchten, explizit anzugeben. Es ist sicherer, wenn sich die eingegebenen Datenrahmen unerwartet ändern und später leichter zu lesen sind.Sie können mehrere Spalten zusammenführen, indem Sie
by
einen Vektor angeben, zby = c("CustomerId", "OrderId")
.Wenn die zusammenzuführenden Spaltennamen nicht identisch sind, können Sie angeben,
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
woCustomerId_in_df1
sich der Name der Spalte im ersten Datenrahmen undCustomerId_in_df2
der Name der Spalte im zweiten Datenrahmen befindet. (Dies können auch Vektoren sein, wenn Sie mehrere Spalten zusammenführen müssen.)quelle
data.table
Paket ansehen - das ist ein völlig neuer Satz von Join-Syntax, aber es ist radikal schneller als alles, worüber wir hier sprechen.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
jetzt, gleiche Funktion nur schneller.Ich würde empfehlen, das sqldf-Paket von Gabor Grothendieck zu lesen , mit dem Sie diese Operationen in SQL ausdrücken können.
Ich finde die SQL-Syntax einfacher und natürlicher als das R-Äquivalent (dies spiegelt jedoch möglicherweise nur meine RDBMS-Tendenz wider).
Siehe Gabor sqldf GitHub für weitere Informationen über beitritt.
quelle
Es gibt den data.table- Ansatz für einen inneren Join, der sehr zeit- und speichereffizient ist (und für einige größere data.frames erforderlich ist):
merge
funktioniert auch mit data.tables (da es generisch ist und aufruftmerge.data.table
)data.table im Stackoverflow dokumentiert:
Ausführen einer Zusammenführungsoperation von data.table
Übersetzen von SQL- Verknüpfungen auf Fremdschlüsseln in die Syntax von R data.table
Effiziente Alternativen zum Zusammenführen für größere data.frames R
So führen Sie einen einfachen linken äußeren Join mit data.table durch in R?
Eine weitere Option ist die
join
Funktion im Plyr- PaketOptionen für
type
:inner
,left
,right
,full
.Von
?join
: Im Gegensatz dazu behältmerge
[join
] die Reihenfolge von x bei, unabhängig davon, welcher Verknüpfungstyp verwendet wird.quelle
plyr::join
. Microbenchmarking zeigt an, dass es etwa dreimal schneller alsmerge
.data.table
jedoch viel schneller als beide. Es gibt auch große Unterstützung in SO, ich sehe nicht viele Paketschreiber, die hier so oft Fragen beantworten wie derdata.table
Verfasser oder die Mitwirkenden.data.table
Syntax zum Zusammenführen einer Liste von Datenrahmen ?nomatch = 0L
in diesem Fall einfach angeben .Sie können auch Joins mit Hadley Wickhams fantastischem dplyr- Paket durchführen.
Mutierende Verknüpfungen: Fügen Sie df1 Spalten hinzu, indem Sie Übereinstimmungen in df2 verwenden
Verknüpfungen filtern: Zeilen in df1 herausfiltern, Spalten nicht ändern
quelle
CustomerId
in numerische konvertieren ? Ich sehe in der Dokumentation (für beideplyr
unddplyr
) keine Erwähnung dieser Art von Einschränkung. Würde Ihr Code falsch funktionieren, wenn die Zusammenführungsspalte vomcharacter
Typ wäre (besonders interessiert anplyr
)? Vermisse ich etwasEs gibt einige gute Beispiele dafür im R-Wiki . Ich werde hier ein paar stehlen:
Zusammenführungsmethode
Da Ihre Schlüssel den gleichen Namen haben, ist merge () der kurze Weg, um einen inneren Join durchzuführen:
Mit dem Schlüsselwort "all" kann ein vollständiger innerer Join (alle Datensätze aus beiden Tabellen) erstellt werden:
eine linke äußere Verbindung von df1 und df2:
eine rechte äußere Verbindung von df1 und df2:
Sie können sie umdrehen, schlagen und abreiben, um die beiden anderen äußeren Verbindungen zu erhalten, nach denen Sie gefragt haben :)
Indexmethode
Ein linker äußerer Join mit df1 auf der linken Seite unter Verwendung einer tiefgestellten Methode wäre:
Die andere Kombination von äußeren Verknüpfungen kann erstellt werden, indem das Beispiel für den Index der linken äußeren Verknüpfungen gemischt wird. (Ja, ich weiß, das ist das Äquivalent zu "Ich überlasse es dem Leser als Übung ...")
quelle
Neu im Jahr 2014:
Insbesondere wenn Sie sich auch für Datenmanipulationen im Allgemeinen interessieren (einschließlich Sortieren, Filtern, Teilmengen, Zusammenfassen usw.), sollten Sie sich unbedingt die Funktionen ansehen
dplyr
, die eine Vielzahl von Funktionen bieten, die Ihre Arbeit speziell mit Datenrahmen erleichtern sollen und bestimmte andere Datenbanktypen. Es bietet sogar eine ziemlich ausgefeilte SQL-Schnittstelle und sogar eine Funktion, um (den meisten) SQL-Code direkt in R zu konvertieren.Die vier verbindungsbezogenen Funktionen im dplyr-Paket sind (um zu zitieren):
inner_join(x, y, by = NULL, copy = FALSE, ...)
: Gibt alle Zeilen von x zurück, in denen übereinstimmende Werte in y vorhanden sind, und alle Spalten von x und yleft_join(x, y, by = NULL, copy = FALSE, ...)
: Alle Zeilen von x und alle Spalten von x und y zurückgebensemi_join(x, y, by = NULL, copy = FALSE, ...)
: Gibt alle Zeilen von x zurück, in denen es übereinstimmende Werte in y gibt, wobei nur Spalten von x beibehalten werden.anti_join(x, y, by = NULL, copy = FALSE, ...)
: Gibt alle Zeilen von x zurück, in denen es keine übereinstimmenden Werte in y gibt, wobei nur Spalten von x beibehalten werdenEs ist alles hier sehr detailliert.
Die Auswahl der Spalten kann durch erfolgen
select(df,"column")
. Wenn Ihnen dies nicht SQL-ish genug ist, gibt es diesql()
Funktion, in die Sie den SQL-Code unverändert eingeben können. Er führt die von Ihnen angegebene Operation so aus, wie Sie sie die ganze Zeit in R geschrieben haben (weitere Informationen finden Sie unter zur dplyr / datenbanken vignette ). Bei korrekter Anwendung werden beispielsweisesql("SELECT * FROM hflights")
alle Spalten aus der dplyr-Tabelle "hflights" (ein "tbl") ausgewählt.quelle
Aktualisierung der data.table-Methoden zum Verbinden von Datasets. Unten finden Sie Beispiele für jeden Join-Typ. Es gibt zwei Methoden, eine davon,
[.data.table
wenn die zweite data.table als erstes Argument an die Teilmenge übergeben wird. Eine andere Möglichkeit besteht darin, einemerge
Funktion zu verwenden , die an die schnelle data.table-Methode gesendet wird.Unterhalb der Benchmark-Tests basieren Basis R, sqldf, dplyr und data.table.
Benchmark testet nicht verschlüsselte / nicht indizierte Datensätze. Der Benchmark wird für 50M-1-Zeilendatensätze durchgeführt. Es gibt 50M-2 gemeinsame Werte für die Verknüpfungsspalte, sodass jedes Szenario (inner, links, rechts, voll) getestet werden kann und die Verknüpfung immer noch nicht trivial ist. Es ist die Art der Verknüpfung, die die Verknüpfungsalgorithmen gut betont. Timings sind wie die
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Beachten Sie, dass es andere Arten von Verknüpfungen gibt, die Sie ausführen können
data.table
:- Aktualisieren bei Verknüpfung - Wenn Sie Werte von einer anderen Tabelle in Ihre Haupttabelle suchen möchten
- Aggregieren bei Verknüpfung - Wenn Sie bei Schlüssel aggregieren möchten, die Sie beitreten, haben Sie keine Um alle Verknüpfungsergebnisse zu materialisieren
- überlappende Verknüpfung - wenn Sie nach Bereichen zusammenführen möchten
- rollierende Verknüpfung - wenn Sie möchten, dass die Zusammenführung mit Werten aus vorhergehenden / nachfolgenden Zeilen übereinstimmen kann, indem Sie sie vorwärts oder rückwärts rollen
- nicht gleichwertige Verknüpfung - wenn Ihre Die Join-Bedingung ist ungleich
Zu reproduzierender Code:
quelle
on =
werden?on
argmerge.data.table
dem Standardargumentsort = TRUE
ein Schlüssel während der Zusammenführung hinzugefügt wird und dort im Ergebnis verbleibt. Dies ist etwas, auf das Sie achten sollten, insbesondere wenn Sie versuchen, das Setzen von Tasten zu vermeiden.data.table
, was meinst du? Können Sie bitte genauer sein?dplyr hat seit 0.4 alle diese Joins implementiert, einschließlich
outer_join
, aber es war erwähnenswert, dass es in den ersten Releases vor 0.4 nicht angeboten wurdeouter_join
, und infolgedessen gab es eine ganze Weile eine Menge wirklich schlechten Hacky-Workaround-Benutzercodes danach (Sie können solchen Code immer noch in SO finden, Kaggle-Antworten, Github aus dieser Zeit. Daher dient diese Antwort immer noch einem nützlichen Zweck.)Join-bezogene Release-Highlights :
v0.5 (6/2016)
v0.4.0 (1/2015)
v0.3 (10/2014)
v0.2 (5/2014)
v0.1.3 (4/2014)
Problemumgehungen pro Hadley-Kommentar in dieser Ausgabe:
quelle
dplyr
Syntax immer noch mag , hat der Wechsel vonlazyeval
zurlang
Backends eine Menge Code für mich gebrochen, was mich dazu brachte, mehr zu lernendata.table
, und jetzt benutze ich meistensdata.table
.)plyr
/dplyr
/data.table
/ tidyverse stark davon abhängt, in welchem Jahr wir begonnen haben und in welchem (embryonalen) Zustand sich die Pakete damals befanden, im Gegensatz zu heute ...Beim Verbinden von zwei Datenrahmen mit jeweils ~ 1 Million Zeilen, einer mit 2 Spalten und der andere mit ~ 20, habe ich überraschenderweise festgestellt
merge(..., all.x = TRUE, all.y = TRUE)
, dass sie dann schneller sinddplyr::full_join()
. Dies ist mit dplyr v0.4Das Zusammenführen dauert ~ 17 Sekunden, full_join dauert ~ 65 Sekunden.
Etwas zu essen, da ich für Manipulationsaufgaben normalerweise standardmäßig dplyr verwende.
quelle
Für den Fall eines linken Joins mit einer
0..*:0..1
Kardinalität oder eines rechten Joins mit einer0..1:0..*
Kardinalität ist es möglich, die einseitigen Spalten vom Joiner (der0..1
Tabelle) direkt dem Joinee (der0..*
Tabelle) zuzuweisen und dadurch die Erstellung von zu vermeiden eine völlig neue Datentabelle. Dies erfordert das Abgleichen der Schlüsselspalten vom Teilnehmer mit dem Joiner und das Indizieren + Ordnen der Zeilen des Joiners entsprechend für die Zuweisung.Wenn der Schlüssel eine einzelne Spalte ist, können wir einen einzelnen Aufruf verwenden,
match()
um den Abgleich durchzuführen. Dies ist der Fall, den ich in dieser Antwort behandeln werde.Hier ist ein Beispiel, das auf dem OP basiert, außer dass ich eine zusätzliche Zeile
df2
mit der ID 7 hinzugefügt habe , um den Fall eines nicht übereinstimmenden Schlüssels im Joiner zu testen. Dies wird effektivdf1
LEFT JOINdf2
:Oben habe ich eine Annahme fest codiert, dass die Schlüsselspalte die erste Spalte beider Eingabetabellen ist. Ich würde argumentieren, dass dies im Allgemeinen keine unangemessene Annahme ist, da es seltsam wäre, wenn Sie einen data.frame mit einer Schlüsselspalte haben, wenn er nicht als erste Spalte des data.frame von eingerichtet worden wäre der Anfang. Und Sie können die Spalten jederzeit neu anordnen, um dies zu erreichen. Eine vorteilhafte Konsequenz dieser Annahme ist, dass der Name der Schlüsselspalte nicht fest codiert werden muss, obwohl er vermutlich nur eine Annahme durch eine andere ersetzt. Präzision ist ein weiterer Vorteil der Ganzzahlindizierung sowie der Geschwindigkeit. In den folgenden Benchmarks werde ich die Implementierung so ändern, dass die Indizierung von Zeichenfolgennamen verwendet wird, um den konkurrierenden Implementierungen zu entsprechen.
Ich denke, dies ist eine besonders geeignete Lösung, wenn Sie mehrere Tabellen haben, die Sie mit einer einzelnen großen Tabelle verknüpfen möchten. Ein wiederholtes Neuerstellen der gesamten Tabelle für jede Zusammenführung wäre unnötig und ineffizient.
Wenn Sie jedoch möchten, dass der Teilnehmer aus diesem Grund aus irgendeinem Grund unverändert bleibt, kann diese Lösung nicht verwendet werden, da sie den Teilnehmer direkt ändert. In diesem Fall können Sie jedoch einfach eine Kopie erstellen und die Vor-Ort-Zuweisung (en) für die Kopie ausführen.
Als Randnotiz habe ich kurz nach möglichen passenden Lösungen für mehrspaltige Schlüssel gesucht. Leider waren die einzigen passenden Lösungen, die ich gefunden habe:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
oder die gleiche Idee mitpaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
und äquivalente paketbasierte Zusammenführungsfunktionen, die immer eine neue Tabelle zuweisen, um das zusammengeführte Ergebnis zurückzugeben, und daher nicht für eine direkte zuweisungsbasierte Lösung geeignet sind.Siehe beispielsweise Abgleichen mehrerer Spalten in verschiedenen Datenrahmen und Abrufen einer anderen Spalte als Ergebnis , Abgleichen von zwei Spalten mit zwei anderen Spalten , Abgleichen mehrerer Spalten und das Betrügen dieser Frage, bei der ich ursprünglich die direkte Lösung Kombinieren entwickelt habe zwei Datenrahmen mit unterschiedlicher Anzahl von Zeilen in R .
Benchmarking
Ich habe mich für ein eigenes Benchmarking entschieden, um zu sehen, wie sich der In-Place-Zuweisungsansatz im Vergleich zu den anderen in dieser Frage angebotenen Lösungen verhält.
Testcode:
Hier ist ein Benchmark des Beispiels basierend auf dem OP, das ich zuvor demonstriert habe:
Hier vergleiche ich zufällige Eingabedaten und probiere verschiedene Skalen und Muster der Schlüsselüberlappung zwischen den beiden Eingabetabellen aus. Dieser Benchmark ist weiterhin auf den Fall eines einspaltigen Ganzzahlschlüssels beschränkt. Um sicherzustellen, dass die In-Place-Lösung sowohl für linke als auch für rechte Verknüpfungen derselben Tabellen funktioniert, verwenden alle zufälligen Testdaten die
0..1:0..1
Kardinalität. Dies wird implementiert, indem die Schlüsselspalte des ersten Datenrahmens beim Generieren der Schlüsselspalte des zweiten Datenrahmens ohne Ersatz abgetastet wird.Ich habe Code geschrieben, um Log-Log-Diagramme der obigen Ergebnisse zu erstellen. Ich habe für jeden Überlappungsprozentsatz ein separates Diagramm erstellt. Es ist ein bisschen überladen, aber ich mag es, wenn alle Lösungstypen und Verknüpfungstypen in derselben Darstellung dargestellt werden.
Ich habe die Spline-Interpolation verwendet, um eine glatte Kurve für jede Kombination aus Lösung und Verbindungstyp anzuzeigen, die mit einzelnen PCH-Symbolen gezeichnet wurde. Der Verknüpfungstyp wird durch das Symbol pch erfasst, wobei ein Punkt für innere, linke und rechte spitze Klammern für links und rechts und ein Diamant für volle verwendet werden. Der Lösungstyp wird anhand der in der Legende gezeigten Farbe erfasst.
Hier ist ein zweiter groß angelegter Benchmark, der in Bezug auf Anzahl und Art der Schlüsselspalten sowie Kardinalität anspruchsvoller ist. Für diesen Benchmark verwende ich drei Schlüsselspalten: ein Zeichen, eine Ganzzahl und eine logische, ohne Einschränkungen der Kardinalität (dh
0..*:0..*
). (Im Allgemeinen ist es aufgrund von Gleitkomma-Vergleichskomplikationen nicht ratsam, Schlüsselspalten mit doppelten oder komplexen Werten zu definieren, und im Grunde verwendet niemand den Rohtyp, geschweige denn für Schlüsselspalten, daher habe ich diese Typen nicht in den Schlüssel aufgenommen Aus Informationsgründen habe ich zunächst versucht, vier Schlüsselspalten zu verwenden, indem ich eine POSIXct-Schlüsselspalte einfügte, aber der POSIXct-Typ spieltesqldf.indexed
aus irgendeinem Grund nicht gut mit der Lösung, möglicherweise aufgrund von Gleitkomma-Vergleichsanomalien entfernte es.)Die resultierenden Diagramme unter Verwendung des oben angegebenen Diagrammcodes:
quelle
merge
Funktion können wir die Variable der linken oder rechten Tabelle auswählen, so wie wir alle mit der select-Anweisung in SQL vertraut sind (EX: Wählen Sie a. * ... oder Select b. * Von .....).Wir müssen zusätzlichen Code hinzufügen, der aus der neu verknüpften Tabelle Teilmenge wird.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
Gleicher Weg
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
quelle
Für eine innere für alle Spalten verbinden, könnten Sie auch
fintersect
aus dem data.table -package oderintersect
aus dem dplyr -package als Alternative zu ,merge
ohne die Angabeby
-columns. Dadurch erhalten Sie die Zeilen, die zwischen zwei Datenrahmen gleich sind:Beispieldaten:
quelle
Join aktualisieren. Ein weiterer wichtiger Join im SQL-Stil ist ein " Update-Join ", bei dem Spalten in einer Tabelle mithilfe einer anderen Tabelle aktualisiert (oder erstellt) werden.
Ändern der Beispieltabellen des OP ...
Angenommen, wir möchten den Status des Kunden aus
cust
der Einkaufstabelle hinzufügen und dabei die Jahresspaltesales
ignorieren. Mit Basis R können wir übereinstimmende Zeilen identifizieren und dann Werte kopieren über:Wie hier zu sehen ist,
match
wird die erste übereinstimmende Zeile aus der Kundentabelle ausgewählt.Aktualisieren Sie den Join mit mehreren Spalten. Der obige Ansatz funktioniert gut, wenn wir uns nur einer einzigen Spalte anschließen und mit dem ersten Spiel zufrieden sind. Angenommen, wir möchten, dass das Messjahr in der Kundentabelle mit dem Verkaufsjahr übereinstimmt.
Wie @ bgoldst Antwort erwähnt,
match
mitinteraction
könnte eine Option für diesen Fall sein. Einfacher könnte man data.table verwenden:Rolling Update Join. Alternativ möchten wir möglicherweise den letzten Status übernehmen, in dem der Kunde gefunden wurde:
Die drei Beispiele konzentrieren sich vor allem auf das Erstellen / Hinzufügen einer neuen Spalte. In den zugehörigen R-FAQ finden Sie ein Beispiel für das Aktualisieren / Ändern einer vorhandenen Spalte.
quelle