Pandas Merge - Comment éviter la duplication de colonnes

93

Je tente une fusion entre deux trames de données. Chaque bloc de données a deux niveaux d'index (date, cusip). Dans les colonnes, certaines colonnes correspondent entre les deux (devise, date d'aj) par exemple.

Quelle est la meilleure façon de les fusionner par index, mais de ne pas prendre deux copies de la devise et de la date adj.

Chaque trame de données compte 90 colonnes, j'essaie donc d'éviter de tout écrire à 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! ...

user1911092
la source

Réponses:

143

Vous pouvez travailler sur les colonnes qui ne sont que dans un DataFrame et l'utiliser 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 qu'il a une tolist()méthode pratique ).

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

Cela évitera que les colonnes ne s'affrontent lors de la fusion.

EdChum
la source
Que faire si la clé est une colonne et qu'elle s'appelle la même chose? Il serait abandonné dès la première étape.
Guerra
Merci beaucoup!!!
Cloudy_Green
88

J'utilise l' suffixesoption dans .merge():

dfNew = df.merge(df2, left_index=True, right_index=True,
                 how='outer', suffixes=('', '_y'))
dfNew.drop(dfNew.filter(regex='_y$').columns.tolist(),axis=1, inplace=True)

Merci @ijoseph

rprog
la source
15
Ce serait une réponse plus utile si elle incluait le code pour filtering (ce qui est assez simple, mais prend encore beaucoup de temps à rechercher / sujet aux erreurs à retenir). ie dfNew.drop(list(dfNew.filter(regex='_y$')), axis=1, inplace=True)
ijoseph
5

Je suis fraîchement nouveau avec Pandas mais je voulais réaliser la même chose, en évitant automatiquement les noms de colonne avec _x ou _y et en supprimant les données en double. Je l'ai finalement fait en utilisant cette réponse et celle- ci de Stackoverflow

sales.csv

    ville; état; unités
    Mendocino; CA; 1
    Denver; CO; 4
    Austin; TX; 2

revenue.csv

    id_branche; ville; revenu; id_état
    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 importer des 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 _xsuffixe par une chaîne vide et je peux supprimer les colonnes se terminant par_y

output.csv

    id; ville; état; unités; id_branche; revenu; id_état
    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
JulienD
la source
4

En vous basant sur la réponse de @ rprog, vous pouvez combiner les différentes parties de l'étape de suffixe et de filtre en une seule ligne en utilisant une expression régulière 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)")

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

Elliott Collins
la source