Panda a tre vie che unisce più frame di dati su colonne


191

Ho 3 file CSV. Ognuno ha la prima colonna come nome (stringa) di persone, mentre tutte le altre colonne in ciascun frame di dati sono attributi di quella persona.

Come posso "unire" tutti e tre i documenti CSV per creare un singolo CSV con ogni riga con tutti gli attributi per ciascun valore univoco del nome della stringa della persona?

La join()funzione in Panda specifica che ho bisogno di un multiindice, ma sono confuso su ciò che uno schema di indicizzazione gerarchico ha a che fare con un join basato su un singolo indice.


2
Non hai bisogno di un multiindice. Indica nei documenti di join che di voi non ha un multiindice quando si passano più colonne su cui unirsi, quindi lo gestirà.
cwharland,

1
Nelle mie prove, df1.join([df2, df3], on=[df2_col1, df3_col1])non ha funzionato.
montagne russe

Devi metterli insieme come nella risposta fornita. Unisci df1 e df2 quindi unisci il risultato con df3
cwharland

Risposte:


475

Importazioni presunte:

import pandas as pd

La risposta di John Galt è sostanzialmente reduceun'operazione. Se avessi più di una manciata di frame di dati, li metterei in un elenco come questo (generato tramite comprensione dell'elenco o loop o quant'altro):

dfs = [df0, df1, df2, dfN]

Supponendo che abbiano una colonna comune, come namenel tuo esempio, farei quanto segue:

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

In questo modo, il codice dovrebbe funzionare con qualsiasi numero di frame di dati che desideri unire.

Modifica 1 agosto 2016 : per coloro che usano Python 3: reduceè stato spostato in functools. Quindi per usare questa funzione, devi prima importare quel modulo:

from functools import reduce

11
Ho appena provato a usarlo e non è riuscito perché è reducestato sostituito con functools.reduceSoimport functools functools.reduce(.......)
MattR

3
Come funzionerà questa soluzione se i nomi dei campi da unire sono diversi? Per esempio, in tre frame di dati ho potuto avere name1, name2e name3, rispettivamente.
ps0604

2
Questo non significa che abbiamo n-1chiamate alla funzione di unione? Immagino in questo caso dove il numero di frame di dati è piccolo, non importa, ma mi chiedo se esiste una soluzione più scalabile.
eapolinario,

1
Questo non ha funzionato abbastanza per i miei dfcon i multiindici di colonna (stava iniettando 'on' come una colonna che funzionava per la prima unione, ma le successive fusioni fallivano), invece l'ho fatto funzionare con:df = reduce(lambda left, right: left.join(right, how='outer', on='Date'), dfs)
Adrian Torrie

+1 a ps0604. cosa succede se le colonne di join sono diverse, funziona? dovremmo andare con pd.merge in caso le colonne di join siano diverse? grazie
steve

106

Puoi provare questo se hai 3 frame di dati

# 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')

in alternativa, come indicato da Cwharland

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

34
Per un look più pulito puoi incatenarli df1.merge(df2,on='name').merge(df3,on='name')
cwharland,

1
Come funzionerà questa soluzione se i nomi dei campi da unire sono diversi? Ad esempio, in tre frame di dati potrei avere name1, name2e name3rispettivamente
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.

e inoltre, come farlo utilizzando l'indice. Non sembra funzionare se "name" è l'indice e non un nome di colonna.
Brian D,

86

Questa è una situazione ideale per il joinmetodo

Il joinmetodo è costruito esattamente per questo tipo di situazioni. Puoi unire qualsiasi numero di DataFrames insieme ad esso. Il DataFrame chiamante si unisce all'indice della raccolta di DataFrame passati. Per lavorare con più DataFrame, è necessario inserire le colonne di unione nell'indice.

Il codice sarebbe simile al seguente:

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

Con i dati di @ zero, puoi fare questo:

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
Unire tutti i DFS a un dataframe vuoto anche funziona: pd.DataFrame().join(dfs, how="outer"). Questo può essere più pulito in alcune situazioni.
Dominik,

4
Questo è un consiglio decente ed è stato ora incorporato nella fusione dei panda 101 (vedere la sezione sulla fusione di più frame di dati). Vale la pena di notare che se le chiavi aderire sono uniche, utilizzando pd.concatsi tradurrà in sintassi più semplice: pd.concat([df.set_index('name') for df in dfs], axis=1, join='inner').reset_index(). concatè anche più versatile quando si hanno a che fare con nomi di colonne duplicati su più dfs ( joinnon è altrettanto bravo in questo) anche se con esso è possibile eseguire solo join interni o esterni.
cs95,

dfs[0].join(dfs[1:])dovrebbe essere modificato in dfs[0].join(dfs[1:], sort=False) perché altrimenti FutureWarningapparirà un testamento. Grazie per il bell'esempio.
gies0r

Ottengo un errore nel provare che: ValueError: Indexes have overlapping valuessebbene, attraverso l'ispezione dei singoli frame di dati nell'elenco, non sembrano avere valori sovrapposti.
SomJura,

17

Questo può essere fatto anche come segue per un elenco di frame di dati df_list:

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

o se i frame di dati si trovano in un oggetto generatore (ad es. per ridurre il consumo di memoria):

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

11

In python3.6.3 con pandas0.22.0 puoi anche usare concatfintanto che imposti come indice le colonne che vuoi usare per l'unione

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

dove df1, df2e df3sono definiti come nella risposta di John Galt

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
Questa dovrebbe essere la risposta accettata. È il più veloce.
R. Zhu

4

Non è necessario un multiindex per eseguire le operazioni di join . È sufficiente impostare correttamente la colonna dell'indice su cui eseguire le operazioni di join (quale comando df.set_index('Name')ad esempio)

L' joinoperazione viene eseguita per impostazione predefinita sull'indice. Nel tuo caso, devi solo specificare che la Namecolonna corrisponde al tuo indice. Di seguito è riportato un esempio

Un tutorial può essere utile.

# 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

Ecco un metodo per unire un dizionario di frame di dati mantenendo sincronizzati i nomi delle colonne con il dizionario. Inoltre, riempie i valori mancanti, se necessario:

Questa è la funzione per unire una serie di frame di dati

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, consente di generare dati e testarlo:

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

Soluzione semplice:

Se i nomi delle colonne sono simili:

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

Se i nomi delle colonne sono diversi:

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

C'è un'altra soluzione dalla documentazione di Panda (che non vedo qui),

usando il .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

La ignore_index=Trueviene utilizzato per ignorare l'indice del dataframe allegata, sostituendolo con l'indice successivo disponibile in quella sorgente.

Se ci sono nomi di colonne diversi, Nanverrà introdotto.


è semantico, per qualcuno che usa la parola "join" per dire mettendo insieme i due frame di dati. (non necessariamente come operazione di join SQL)
Sylhare

1

I tre frame di dati sono

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Uniamo questi frame usando pd.merge nidificato

inserisci qui la descrizione dell'immagine

Eccoci, abbiamo il nostro dataframe unito.

Buona analisi !!!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.