Ich habe zwei DataFrames, die ich basierend auf einer Spalte zusammenführen möchte. Aufgrund alternativer Schreibweisen, unterschiedlicher Anzahl von Leerzeichen, Fehlen / Vorhandensein diakritischer Zeichen möchte ich jedoch in der Lage sein, zusammenzuführen, solange sie einander ähnlich sind.
Jeder Ähnlichkeitsalgorithmus reicht aus (Soundex, Levenshtein, Difflib).
Angenommen, ein DataFrame enthält die folgenden Daten:
df1 = DataFrame([[1],[2],[3],[4],[5]], index=['one','two','three','four','five'], columns=['number'])
number
one 1
two 2
three 3
four 4
five 5
df2 = DataFrame([['a'],['b'],['c'],['d'],['e']], index=['one','too','three','fours','five'], columns=['letter'])
letter
one a
too b
three c
fours d
five e
Dann möchte ich den resultierenden DataFrame erhalten
number letter
one 1 a
two 2 b
three 3 c
four 4 d
five 5 e
Antworten:
Ähnlich wie bei @locojay können Sie den Index von
difflib
'sget_close_matches
aufdf2
' anwenden und dann Folgendes anwendenjoin
:In [23]: import difflib In [24]: difflib.get_close_matches Out[24]: <function difflib.get_close_matches> In [25]: df2.index = df2.index.map(lambda x: difflib.get_close_matches(x, df1.index)[0]) In [26]: df2 Out[26]: letter one a two b three c four d five e In [31]: df1.join(df2) Out[31]: number letter one 1 a two 2 b three 3 c four 4 d five 5 e
.
Wenn dies Spalten wären, könnten Sie auf die gleiche Weise auf die Spalte anwenden
merge
:df1 = DataFrame([[1,'one'],[2,'two'],[3,'three'],[4,'four'],[5,'five']], columns=['number', 'name']) df2 = DataFrame([['a','one'],['b','too'],['c','three'],['d','fours'],['e','five']], columns=['letter', 'name']) df2['name'] = df2['name'].apply(lambda x: difflib.get_close_matches(x, df1['name'])[0]) df1.merge(df2)
quelle
Verwenden von
fuzzywuzzy
Antwort 2019
Da das
fuzzywuzzy
Paket keine Beispiele enthält , habe ich eine Funktion geschrieben, die alle Übereinstimmungen basierend auf einem Schwellenwert zurückgibt, den Sie als Benutzer festlegen können:Beispiel für einen Datenrahmen
df1 = pd.DataFrame({'Key':['Apple', 'Banana', 'Orange', 'Strawberry']}) df2 = pd.DataFrame({'Key':['Aple', 'Mango', 'Orag', 'Straw', 'Bannanna', 'Berry']}) # df1 Key 0 Apple 1 Banana 2 Orange 3 Strawberry # df2 Key 0 Aple 1 Mango 2 Orag 3 Straw 4 Bannanna 5 Berry
Funktion für Fuzzy Matching
def fuzzy_merge(df_1, df_2, key1, key2, threshold=90, limit=2): """ :param df_1: the left table to join :param df_2: the right table to join :param key1: key column of the left table :param key2: key column of the right table :param threshold: how close the matches should be to return a match, based on Levenshtein distance :param limit: the amount of matches that will get returned, these are sorted high to low :return: dataframe with boths keys and matches """ s = df_2[key2].tolist() m = df_1[key1].apply(lambda x: process.extract(x, s, limit=limit)) df_1['matches'] = m m2 = df_1['matches'].apply(lambda x: ', '.join([i[0] for i in x if i[1] >= threshold])) df_1['matches'] = m2 return df_1
Verwenden unserer Funktion für die Datenrahmen: # 1
from fuzzywuzzy import fuzz from fuzzywuzzy import process fuzzy_merge(df1, df2, 'Key', 'Key', threshold=80) Key matches 0 Apple Aple 1 Banana Bannanna 2 Orange Orag 3 Strawberry Straw, Berry
Verwenden unserer Funktion für die Datenrahmen: # 2
df1 = pd.DataFrame({'Col1':['Microsoft', 'Google', 'Amazon', 'IBM']}) df2 = pd.DataFrame({'Col2':['Mcrsoft', 'gogle', 'Amason', 'BIM']}) fuzzy_merge(df1, df2, 'Col1', 'Col2', 80) Col1 matches 0 Microsoft Mcrsoft 1 Google gogle 2 Amazon Amason 3 IBM
Installation:
Pip
Anakonda
quelle
s = df_2.to_dict()[key2]
Ich habe ein Python-Paket geschrieben, das dieses Problem lösen soll:
pip install fuzzymatcher
Das Repo finden Sie hier und die Dokumente hier .
Grundlegende Verwendung:
Bei zwei Datenrahmen
df_left
unddf_right
, die Sie unscharf verbinden möchten, können Sie Folgendes schreiben:from fuzzymatcher import link_table, fuzzy_left_join # Columns to match on from df_left left_on = ["fname", "mname", "lname", "dob"] # Columns to match on from df_right right_on = ["name", "middlename", "surname", "date"] # The link table potentially contains several matches for each record fuzzymatcher.link_table(df_left, df_right, left_on, right_on)
Oder wenn Sie nur auf die nächstgelegene Übereinstimmung verlinken möchten:
quelle
no such module: fts4
name 'fuzzymatcher' is not defined
no such module: fts4
Problem behoben werden kann ? Ich habe versucht, dies ohne Erfolg zu arbeiten.Ich würde Jaro-Winkler verwenden, da es einer der leistungsstärksten und genauesten derzeit verfügbaren Algorithmen für die ungefähre Zeichenfolgenanpassung ist [ Cohen et al. ], [ Winkler ].
So würde ich es mit Jaro-Winkler aus dem Quallenpaket machen :
def get_closest_match(x, list_strings): best_match = None highest_jw = 0 for current_string in list_strings: current_score = jellyfish.jaro_winkler(x, current_string) if(current_score > highest_jw): highest_jw = current_score best_match = current_string return best_match df1 = pandas.DataFrame([[1],[2],[3],[4],[5]], index=['one','two','three','four','five'], columns=['number']) df2 = pandas.DataFrame([['a'],['b'],['c'],['d'],['e']], index=['one','too','three','fours','five'], columns=['letter']) df2.index = df2.index.map(lambda x: get_closest_match(x, df1.index)) df1.join(df2)
Ausgabe:
number letter one 1 a two 2 b three 3 c four 4 d five 5 e
quelle
http://pandas.pydata.org/pandas-docs/dev/merging.html verfügt nicht über eine Hook-Funktion, um dies im laufenden Betrieb zu tun. Wäre aber schön ...
Ich würde nur einen separaten Schritt ausführen und difflib getclosest_matches verwenden, um eine neue Spalte in einem der beiden Datenrahmen und die Zusammenführung / Verknüpfung in der Fuzzy-Matched-Spalte zu erstellen
quelle
difflib.get_closest_matches
eine solche Spalte erstellen und dann zusammenführen?Für einen allgemeinen Ansatz:
fuzzy_merge
Für ein allgemeineres Szenario, in dem Spalten aus zwei Datenrahmen zusammengeführt werden sollen, die leicht unterschiedliche Zeichenfolgen enthalten, wird die folgende Funktion
difflib.get_close_matches
zusammen mitmerge
verwendet, um die Funktionalität von Pandas nachzuahmen,merge
jedoch mit Fuzzy-Matching:import difflib def fuzzy_merge(df1, df2, left_on, right_on, how='inner', cutoff=0.6): df_other= df2.copy() df_other[left_on] = [get_closest_match(x, df1[left_on], cutoff) for x in df_other[right_on]] return df1.merge(df_other, on=left_on, how=how) def get_closest_match(x, other, cutoff): matches = difflib.get_close_matches(x, other, cutoff=cutoff) return matches[0] if matches else None
Hier sind einige Anwendungsfälle mit zwei Beispieldatenrahmen:
print(df1) key number 0 one 1 1 two 2 2 three 3 3 four 4 4 five 5 print(df2) key_close letter 0 three c 1 one a 2 too b 3 fours d 4 a very different string e
Mit dem obigen Beispiel würden wir erhalten:
fuzzy_merge(df1, df2, left_on='key', right_on='key_close') key number key_close letter 0 one 1 one a 1 two 2 too b 2 three 3 three c 3 four 4 fours d
Und wir könnten einen Linken machen mit:
fuzzy_merge(df1, df2, left_on='key', right_on='key_close', how='left') key number key_close letter 0 one 1 one a 1 two 2 too b 2 three 3 three c 3 four 4 fours d 4 five 5 NaN NaN
Für einen linken Join hätten wir alle nicht übereinstimmenden Schlüssel im linken Datenrahmen, um
None
:fuzzy_merge(df1, df2, left_on='key', right_on='key_close', how='right') key number key_close letter 0 one 1.0 one a 1 two 2.0 too b 2 three 3.0 three c 3 four 4.0 fours d 4 None NaN a very different string e
Beachten Sie auch, dass eine leere Liste zurückgegeben wird, wenn innerhalb des Cutoffs kein Element übereinstimmt. Wenn wir im gemeinsamen Beispiel den letzten Index ändern, um zu sagen:
difflib.get_close_matches
df2
Wir würden eine
index out of range
Fehlermeldung erhalten:df2.index.map(lambda x: difflib.get_close_matches(x, df1.index)[0])
Um dies zu lösen, gibt die obige Funktion
get_closest_match
die engste Übereinstimmung zurück, indem sie die zurückgegebene Listedifflib.get_close_matches
nur indiziert, wenn sie tatsächlich Übereinstimmungen enthält.quelle
apply
, es schneller zu machen:df_other[left_on] = df_other[right_on].apply(lambda x: get_closest_match(x, df1[left_on], cutoff))
apply
für mich zu verlangsamen. Vielen Dank!Als Heads-up funktioniert dies grundsätzlich, es sei denn, es wird keine Übereinstimmung gefunden oder Sie haben NaNs in einer der Spalten. Anstatt
get_close_matches
mich direkt zu bewerben , fiel es mir leichter, die folgende Funktion anzuwenden. Die Wahl des NaN-Ersatzes hängt stark von Ihrem Datensatz ab.def fuzzy_match(a, b): left = '1' if pd.isnull(a) else a right = b.fillna('2') out = difflib.get_close_matches(left, right) return out[0] if out else np.NaN
quelle
Ich habe das Fuzzymatcher-Paket verwendet und das hat bei mir gut funktioniert. Besuchen Sie diesen Link für weitere Details.
Verwenden Sie zum Installieren den folgenden Befehl
Unten finden Sie den Beispielcode (bereits oben von RobinL eingereicht).
from fuzzymatcher import link_table, fuzzy_left_join # Columns to match on from df_left left_on = ["fname", "mname", "lname", "dob"] # Columns to match on from df_right right_on = ["name", "middlename", "surname", "date"] # The link table potentially contains several matches for each record fuzzymatcher.link_table(df_left, df_right, left_on, right_on)
Möglicherweise auftretende Fehler
Vorteile:
Nachteile:
quelle
Es gibt ein Paket namens
fuzzy_pandas
dass verwenden kannlevenshtein
,jaro
,metaphone
undbilenco
Methoden. Mit einigen guten Beispielen hierimport pandas as pd import fuzzy_pandas as fpd df1 = pd.DataFrame({'Key':['Apple', 'Banana', 'Orange', 'Strawberry']}) df2 = pd.DataFrame({'Key':['Aple', 'Mango', 'Orag', 'Straw', 'Bannanna', 'Berry']}) results = fpd.fuzzy_merge(df1, df2, left_on='Key', right_on='Key', method='levenshtein', threshold=0.6) results.head()
Key Key 0 Apple Aple 1 Banana Bannanna 2 Orange Orag
quelle
Sie können mit d6tjoin für das
import d6tjoin.top1 d6tjoin.top1.MergeTop1(df1.reset_index(),df2.reset_index(), fuzzy_left_on=['index'],fuzzy_right_on=['index']).merge()['merged']
index number index_right letter 0 one 1 one a 1 two 2 too b 2 three 3 three c 3 four 4 fours d 4 five 5 five e
Es verfügt über eine Vielzahl zusätzlicher Funktionen wie:
Details siehe
quelle
government
mitbusiness
, ist es eine Möglichkeit , den Schwellenwert für den Abgleichswert zu konfigurieren?top_limit
und möchten möglicherweise auch ändern,fun_diff
zufun_diff=[affinegap.affineGapDistance]
welchen tendenziell bessere Übereinstimmungen führen.