Pandas Drei-Wege-Verbindung mehrerer Datenrahmen in Spalten


191

Ich habe 3 CSV-Dateien. Jede hat die erste Spalte als (Zeichenfolgen-) Namen von Personen, während alle anderen Spalten in jedem Datenrahmen Attribute dieser Person sind.

Wie kann ich alle drei CSV-Dokumente "zusammenfügen", um eine einzelne CSV zu erstellen, wobei jede Zeile alle Attribute für jeden eindeutigen Wert des Zeichenfolgennamens der Person enthält?

Die join()Funktion in Pandas gibt an, dass ich einen Multiindex benötige, aber ich bin verwirrt darüber, was ein hierarchisches Indexierungsschema mit dem Erstellen eines Joins auf der Grundlage eines einzelnen Index zu tun hat.


2
Sie benötigen keinen Multiindex. In den Join-Dokumenten heißt es, dass Sie keinen Multiindex haben, wenn Sie mehrere Spalten zum Join übergeben, dann wird dies erledigt.
cwharland

1
In meinen Versuchen hat df1.join([df2, df3], on=[df2_col1, df3_col1])es nicht funktioniert.
Lollercoaster

Sie müssen sie wie in der angegebenen Antwort miteinander verketten. Füge df1 und df2 zusammen und füge dann das Ergebnis mit df3
cwharland

Antworten:


472

Angenommene Importe:

import pandas as pd

John Galts Antwort ist im Grunde eine reduceOperation. Wenn ich mehr als eine Handvoll Datenrahmen habe, würde ich sie in eine Liste wie diese einfügen (generiert über Listenverständnisse oder Schleifen oder so weiter):

dfs = [df0, df1, df2, dfN]

Angenommen, sie haben eine gemeinsame Spalte, wie namein Ihrem Beispiel, würde ich Folgendes tun:

df_final = reduce(lambda left,right: pd.merge(left,right,on='name'), dfs)

Auf diese Weise sollte Ihr Code mit einer beliebigen Anzahl von Datenrahmen funktionieren, die Sie zusammenführen möchten.

Bearbeiten 1. August 2016 : Für Benutzer von Python 3: reducewurde verschoben functools. Um diese Funktion nutzen zu können, müssen Sie zuerst das folgende Modul importieren:

from functools import reduce

11
Ich habe es gerade versucht und es ist fehlgeschlagen, weil reducees durch functools.reduceSoimport functools functools.reduce(.......)
MattR

3
Wie funktioniert diese Lösung, wenn die Namen der zu verbindenden Felder unterschiedlich sind? Zum Beispiel in drei Datenrahmen könnte ich name1, name2und name3jeweils.
ps0604

2
Bedeutet das nicht, dass wir n-1die Merge-Funktion aufrufen? Ich denke, in diesem Fall, in dem die Anzahl der Datenrahmen gering ist, spielt es keine Rolle, aber ich frage mich, ob es eine skalierbarere Lösung gibt.
eapolinario

1
Dies funktionierte bei meinen dfs mit Spalten-Multi-Indizes nicht ganz (es wurde das 'on' als Spalte eingefügt, die für die erste Zusammenführung funktionierte, aber nachfolgende Zusammenführungen schlugen fehl), stattdessen musste ich damit arbeiten:df = reduce(lambda left, right: left.join(right, how='outer', on='Date'), dfs)
Adrian Torrie

+1 bis ps0604. Was funktioniert, wenn die Verknüpfungsspalten unterschiedlich sind? sollten wir mit pd.merge gehen, falls die Join-Spalten unterschiedlich sind? danke
steve

106

Sie können dies versuchen, wenn Sie 3 Datenrahmen haben

# Merge multiple dataframes
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

pd.merge(pd.merge(df1,df2,on='name'),df3,on='name')

alternativ, wie von cwharland erwähnt

df1.merge(df2,on='name').merge(df3,on='name')

34
Für sauberere Looks können Sie sie df1.merge(df2,on='name').merge(df3,on='name')
verketten

1
Wie funktioniert diese Lösung, wenn die Namen der zu verbindenden Felder unterschiedlich sind? Zum Beispiel in drei Datenrahmen könnte ich name1, name2und name3jeweils
ps0604

4
@ ps0604df1.merge(df2,left_on='name1', right_on='name2').merge(df3,left_on='name1', right_on='name3').drop(columns=['name2', 'name3']).rename(columns={'name1':'name'})
Michael H.

und weiter, wie man das mit dem Index macht. Scheint nicht zu funktionieren, wenn 'name' der Index und kein Spaltenname ist.
Brian D

85

Dies ist eine ideale Situation für die joinMethode

Die joinMethode ist genau für diese Art von Situationen ausgelegt. Sie können damit beliebig viele DataFrames verbinden. Der aufrufende DataFrame wird mit dem Index der Sammlung übergebener DataFrames verknüpft. Um mit mehreren DataFrames arbeiten zu können, müssen Sie die Verknüpfungsspalten in den Index einfügen.

Der Code würde ungefähr so ​​aussehen:

filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])

Mit den Daten von @ zero können Sie Folgendes tun:

df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
dfs[0].join(dfs[1:])

     attr11 attr12 attr21 attr22 attr31 attr32
name                                          
a         5      9      5     19     15     49
b         4     61     14     16      4     36
c        24      9      4      9     14      9

4
Das Verbinden aller dfs mit einem leeren Datenrahmen funktioniert auch : pd.DataFrame().join(dfs, how="outer"). Dies kann in einigen Situationen sauberer sein.
Dominik

4
Dies ist ein anständiger Ratschlag und wurde nun in die Zusammenführung von Pandas 101 integriert (siehe Abschnitt zum Zusammenführen mehrerer Datenrahmen). Wenn Ihre Join-Schlüssel eindeutig sind, führt die Verwendung pd.concatzu einer einfacheren Syntax : pd.concat([df.set_index('name') for df in dfs], axis=1, join='inner').reset_index(). concatist auch vielseitiger, wenn Sie mit doppelten Spaltennamen über mehrere dfs hinweg arbeiten (dies joinist nicht so gut), obwohl Sie nur innere oder äußere Verknüpfungen damit durchführen können.
CS95

dfs[0].join(dfs[1:])sollte bearbeitet werden, dfs[0].join(dfs[1:], sort=False) da sonst ein FutureWarningWille auftaucht. Danke für das schöne Beispiel.
gies0r

Ich erhalte eine Fehlermeldung ValueError: Indexes have overlapping values, wenn ich das versuche: Obwohl die einzelnen Datenrahmen in der Liste überprüft werden, scheinen sie keine überlappenden Werte zu haben.
SomJura

17

Dies kann für eine Liste von Datenrahmen auch wie folgt erfolgen df_list:

df = df_list[0]
for df_ in df_list[1:]:
    df = df.merge(df_, on='join_col_name')

oder wenn sich die Datenrahmen in einem Generatorobjekt befinden (z. B. um den Speicherverbrauch zu reduzieren):

df = next(df_list)
for df_ in df_list:
    df = df.merge(df_, on='join_col_name')

11

In python3.6.3 mit pandas0.22.0 können Sie auch concatdie Spalten verwenden, die Sie für die Verknüpfung verwenden möchten, solange Sie als Index festlegen

pd.concat(
    (iDF.set_index('name') for iDF in [df1, df2, df3]),
    axis=1, join='inner'
).reset_index()

wo df1, df2und df3sind wie in John Galts Antwort definiert

import pandas as pd
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12']
)
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22']
)
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32']
)

2
Dies sollte die akzeptierte Antwort sein. Es ist das schnellste.
R. Zhu

4

Man benötigt keinen Multiindex, um Join- Operationen durchzuführen. Man muss nur die Indexspalte richtig einstellen, für die die Verknüpfungsoperationen ausgeführt werden sollen (welcher Befehl df.set_index('Name')zum Beispiel).

Die joinOperation wird standardmäßig für den Index ausgeführt. In Ihrem Fall müssen Sie nur angeben, dass die NameSpalte Ihrem Index entspricht. Unten ist ein Beispiel

Ein Tutorial kann nützlich sein.

# Simple example where dataframes index are the name on which to perform the join operations
import pandas as pd
import numpy as np
name = ['Sophia' ,'Emma' ,'Isabella' ,'Olivia' ,'Ava' ,'Emily' ,'Abigail' ,'Mia']
df1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=name)
df2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'],         index=name)
df3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'],     index=name)
df = df1.join(df2)
df = df.join(df3)

# If you a 'Name' column that is not the index of your dataframe, one can set this column to be the index
# 1) Create a column 'Name' based on the previous index
df1['Name']=df1.index
# 1) Select the index from column 'Name'
df1=df1.set_index('Name')

# If indexes are different, one may have to play with parameter how
gf1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=range(8))
gf2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'], index=range(2,10))
gf3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'], index=range(4,12))

gf = gf1.join(gf2, how='outer')
gf = gf.join(gf3, how='outer')

4

Hier ist eine Methode zum Zusammenführen eines Wörterbuchs mit Datenrahmen, während die Spaltennamen mit dem Wörterbuch synchron bleiben. Außerdem werden bei Bedarf fehlende Werte ausgefüllt:

Dies ist die Funktion zum Zusammenführen eines Diktats von Datenrahmen

def MergeDfDict(dfDict, onCols, how='outer', naFill=None):
  keys = dfDict.keys()
  for i in range(len(keys)):
    key = keys[i]
    df0 = dfDict[key]
    cols = list(df0.columns)
    valueCols = list(filter(lambda x: x not in (onCols), cols))
    df0 = df0[onCols + valueCols]
    df0.columns = onCols + [(s + '_' + key) for s in valueCols] 

    if (i == 0):
      outDf = df0
    else:
      outDf = pd.merge(outDf, df0, how=how, on=onCols)   

  if (naFill != None):
    outDf = outDf.fillna(naFill)

  return(outDf)

OK, können Daten generiert und getestet werden:

def GenDf(size):
  df = pd.DataFrame({'categ1':np.random.choice(a=['a', 'b', 'c', 'd', 'e'], size=size, replace=True),
                      'categ2':np.random.choice(a=['A', 'B'], size=size, replace=True), 
                      'col1':np.random.uniform(low=0.0, high=100.0, size=size), 
                      'col2':np.random.uniform(low=0.0, high=100.0, size=size)
                      })
  df = df.sort_values(['categ2', 'categ1', 'col1', 'col2'])
  return(df)


size = 5
dfDict = {'US':GenDf(size), 'IN':GenDf(size), 'GER':GenDf(size)}   
MergeDfDict(dfDict=dfDict, onCols=['categ1', 'categ2'], how='outer', naFill=0)

3

Einfache Lösung:

Wenn die Spaltennamen ähnlich sind:

 df1.merge(df2,on='col_name').merge(df3,on='col_name')

Wenn die Spaltennamen unterschiedlich sind:

df1.merge(df2,left_on='col_name1', right_on='col_name2').merge(df3,left_on='col_name1', right_on='col_name3').drop(columns=['col_name2', 'col_name3']).rename(columns={'col_name1':'col_name'})

2

Es gibt eine andere Lösung aus der Pandas-Dokumentation (die ich hier nicht sehe):

Verwendung der .append

>>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
   A  B
0  1  2
1  3  4
>>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
   A  B
0  5  6
1  7  8
>>> df.append(df2, ignore_index=True)
   A  B
0  1  2
1  3  4
2  5  6
3  7  8

Mit ignore_index=Truewird der Index des angehängten Datenrahmens ignoriert und durch den nächsten im Quellindex verfügbaren Index ersetzt.

Wenn es unterschiedliche Spaltennamen gibt, Nanwird eingeführt.


Es ist semantisch, wenn jemand das Wort "Join" verwendet, um zu sagen, dass er die beiden Datenrahmen zusammenfügt. (nicht unbedingt als SQL-Join-Operation)
Sylhare

1

Die drei Datenrahmen sind

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Lassen Sie uns diese Frames mit verschachteltem pd.merge zusammenführen

Geben Sie hier die Bildbeschreibung ein

Los geht's, wir haben unseren zusammengeführten Datenrahmen.

Glückliche Analyse !!!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.