pandas de tres vías uniendo múltiples marcos de datos en columnas

Tengo 3 archivos CSV. Cada uno tiene la primera columna como los nombres (de cadena) de las personas, mientras que todas las demás columnas en cada dataframe son atributos de esa persona.

¿Cómo puedo “unirme” a los tres documentos CSV para crear un solo CSV con cada fila que tiene todos los atributos para cada valor único del nombre de cadena de la persona?

La función join() en pandas especifica que necesito un índice múltiple, pero estoy confundido acerca de lo que tiene que ver un esquema de indexación jerárquica con hacer una unión basada en un solo índice.

Importaciones asumidas:

 import pandas as pd 

La respuesta de John Galt es básicamente una operación de reduce . Si tengo más de un puñado de marcos de datos, los pondría en una lista como esta (generada a través de listas de comprensión o bucles o lo que sea):

 dfs = [df0, df1, df2, dfN] 

Suponiendo que tengan alguna columna común, como el name en su ejemplo, yo haría lo siguiente:

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

De esa manera, su código debería funcionar con cualquier número de marcos de datos que desee combinar.

Editar agosto 1, 2016 : Para aquellos que usan Python 3: reduce se ha movido a functools . Entonces, para usar esta función, primero deberá importar ese módulo:

 from functools import reduce 

Puedes intentar esto si tienes 3 marcos de datos

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

alternativamente, según lo mencionado por cwharland

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

Esta es una situación ideal para el método de join

El método de join se construye exactamente para este tipo de situaciones. Puede unirse a cualquier número de DataFrames junto con él. El DataFrame de llamada se une con el índice de la colección de DataFrames pasados. Para trabajar con múltiples DataFrames, debe colocar las columnas de unión en el índice.

El código se vería algo así:

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

Con los datos de @cero, podrías hacer esto:

 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 

Esto también se puede hacer de la siguiente manera para una lista de marcos de datos df_list :

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

o si los marcos de datos están en un objeto generador (por ejemplo, para reducir el consumo de memoria):

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

En python 3.6.3 con pandas 0.22.0 también puede usar concat durante un tiempo, ya que establece como índice las columnas que desea usar para la unión.

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

donde df1 , df2 y df3 se definen como en la respuesta de 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'] ) 

Uno no necesita un multiindex para realizar operaciones de unión . Solo hay que configurar correctamente la columna de índice en la que se realizarán las operaciones de df.set_index('Name') por ejemplo, el comando df.set_index('Name') )

La operación de join se realiza por defecto en el índice. En su caso, solo tiene que especificar que la columna Name corresponde a su índice. A continuación se muestra un ejemplo.

Un tutorial puede ser útil.

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

Aquí hay un método para combinar un diccionario de marcos de datos mientras se mantienen los nombres de las columnas sincronizados con el diccionario. También rellena los valores perdidos si es necesario:

Esta es la función para fusionar un dict de marcos de datos

 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, vamos a generar datos y probar esto:

 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) 

Hay otra solución de la documentación de los pandas (que no veo aquí),

usando el .append

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

ignore_index=True se usa para ignorar el índice del dataframe adjunto, reemplazándolo con el siguiente índice disponible en el de origen.

Si hay diferentes nombres de columna, se introducirá Nan .