189 votes

Fusion de Pandas - Comment éviter de dupliquer des colonnes

Je tente une fusion entre deux data frames. Chaque data frame a deux niveaux d'index (date, cusip). Dans les colonnes, certaines colonnes correspondent entre les deux (devise, date ajustée) par exemple.

Quel est le meilleur moyen de fusionner ceux-ci par index, mais sans prendre deux copies de la devise et de la date ajustée.

Chaque data frame a 90 colonnes, donc j'essaie d'éviter d'écrire tout à la main.

df:                 currency  adj_date   data_col1 ...
date        cusip
2012-01-01  XSDP      USD      2012-01-03   0.45
...

df2:                currency  adj_date   data_col2 ...
date        cusip
2012-01-01  XSDP      USD      2012-01-03   0.45
...

Si je fais :

dfNew = merge(df, df2, left_index=True, right_index=True, how='outer')

Je reçois

dfNew:              currency_x  adj_date_x   data_col2 ... currency_y adj_date_y
date        cusip
2012-01-01  XSDP      USD      2012-01-03   0.45             USD         2012-01-03

Merci! ...

234voto

EdChum Points 10205

Vous pouvez identifier les colonnes qui ne sont présentes que dans un DataFrame et utiliser cela pour sélectionner un sous-ensemble de colonnes dans la fusion.

cols_to_use = df2.columns.difference(df.columns)

Ensuite, effectuez la fusion (notez qu'il s'agit d'un objet d'index mais il possède une méthode pratique tolist()).

dfNew = merge(df, df2[cols_to_use], left_index=True, right_index=True, how='outer')

Cela évitera tout conflit de colonnes lors de la fusion.

196voto

rprog Points 1232

J'utilise l'option suffixes dans .merge() suivi de drop():

dfNew = df.merge(df2, left_index=True, right_index=True,
                 how='outer', suffixes=('', '_y'))

dfNew.drop(dfNew.filter(regex='_y$').columns, axis=1, inplace=True)

Merci @ijoseph

25voto

Elliott Collins Points 404

En s'appuyant sur la réponse de @rprog, vous pouvez combiner les différentes parties de l'étape de suffixe et de filtrage en une seule ligne en utilisant une regex négative :

dfNew = df.merge(df2, left_index=True, right_index=True,
             how='outer', suffixes=('', '_DROP')).filter(regex='^(?!.*_DROP)')

Ou en utilisant df.join:

dfNew = df.join(df2, lsuffix="DROP").filter(regex="^(?!.*DROP)")

La regex ici garde tout ce qui ne se termine pas par le mot "DROP", donc assurez-vous simplement d'utiliser un suffixe qui n'apparaît pas parmi les colonnes déjà présentes.

7voto

JulienD Points 151

Je suis tout nouveau avec Pandas, mais je voulais faire la même chose, en évitant automatiquement les noms de colonnes avec _x ou _y et en supprimant les données en double. J'ai finalement réussi en utilisant cette réponse et cette autre de Stackoverflow

sales.csv

    city;state;units
    Mendocino;CA;1
    Denver;CO;4
    Austin;TX;2

revenue.csv

    branch\_id;city;revenue;state\_id
    10;Austin;100;TX
    20;Austin;83;TX
    30;Austin;4;TX
    47;Austin;200;TX
    20;Denver;83;CO
    30;Springfield;4;I

merge.py import pandas

def drop_y(df):
    # list comprehension of the cols that end with '_y'
    to_drop = [x for x in df if x.endswith('_y')]
    df.drop(to_drop, axis=1, inplace=True)

sales = pandas.read_csv('data/sales.csv', delimiter=';')
revenue = pandas.read_csv('data/revenue.csv', delimiter=';')

result = pandas.merge(sales, revenue,  how='inner', left_on=['state'], right_on=['state_id'], suffixes=('', '_y'))
drop_y(result)
result.to_csv('results/output.csv', index=True, index_label='id', sep=';')

Lors de l'exécution de la commande de fusion, je remplace le suffixe _x par une chaîne vide, puis je peux supprimer les colonnes se terminant par _y

output.csv

    id;city;state;units;branch\_id;revenue;state\_id
    0;Denver;CO;4;20;83;CO
    1;Austin;TX;2;10;100;TX
    2;Austin;TX;2;20;83;TX
    3;Austin;TX;2;30;4;TX
    4;Austin;TX;2;47;200;TX

3voto

sophocles Points 5617

C'est un peu contourner le problème, mais j'ai écrit une fonction qui gère essentiellement les colonnes supplémentaires :

def merge_fix_cols(df_company,df_product,uniqueID):

    df_merged = pd.merge(df_company,
                         df_product,
                         how='left',left_on=uniqueID,right_on=uniqueID)    
    for col in df_merged:
        if col.endswith('_x'):
            df_merged.rename(columns = lambda col:col.rstrip('_x'),inplace=True)
        elif col.endswith('_y'):
            to_drop = [col for col in df_merged if col.endswith('_y')]
            df_merged.drop(to_drop,axis=1,inplace=True)
        else:
            pass
    return df_merged

Semble bien fonctionner avec mes fusions !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X