Ich habe zwei data.frame
s mit mehreren gemeinsamen Spalten (hier: date
, city
, ctry
, und ( other_
) number
).
Ich möchte sie jetzt in den obigen Spalten zusammenführen, aber einen gewissen Unterschied tolerieren:
threshold.numbers <- 3
threshold.date <- 5 # in days
Wenn der Unterschied zwischen den date
Einträgen > threshold.date
(in Tagen) oder ist > threshold.numbers
, möchte ich nicht, dass die Zeilen zusammengeführt werden. Wenn der Eintrag in city
eine Teilzeichenfolge des Eintrags des anderen df
in der city
Spalte ist, möchte ich, dass die Zeilen zusammengeführt werden. [Wenn jemand eine bessere Idee zu Test für die tatsächlichen Stadtnamen hat Ähnlichkeit, würde ich gerne davon hören.] (Und halten Sie die ersten df
‚s Einträge date
, city
und country
doch beide ( other_
) number
Spalten und alle andere Spalten in der df
.
Betrachten Sie das folgende Beispiel:
df1 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17",
"1999-06-30", "1999-03-16", "1999-07-16", "2001-08-29", "2002-07-30"),
city = c("Berlin", "Paris", "London", "Rome", "Bern",
"Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"),
ctry = c("Germany", "France", "UK", "Italy", "Switzerland",
"Denmark", "Poland", "Russia", "Tunisia", "Austria"),
number = c(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
col = c("apple", "banana", "pear", "banana", "lemon", "cucumber", "apple", "peach", "cherry", "cherry"))
df2 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17", # all identical to df1
"1999-06-29", "1999-03-14", "1999-07-17", # all 1-2 days different
"2000-01-29", "2002-07-01"), # all very different (> 2 weeks)
city = c("Berlin", "East-Paris", "near London", "Rome", # same or slight differences
"Zurich", # completely different
"Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"), # same
ctry = c("Germany", "France", "UK", "Italy", "Switzerland", # all the same
"Denmark", "Poland", "Russia", "Tunisia", "Austria"),
other_number = c(13, 17, 3100, 45, 51, 61, 780, 85, 90, 101), # slightly different to very different
other_col = c("yellow", "green", "blue", "red", "purple", "orange", "blue", "red", "black", "beige"))
Jetzt möchte ich die zusammenführen data.frames
und erhalten, df
wo Zeilen zusammengeführt werden, wenn die oben genannten Bedingungen erfüllt sind.
(Die erste Spalte dient nur der Übersichtlichkeit: Hinter der ersten Ziffer, die den ursprünglichen Fall angibt, wird angezeigt, ob die Zeilen zusammengeführt wurden ( .
) oder ob die Zeilen von df1
( 1
) oder df2
( 2
) stammen.
date city ctry number other_col other_number other_col2 #comment
1. 2003-08-29 Berlin Germany 10 apple 13 yellow # matched on date, city, number
2. 1999-06-12 Paris France 20 banana 17 green # matched on date, city similar, number - other_number == threshold.numbers
31 2000-08-29 London UK 30 pear <NA> <NA> # not matched: number - other_number > threshold.numbers
32 2000-08-29 near London UK <NA> <NA> 3100 blue #
41 1999-02-24 Rome Italy 40 banana <NA> <NA> # not matched: number - other_number > threshold.numbers
42 1999-02-24 Rome Italy <NA> <NA> 45 red #
51 2001-04-17 Bern Switzerland 50 lemon <NA> <NA> # not matched: cities different (dates okay, numbers okay)
52 2001-04-17 Zurich Switzerland <NA> <NA> 51 purple #
6. 1999-06-30 Copenhagen Denmark 60 cucumber 61 orange # matched: date difference < threshold.date (cities okay, dates okay)
71 1999-03-16 Warsaw Poland 70 apple <NA> <NA> # not matched: number - other_number > threshold.numbers (dates okay)
72 1999-03-14 Warsaw Poland <NA> <NA> 780 blue #
81 1999-07-16 Moscow Russia 80 peach <NA> <NA> # not matched: number - other_number > threshold.numbers (dates okay)
82 1999-07-17 Moscow Russia <NA> <NA> 85 red #
91 2001-08-29 Tunis Tunisia 90 cherry <NA> <NA> # not matched: date difference < threshold.date (cities okay, dates okay)
92 2000-01-29 Tunis Tunisia <NA> <NA> 90 black #
101 2002-07-30 Vienna Austria 100 cherry <NA> <NA> # not matched: date difference < threshold.date (cities okay, dates okay)
102 2002-07-01 Vienna Austria <NA> <NA> 101 beige #
Ich habe verschiedene Implementierungen zum Zusammenführen versucht, kann aber den Schwellenwert nicht implementieren.
EDIT Entschuldigung für unklare Formulierung - Ich möchte alle Zeilen behalten und einen Indikator erhalten, ob die Zeile übereinstimmt, nicht übereinstimmt und von df1 oder nicht übereinstimmt und von df2.
Der Pseudocode lautet:
if there is a case where abs("date_df2" - "date_df1") <= threshold.date:
if "ctry_df2" == "ctry_df1":
if "city_df2" ~ "city_df1":
if abs("number_df2" - "number_df1") <= threshold.numbers:
merge and go to next row in df2
else:
add row to df1```
.
?Antworten:
Hier ist eine Lösung, die mein Paket safejoin verwendet und in diesem Fall das Paket fuzzyjoin umschließt .
Wir können das
by
Argument verwenden, um eine komplexe Bedingung anzugeben, indem wir die Funktion verwendenX()
, um den Wert vondf1
undY()
den Wert von zu erhaltendf2
.Wenn Ihre echten Tische groß sind, kann dies langsam oder unmöglich sein, da es sich um ein kartesisches Produkt handelt, aber hier funktioniert es gut.
Was wir wollen, ist eine vollständige Verknüpfung (behalten Sie alle Zeilen bei und verbinden Sie, was verbunden werden kann), und wir möchten den ersten Wert behalten, wenn sie verbunden werden, und den nächsten anderen Wert. Dies bedeutet, dass wir uns mit dem Konflikt von befassen möchten Spalten, die durch Zusammenführen identisch benannt wurden, verwenden wir daher das Argument
conflict = dplyr::coalesce
Ausgabe :
Erstellt am 2019-11-13 vom reprex-Paket (v0.3.0)
Leider fuzzyjoin alle Spalten in einer Matrix nötigt , wenn ein Multi tun beizutreten, und safejoin Wraps fuzzyjoin , so dass wir die Variablen in den entsprechenden Typ innerhalb der durch das Argument konvertieren muss, erklärt dies die ersten Zeilen in der
by
Argument.Weitere Informationen zu safejoin : https://github.com/moodymudskipper/safejoin
quelle
Ich habe zuerst die Städtenamen in Zeichenvektoren umgewandelt, da Sie (wenn ich das richtig verstanden habe) Stadtnamen einschließen möchten, die in df2 enthalten sind.
Dann führen Sie sie nach Ländern zusammen:
In der Bibliothek
stringr
können Sie sehen, ob sich city.x in city.y befindet (siehe letzte Spalte):Dann können Sie die Differenz in Tagen zwischen Daten erhalten:
und der Unterschied in Zahlen:
So sieht der resultierende Datenrahmen aus:
Wir möchten jedoch Dinge löschen, bei denen city.x nicht in city.y gefunden wurde, bei denen der Tagesunterschied größer als 5 oder der Zahlenunterschied größer als 3 ist:
Was bleibt, sind die drei Zeilen, die Sie oben hatten (die Punkte in Spalte 1 enthielten).
Jetzt können wir die drei von uns erstellten Spalten sowie das Datum und die Stadt aus df2 löschen:
quelle
Schritt 1: Führen Sie die Daten basierend auf "Stadt" und "Land" zusammen:
Schritt 2: Entfernen Sie Zeilen, wenn der Unterschied zwischen den Datumseinträgen> Schwellwert.Datum (in Tagen) ist:
Schritt 3: Entfernen Sie Zeilen, wenn der Unterschied zwischen den Zahlen> Schwellwert.Nummer ist:
Die Daten sollten vor dem Anwenden von Bedingungen zusammengeführt werden, falls die Zeilen nicht übereinstimmen.
quelle
Eine Option mit
data.table
(Erklärungen inline):Ausgabe:
quelle
Sie können das
city
Match mitgrepl
undctry
einfach mit testen==
. Für diejenigen , die bis hier passen können Sie das Datum Differenz durch Umwandlung berechnendate
verwendenas.Date
und einen Vergleich mit eindifftime
. Dernumber
Unterschied wird auf die gleiche Weise gemacht.quelle
Hier ist ein flexibler Ansatz, mit dem Sie eine beliebige Sammlung von Zusammenführungskriterien angeben können, die Sie auswählen.
Vorbereitungsarbeit
Ich habe dafür gesorgt, dass alle Zeichenfolgen in
df1
unddf2
Zeichenfolgen waren, keine Faktoren (wie in einigen der anderen Antworten angegeben). Ich habe auch die Daten eingewickeltas.Date
, um sie zu echten Daten zu machen.Geben Sie die Zusammenführungskriterien an
Erstellen Sie eine Liste mit Listen. Jedes Element der Hauptliste ist ein Kriterium; Die Mitglieder eines Kriteriums sind
final.col.name
: Der Name der Spalte, die wir in der Final Table haben möchtencol.name.1
: der Name der Spalte indf1
col.name.2
: der Name der Spalte indf2
exact
: boolean; sollten wir diese Spalte genau abgleichen?threshold
: Schwelle (wenn wir nicht genau übereinstimmen)match.function
: Eine Funktion, die zurückgibt, ob die Zeilen übereinstimmen oder nicht (für spezielle Fälle wie die Verwendunggrepl
für die Zeichenfolgenübereinstimmung; beachten Sie, dass diese Funktion vektorisiert werden muss ).Funktion zum Zusammenführen
Diese Funktion verwendet drei Argumente: die beiden Datenrahmen, die zusammengeführt werden sollen, und die Liste der Übereinstimmungskriterien. Es geht wie folgt vor:
Wenden Sie die Funktion an, und wir sind fertig
quelle