J'ai 3 fichiers CSV. Chacune a la première colonne en tant que (chaîne) noms de personnes, tandis que toutes les autres colonnes de chaque cadre de données sont des attributs de cette personne.
Comment puis-je "joindre" les trois documents CSV pour créer un seul fichier CSV, chaque ligne ayant tous les attributs pour chaque valeur unique du nom de chaîne de la personne?
La fonction join()
dans pandas indique que j'ai besoin d'un multi-index, mais je ne comprends pas ce qu'un schéma d'indexation hiérarchique doit faire pour créer une jointure basée sur un seul index.
Importations présumées:
import pandas as pd
La réponse de John Galt est fondamentalement une opération reduce
. Si j'ai plus d'une poignée de trames de données, je les mettrais dans une liste comme celle-ci (générée via des compréhensions de liste, des boucles ou autres):
dfs = [df0, df1, df2, dfN]
En supposant qu'ils aient une colonne commune, comme name
dans votre exemple, je procéderais comme suit:
df_final = reduce(lambda left,right: pd.merge(left,right,on='name'), dfs)
De cette façon, votre code doit fonctionner avec le nombre de trames de données que vous souhaitez fusionner.
Edit August 1, 2016 : Pour ceux qui utilisent Python 3: reduce
a été déplacé dans functools
. Donc, pour utiliser cette fonction, vous devez d'abord importer ce module:
from functools import reduce
Vous pouvez essayer ceci si vous avez 3 cadres de données
# 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')
alternativement, comme mentionné par cwharland
df1.merge(df2,on='name').merge(df3,on='name')
join
La méthode join
est construite exactement pour ce type de situation. Vous pouvez joindre autant de DataFrames que vous le souhaitez. Le DataFrame appelant se joint à l'index de la collection de DataFrames transmis. Pour utiliser plusieurs DataFrames, vous devez placer les colonnes de jointure dans l'index.
Le code ressemblerait à ceci:
filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])
Avec les données de @ zero, vous pouvez faire ceci:
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
Cela peut également être fait comme suit pour une liste de trames de données df_list
:
df = df_list[0]
for df_ in df_list[1:]:
df = df.merge(df_, on='join_col_name')
ou si les trames de données se trouvent dans un objet générateur (par exemple, pour réduire la consommation de mémoire):
df = next(df_list)
for df_ in df_list:
df = df.merge(df_, on='join_col_name')
Dans python
3.6.3 avec pandas
0.22.0, vous pouvez également utiliser concat
tant que vous définissez comme index les colonnes que vous souhaitez utiliser pour la jointure.
pd.concat(
(iDF.set_index('name') for iDF in [df1, df2, df3]),
axis=1, join='inner'
).reset_index()
où df1
, df2
et df3
sont définis comme suit réponse 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']
)
Voici une méthode pour fusionner un dictionnaire de trames de données tout en maintenant les noms des colonnes en synchronisation avec le dictionnaire. En outre, il remplit les valeurs manquantes si nécessaire:
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)
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)
On n'a pas besoin d'un multi-index pour effectuer des opérations join . Il suffit de définir correctement la colonne d’index sur laquelle effectuer les opérations de jointure (commande df.set_index('Name')
par exemple)
L'opération join
est effectuée par défaut sur l'index. Dans votre cas, il vous suffit de spécifier que la colonne Name
correspond à votre index. Ci-dessous un exemple
Un tutoriel peut être 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')
Il y a une autre solution dans documentation sur les pandas (que je ne vois pas ici),
en utilisant le .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
Le ignore_index=True
est utilisé pour ignorer l'index du cadre de données ajouté, en le remplaçant par le prochain index disponible dans le source.
S'il existe différents noms de colonne, Nan
sera introduit.
Solution simple:
Si les noms de colonne sont similaires:
df1.merge(df2,on='col_name').merge(df3,on='col_name')
Si les noms de colonne sont différents:
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'})