web-dev-qa-db-fra.com

Python: pandas fusionner plusieurs images

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%   
33
Vasco Ferreira

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')
  • Ainsi, les valeurs de la même date sont sur les mêmes lignes.
  • Vous pouvez remplir les données non existantes de différents cadres pour différentes colonnes en utilisant fillna ().

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

43
everestial007

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])
8
dannyeuu

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)
5

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%
3
jezrael

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)
3
Allen Wang

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)])
1
zipa

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
0
Vasco Ferreira