merge, join und concat in Pandas

Pandas ist unzweifelhaft die mächtigste Bibliothek für das Datenmanagement in Python. Natürlich finden wir in ihr auch Funktionen und Methoden, mit denen sich Tabellen erweitern bzw. zusammenfügen lassen. In SQL spricht man bei solchen Operationen von ‚Joins‘. Auch wenn eine ausführliche Online-Dokumentation auf der Pandas-Homepage über die Join-Funktionen existiert, trägt diese nicht gerade dazu bei, sich schnell in die unterschiedlichen Methoden und Funktionen einzufinden. Dies liegt insbesondere daran, dass die offizielle Dokumentation sehr umfassend, viele spezielle Anwendungen aufführt. Mit diesem Artikel möchte ich einige wesentliche Aspekte auf den Punkt bringen und eine kleine Heurisitik bereitstellen, um zwischen den verschiedenen Möglichkeiten, Daten miteinander zu verknüpfen, die richtige auszuwählen.

Inhalt

Anmerkungen zum Artikel:

  • Ein Join wird grundsätzlich getätigt, wenn 2 zu verknüpfende Objekte nicht über eine gemeinsame Sortierung verfügen. Um die Verknüpfung zu erstellen wird ein Mapping benötigt, welches die einzelnen Beobachtungsobjekte der Daten kennzeichnet und einander zuordnet. Liegen die Daten beiden Objekte in identischer Sortierung vor, können die Objekte einfach aneinander gehangen werden. Pandas verwendet für die Verknüpfung von Daten immer einen Mapper – führt also nach der Definition einen tatsächlichen Join durch.
  • Der Übersichtlichkeit Rechnung tragend, werden diesem Artikel lediglich DataFrame Operationen aufgeführt.

Einleitung: merge, join, concat (und append)

Die 3 Implementierungen merge, join und concat (sowie auch der concat-Shortcut append) erlauben es, die nativen Pandas-Datentypen DataFrame und Series in vielfältiger Weise miteinander zu verknüpfen. Einige klassische Operationen des Datenmanagements können sogar auf allen 3 Wegen gelöst werden – diese Schnittmenge gleicher Funktionalität macht Pandas in diesem Anwendungsbereich jedoch unübersichtlich. Schnell stellt sich die Frage: Wann ist welche Funktion zu verwenden?

Wir wollen es in diesem Beitrag einfach halten und stellen folgendes (Daumen-) Regelwerk auf, dass 2 Vorüberlegungen abverlangt. Diese lauten:

  • Sollten die Daten über Indizes oder Spalten verknüpft werden?
  • Sollen 2 oder mehr als 2 Objekte verknüpft werden?

Diese Vorüberledungen führen dann zu einer der Funktionen:

  1. merge (Methode): Wird verwendet, wenn 2 Objekte verknüpft werden und der Join über die Spalten erfolgen soll.
  2. join (Methode): Wird verwendet, wenn 2 Objekte verknüpft werden und der Join über die Indizes erfolgen soll.
  3. concat (Funktion): Wird verwendet, wenn mehr als 2 Objekte verknüpft werden.
  4. append: (Methode): Wie concat da keine Erweiterung der Funktionalität. Dient als Shortcut von concat.

Beispieldaten

Um die Funktionalität für das Joinen von Daten mit den genannten Methoden aufzuzeigen, erstellen wir folgenden Beispieldatensatz:

In [68]:
import pandas as pd
df = pd.DataFrame({'Name' : ["Peter", "Karla", "Anne", "Nino", "Andrzej"],
                   'Geschlecht': ['M','W','W','M','M'],
                   'Alter': [34, 53, 16, 22, 61],
                   'Nationalität': ["deutsch", "schweizerisch", "deutsch", "italienisch", "polnisch"],
                   'Gehalt': [3400, 4000, 0, 3000, 2300]},
                  index = ['ID-123', 'ID-462', 'ID-111', 'ID-997', 'ID-707'])

df
Out[68]:
Name Geschlecht Alter Nationalität Gehalt
ID-123 Peter M 34 deutsch 3400
ID-462 Karla W 53 schweizerisch 4000
ID-111 Anne W 16 deutsch 0
ID-997 Nino M 22 italienisch 3000
ID-707 Andrzej M 61 polnisch 2300

Merge

Merge ist die mächtigste Methode, mit der sich join-Operationen in Pandas umsetzen lassen. Mit ihr können 2 Pandas-Objekte miteinander verknüpft werden – und dies ganz flexibel über ID-Spalten, als auch über die Indizes der Objekte. Dieser Aspekt ist ganz entscheidend. Wir werden noch sehen, dass die Methode join 2 Objekte ausschließlich über den Index miteinander verknüpft. Die join-Methode stellt also nur eine Teilmenge der Funktionalität von merge bereit. Etwas provokativ könnte man daher sogar sagen: Auf join könnte Pandas auch verzichten. Hier die Default-Signatur von merge:

DataFrame.merge(self, right, how=’inner‘, on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=(‚_x‘, ‚_y‘), copy=True, indicator=False, validate=None)

Wenn der Join über Spalten erfolgen soll, werden diese in den Argumenten on bzw. left_on und right_on als Strings übergeben. Erfolgt der Join am Index wird left_index bzw. right_index entsprechend auf True gesetzt.

Erstellen wir uns einen weiteren Datensatz, den wir an die Beispieldaten anfügen. Dieser enthält eine Spalte ID. Nun soll der anschließende Join anhand des Indizes von df und der Spalte ID von wohnort erfolgen.

In [69]:
wohnort = pd.DataFrame({'Wohnort': ['Paderborn', 'Kassel', 'Berlin', 'Aachen', 'Bremen'],
                           'ID': ['ID-462', 'ID-111', 'ID-123', 'ID-997', 'ID-707']})

df.merge(right=wohnort, left_index=True, right_on='ID')
Out[69]:
Name Geschlecht Alter Nationalität Gehalt Wohnort ID
2 Peter M 34 deutsch 3400 Berlin ID-123
0 Karla W 53 schweizerisch 4000 Paderborn ID-462
1 Anne W 16 deutsch 0 Kassel ID-111
3 Nino M 22 italienisch 3000 Aachen ID-997
4 Andrzej M 61 polnisch 2300 Bremen ID-707

Wie wir sehen, verliert die Verknüpfung von df und wohnort den ursprünglichen Index von df. Um die Datenmanagement-Operation des Joins zu beenden, sollten wir die ID-Spalte in einem abschließenden Schritt noch als Index deklariereren.

df.set_index('ID')

Eben diesen Schritt müssen wir explizit nach dem Verknüpfgen gehen, wenn wir merge und nicht join verwenden (dazu mehr weiter unten).

Im Übrigen: Wenn wir den Datensatz df um neue Zeilen erweitern wollen, muss ein Outerjoin durchgeführt werden. Die Spezifikation erfolgt am Argument how:

df_newItems = pd.DataFrame({'Name' : ["Werner", "Kira"],
                   'Geschlecht': ['M','W'],
                   'Alter': [66, 32],
                   'Nationalität': ["deutsch", "deutsch"],
                   'Gehalt': [5300, 3000]},
                  index = ['ID-323', 'ID-499'])

df.merge(df_newItems,how='outer')

Join

Join verknüpft Daten nicht aufgrund von Spalten, sondern aussschließlich am Index. Die nach dem merge vorgenommene Deklaration der Spalte ID als Index, müssen wir – wenn wir join verwenden wollen – nun im Vorfeld erledigen. Wir können festhalten, dass dies der entscheidende Unterschied zwischen merge und join ist.

In [70]:
wohnort_with_id_as_index = wohnort.set_index('ID')

df.join(wohnort_with_id_as_index)
Out[70]:
Name Geschlecht Alter Nationalität Gehalt Wohnort
ID-123 Peter M 34 deutsch 3400 Berlin
ID-462 Karla W 53 schweizerisch 4000 Paderborn
ID-111 Anne W 16 deutsch 0 Kassel
ID-997 Nino M 22 italienisch 3000 Aachen
ID-707 Andrzej M 61 polnisch 2300 Bremen

Concat

Mit concat stellt Pandas eine Funktion bereit, die ebenfalls Objekte miteinander Verknüpft – nur eben als Funktion und nicht als Methode wie merge und join. Da die Funktion mit einem Iterable beliebig viele Objekte entgegennehmen kann, macht ihre Benutzung eben für diesen Fall Sinn: Sie soll eine Vielzahl von Objekten miteinander verketten.

Sehen wir uns die Funktionsweise von concat mit Hilfe von Beispielen an. Wir verknüpfen unten stehend zunächst die 3 Objekte df, wohnort_with_id_as_index und einen weiteren DataFrame beruf. Wie auch join nutzt concat die Indizes für ein Mapping zwischen den Objekten. Wir müssen also vor dem Aufruf von concat sicherstellen, dass die Objekte über einen gemeinsamen Index verfügen.

In [71]:
# Erstellen eines weiteres Objekts
beruf = pd.DataFrame({'Beruf': ['Schreiner','Maler','DataScientist','Makler','Banker']},
                     index = ['ID-123', 'ID-462', 'ID-111', 'ID-997', 'ID-707'])


# Verknüpfen von 3 Objekten mit concat
pd.concat([df,wohnort_with_id_as_index, beruf], axis = 1, sort = False)
Out[71]:
Name Geschlecht Alter Nationalität Gehalt Wohnort Beruf
ID-123 Peter M 34 deutsch 3400 Berlin Schreiner
ID-462 Karla W 53 schweizerisch 4000 Paderborn Maler
ID-111 Anne W 16 deutsch 0 Kassel DataScientist
ID-997 Nino M 22 italienisch 3000 Aachen Makler
ID-707 Andrzej M 61 polnisch 2300 Bremen Banker

Wie wir weiter oben gesehen haben, lassen sich mit merge bzw. join neue Zeilen an einen Datensatz hängen, indem ein Outerjoin durchgeführt wird. In concat erfolgt die Spezifikation für das Anhänger neuer Zeilen über das Argument axis. Wird dieses auf 0 gesetzt wird zeilenweise verknüpft. Der Wert 1 sorgt für Spaltenweises anhängen.

In [72]:
# Erstellen von DataFrame mit weiteren Items/Personen
df_newItems = pd.DataFrame({'Name' : ["Werner", "Kira"],
                   'Geschlecht': ['M','W'],
                   'Alter': [66, 32],
                   'Nationalität': ["deutsch", "deutsch"],
                   'Gehalt': [5300, 3000]},
                  index = ['ID-323', 'ID-499'])


pd.concat([df,df_newItems], axis=0)
Out[72]:
Name Geschlecht Alter Nationalität Gehalt
ID-123 Peter M 34 deutsch 3400
ID-462 Karla W 53 schweizerisch 4000
ID-111 Anne W 16 deutsch 0
ID-997 Nino M 22 italienisch 3000
ID-707 Andrzej M 61 polnisch 2300
ID-323 Werner M 66 deutsch 5300
ID-499 Kira W 32 deutsch 3000

Append

Die Methode append hängt Daten immer zeilenbasiert aneinander. Sie ist als Shortcut von concat zu verstehen und stellt keine Erweiterung zu irgendeiner der oben genannten Methoden/Funktionen dar. Analog zu concat kann auch append beliebig viele Objekte entgegennehmen und sie miteinander verknüpfen.

In [74]:
df_moreItems = pd.DataFrame({'Name' : ["Jonas", "Marlon"],
                   'Geschlecht': ['M','M'],
                   'Alter': [23, 54],
                   'Nationalität': ["deutsch", "Spanisch"],
                   'Gehalt': [1200, 3400]},
                  index = ['ID-311', 'ID-733'])

df.append([df_newItems, df_moreItems])
Out[74]:
Name Geschlecht Alter Nationalität Gehalt
ID-123 Peter M 34 deutsch 3400
ID-462 Karla W 53 schweizerisch 4000
ID-111 Anne W 16 deutsch 0
ID-997 Nino M 22 italienisch 3000
ID-707 Andrzej M 61 polnisch 2300
ID-323 Werner M 66 deutsch 5300
ID-499 Kira W 32 deutsch 3000
ID-311 Jonas M 23 deutsch 1200
ID-733 Marlon M 54 Spanisch 3400

Fazit

Die in Pandas implementierten Methoden und Funktionen um Daten zu verknüpfen, sind in ihrer Mächtigkeit sehr redundant. Im Kern entscheidet die Wahl zwischen merge und join lediglich darüber, zu welchem Zeitpunkt die Indizes der zu verknüpfenden Objekte synchronisiert werden. Die Auswahl der richtigen Funktion aus merge, join, concat und append lässt sich aber mit Hilfe zweier Fragestellungen treffen: Sollen Spalten oder Indizes verwendet werden, um die Objekte zu verknüpfen und: Werden 2 oder mehr als 2 Objekte miteinander verknüpft.