J'ai différentes bases de données et j'ai besoin de les fusionner en fonction de la colonne de date. Si je n'avais que deux images de données, je pourrais utiliser df1.merge(df2, on='date')
, pour le faire avec trois images de données, j'utilise df1.merge(df2.merge(df3, on='date'), on='date')
, mais il devient très complexe et illisible de le faire avec plusieurs images de données.
Toutes les images ont une colonne en commun -date
, mais elles n'ont pas le même nombre de lignes ni de colonnes et je n'ai besoin que des lignes dans lesquelles chaque date est commune à chaque image.
Donc, j'essaie d'écrire une fonction de récursivité qui renvoie une image de données avec toutes les données, mais cela n'a pas fonctionné. Comment dois-je fusionner plusieurs images alors?
J'ai essayé de différentes manières et j'ai eu des erreurs comme out of range
, keyerror 0/1/2/3
et can not merge DataFrame with instance of type <class 'NoneType'>
.
C'est le script que j'ai écrit:
dfs = [df1, df2, df3] # list of dataframes
def mergefiles(dfs, countfiles, i=0):
if i == (countfiles - 2): # it gets to the second to last and merges it with the last
return
dfm = dfs[i].merge(mergefiles(dfs[i+1], countfiles, i=i+1), on='date')
return dfm
print(mergefiles(dfs, len(dfs)))
Un exemple: df_1:
May 19, 2017;1,200.00;0.1%
May 18, 2017;1,100.00;0.1%
May 17, 2017;1,000.00;0.1%
May 15, 2017;1,901.00;0.1%
df_2:
May 20, 2017;2,200.00;1000000;0.2%
May 18, 2017;2,100.00;1590000;0.2%
May 16, 2017;2,000.00;1230000;0.2%
May 15, 2017;2,902.00;1000000;0.2%
df_3:
May 21, 2017;3,200.00;2000000;0.3%
May 17, 2017;3,100.00;2590000;0.3%
May 16, 2017;3,000.00;2230000;0.3%
May 15, 2017;3,903.00;2000000;0.3%
Résultat de fusion attendu:
May 15, 2017; 1,901.00;0.1%; 2,902.00;1000000;0.2%; 3,903.00;2000000;0.3%
Vous trouverez ci-dessous le moyen le plus simple et le plus compréhensible de fusionner plusieurs bases de données si des requêtes complexes ne sont pas impliquées.
Il vous suffit simplement de fusionner avec DATE comme index et de le faire à l’aide de la méthode OUTER (pour obtenir toutes les données).
import pandas as pd
from functools import reduce
df1 = pd.read_table('file1.csv', sep=',')
df2 = pd.read_table('file2.csv', sep=',')
df3 = pd.read_table('file3.csv', sep=',')
Donc, chargez essentiellement tous les fichiers que vous avez en tant que trame de données. Fusionnez ensuite les fichiers en utilisant la fonction merge
ou reduce
.
# compile the list of dataframes you want to merge
data_frames = [df1, df2, df3]
vous pouvez ajouter autant de données-cadres dans le code ci-dessus. C'est la bonne partie de cette méthode. Aucune question complexe impliquée.
Pour conserver les valeurs qui appartiennent à la même date, vous devez le fusionner sur le DATE
df_merged = reduce(lambda left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames)
# if you want to fill the values that don't exist in the lines of merged dataframe simply fill with required strings as
df_merged = reduce(lambda left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames).fillna('void')
Ensuite, écrivez les données fusionnées dans le fichier csv si vous le souhaitez.
pd.DataFrame.to_csv(df_merged, 'merged.txt', sep=',', na_rep='.', index=False)
Cela devrait vous donner
DATE VALUE1 VALUE2 VALUE3 ....
On dirait que les données ont les mêmes colonnes, vous pouvez donc:
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
merged_df = pd.concat([df1, df2])
functools.reduce et pd.concat sont de bonnes solutions, mais en termes de temps d’exécution, pd.concat est la meilleure.
from functools import reduce
import pandas as pd
dfs = [df1, df2, df3, ...]
nan_value = 0
# solution 1 (fast)
result_1 = pd.concat(dfs, join='outer', axis=1).fillna(nan_value)
# solution 2
result_2 = reduce(lambda left,right: pd.merge(df_left, df_right,
left_index=True, right_index=True,
how='outer'),
dfs).fillna(nan_value)
Il existe 2 solutions pour cela, mais il renvoie toutes les colonnes séparément:
import functools
dfs = [df1, df2, df3]
df_final = functools.reduce(lambda left,right: pd.merge(left,right,on='date'), dfs)
print (df_final)
date a_x b_x a_y b_y c_x a b c_y
0 May 15,2017 900.00 0.2% 1,900.00 1000000 0.2% 2,900.00 2000000 0.2%
k = np.arange(len(dfs)).astype(str)
df = pd.concat([x.set_index('date') for x in dfs], axis=1, join='inner', keys=k)
df.columns = df.columns.map('_'.join)
print (df)
0_a 0_b 1_a 1_b 1_c 2_a 2_b 2_c
date
May 15,2017 900.00 0.2% 1,900.00 1000000 0.2% 2,900.00 2000000 0.2%
La réponse de @ dannyeuu est correcte. pd.concat fait naturellement une jointure sur des colonnes d'index, si vous définissez l'option d'axe sur 1. La valeur par défaut est une jointure externe, mais vous pouvez également spécifier une jointure interne. Voici un exemple:
x = pd.DataFrame({'a': [2,4,3,4,5,2,3,4,2,5], 'b':[2,3,4,1,6,6,5,2,4,2], 'val': [1,4,4,3,6,4,3,6,5,7], 'val2': [2,4,1,6,4,2,8,6,3,9]})
x.set_index(['a','b'], inplace=True)
x.sort_index(inplace=True)
y = x.__deepcopy__()
y.loc[(14,14),:] = [3,1]
y['other']=range(0,11)
y.sort_values('val', inplace=True)
z = x.__deepcopy__()
z.loc[(15,15),:] = [3,4]
z['another']=range(0,22,2)
z.sort_values('val2',inplace=True)
pd.concat([x,y,z],axis=1)
Si vous filtrez par date commune, cela le retournera:
dfs = [df1, df2, df3]
checker = dfs[-1]
check = set(checker.loc[:, 0])
for df in dfs[:-1]:
check = check.intersection(set(df.loc[:, 0]))
print(checker[checker.loc[:, 0].isin(check)])
Merci pour votre aide @ jezrael, @ zipa et @ everestial007, les deux réponses sont ce dont j'ai besoin. Si je voulais faire un récursif, cela fonctionnerait également comme prévu:
def mergefiles(dfs=[], on=''):
"""Merge a list of files based on one column"""
if len(dfs) == 1:
return "List only have one element."
Elif len(dfs) == 2:
df1 = dfs[0]
df2 = dfs[1]
df = df1.merge(df2, on=on)
return df
# Merge the first and second datafranes into new dataframe
df1 = dfs[0]
df2 = dfs[1]
df = dfs[0].merge(dfs[1], on=on)
# Create new list with merged dataframe
dfl = []
dfl.append(df)
# Join lists
dfl = dfl + dfs[2:]
dfm = mergefiles(dfl, on)
return dfm