Pandas Left Outer Join führt zu einer Tabelle, die größer als die linke Tabelle ist

78

Nach meinem Verständnis eines linken äußeren Joins sollte die resultierende Tabelle niemals mehr Zeilen als die linke Tabelle enthalten ... Bitte lassen Sie mich wissen, wenn dies falsch ist ...

Meine linke Tabelle besteht aus 192572 Zeilen und 8 Spalten.

Meine rechte Tabelle besteht aus 42160 Zeilen und 5 Spalten.

Meine linke Tabelle enthält ein Feld mit dem Namen "id", das mit einer Spalte in meiner rechten Tabelle mit dem Namen "key" übereinstimmt.

Deshalb füge ich sie als solche zusammen:

combined = pd.merge(a,b,how='left',left_on='id',right_on='key')

Aber dann ist die kombinierte Form 236569.

Was missverstehe ich?

Terence Chow
quelle
Können Sie einige minimale Daten posten, die dies demonstrieren (nicht alle 200k bitte)?
Paul H
@PaulH das Problem ist, dass ich den Grund dafür nicht finden kann ... wenn ich dies pd.mergefür einen kleinen Abschnitt des Codes verwende, hat die resultierende Tabelle tatsächlich nur die Größe der linken Tabelle
Terence Chow

Antworten:

117

Sie können davon ausgehen, dass sich dies erhöht, wenn Schlüssel mit mehr als einer Zeile im anderen DataFrame übereinstimmen:

In [11]: df = pd.DataFrame([[1, 3], [2, 4]], columns=['A', 'B'])

In [12]: df2 = pd.DataFrame([[1, 5], [1, 6]], columns=['A', 'C'])

In [13]: df.merge(df2, how='left')  # merges on columns A
Out[13]: 
   A  B   C
0  1  3   5
1  1  3   6
2  2  4 NaN

Um dieses Verhalten zu vermeiden, löschen Sie die Duplikate in df2:

In [21]: df2.drop_duplicates(subset=['A'])  # you can use take_last=True
Out[21]: 
   A  C
0  1  5

In [22]: df.merge(df2.drop_duplicates(subset=['A']), how='left')
Out[22]: 
   A  B   C
0  1  3   5
1  2  4 NaN
Andy Hayden
quelle
Gibt es eine Möglichkeit, das zu unterdrücken? In Ihrem Beispiel muss ich nicht Zeile 0 oder 1 sehen, nur um eine der 2 Zeilen zu sehen ...
Terence Chow
1
@Chowza yup, lassen Sie die Duplikate fallen, bearbeitete Antwort, um das zu reflektieren.
Andy Hayden
1
Nur zu Ihrer Information, cols ist jetzt veraltet. Verwenden Sie stattdessen 'subset': df.merge (df2.drop_duplicates (subset = ['A']), how = 'left')
SummerEla
3
@ SummerEla Danke! Ich sollte wirklich alle meine Fragen durchgehen und Verwerfungen korrigieren (sollte in der Lage sein, es zu schreiben) ...
Andy Hayden
3
Ich habe gerade festgestellt, dass Sie, wenn Sie dies für einen inneren Join anstatt für einen linken tun , die Duplikate in beiden Datenrahmen gemäß dieser Antwort entfernen müssen .
Kardamom
7

Es gibt auch Strategien, mit denen Sie dieses Verhalten vermeiden können, bei denen die duplizierten Daten nicht verloren gehen, wenn beispielsweise nicht alle Spalten dupliziert werden. Wenn Sie haben

In [1]: df = pd.DataFrame([[1, 3], [2, 4]], columns=['A', 'B'])

In [2]: df2 = pd.DataFrame([[1, 5], [1, 6]], columns=['A', 'C'])

Eine Möglichkeit wäre, den Mittelwert des Duplikats zu nehmen (kann auch die Summe usw. nehmen).

In [3]: df3 = df2.groupby('A').mean().reset_index()

In [4]: df3
Out[4]: 
     C
A     
1  5.5

In [5]: merged = pd.merge(df,df3,on=['A'], how='outer')

In [6]: merged
Out[204]: 
   A  B    C
0  1  3  5.5
1  2  4  NaN

Wenn Sie nicht numerische Daten haben, die nicht mit pd.to_numeric () konvertiert werden können, oder wenn Sie einfach nicht den Mittelwert nehmen möchten, können Sie die Zusammenführungsvariable ändern, indem Sie die Duplikate auflisten. Diese Strategie würde jedoch angewendet, wenn die Duplikate in beiden Datensätzen vorhanden sind (was das gleiche problematische Verhalten verursachen würde und auch ein häufiges Problem darstellt):

In [7]: df = pd.DataFrame([['a', 3], ['b', 4],['b',0]], columns=['A', 'B'])

In [8]: df2 = pd.DataFrame([['a', 3], ['b', 8],['b',5]], columns=['A', 'C'])

In [9]: df['count'] = df.groupby('A')['B'].cumcount()

In [10]: df['A'] = np.where(df['count']>0,df['A']+df['count'].astype(str),df['A'].astype(str))

In[11]: df
Out[11]: 
    A  B  count
0   a  3      0
1   b  4      0
2  b1  0      1

Machen Sie dasselbe für df2, löschen Sie die Zählvariablen in df und df2 und führen Sie sie auf 'A' zusammen:

In [16]: merged
Out[16]: 
    A  B  C
0   a  3  3        
1   b  4  8        
2  b1  0  5        

Ein paar Notizen. In diesem letzten Fall verwende ich .cumcount () anstelle von .duplicated, da es sein kann, dass Sie für eine bestimmte Beobachtung mehr als ein Duplikat haben. Außerdem verwende ich .astype (str), um die Zählwerte in Zeichenfolgen zu konvertieren, da ich den Befehl np.where () verwende, aber die Verwendung von pd.concat () oder etwas anderem kann unterschiedliche Anwendungen ermöglichen.

Wenn nur ein Datensatz die Duplikate enthält, Sie diese aber dennoch behalten möchten, können Sie die erste Hälfte der letzteren Strategie verwenden, um die Duplikate in der resultierenden Zusammenführung zu unterscheiden.

seeiespi
quelle
4

Eine kleine Ergänzung zu den gegebenen Antworten ist, dass es einen Parameter namens validate gibt, der verwendet werden kann, um einen Fehler auszulösen, wenn doppelte IDs in der rechten Tabelle übereinstimmen:

combined = pd.merge(a,b,how='left',left_on='id',right_on='key', validate = 'm:1')
Tobias Dekker
quelle