Pandas Merge - Cómo evitar la duplicación de columnas


93

Estoy intentando una fusión entre dos marcos de datos. Cada marco de datos tiene dos niveles de índice (fecha, cúspide). En las columnas, algunas columnas coinciden entre las dos (moneda, fecha de ajuste), por ejemplo.

¿Cuál es la mejor manera de fusionarlos por índice, pero no tomar dos copias de moneda y fecha de ajuste?

Cada marco de datos tiene 90 columnas, por lo que estoy tratando de evitar escribir todo a mano.

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 lo hago:

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

yo obtengo

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

¡Gracias! ...

Respuestas:


143

Puede calcular las columnas que están solo en un DataFrame y usar esto para seleccionar un subconjunto de columnas en la combinación.

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

Luego realice la fusión (tenga en cuenta que este es un objeto de índice pero tiene un tolist()método útil ).

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

Esto evitará que las columnas entren en conflicto en la fusión.


¿Qué pasa si la clave es una columna y se llama igual? Se eliminaría con el primer paso.
Guerra

¡¡¡Muchas gracias!!!
Cloudy_Green

88

Yo uso la suffixesopción en .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)

Gracias @ijoseph


15
Sería una respuesta más útil si incluyera el código para filtering (que es bastante sencillo, pero aún requiere mucho tiempo de búsqueda / propenso a errores de recordar). ie dfNew.drop(list(dfNew.filter(regex='_y$')), axis=1, inplace=True)
ijoseph

5

Soy nuevo con Pandas, pero quería lograr lo mismo, evitando automáticamente los nombres de columna con _x o _y y eliminando datos duplicados. Finalmente lo hice utilizando esta respuesta y esto uno de Stackoverflow

sales.csv

    ciudad; estado; unidades
    Mendocino; CA; 1
    Denver; CO; 4
    Austin; TX; 2

ingresos.csv

    branch_id; ciudad; ingresos; 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; Yo

merge.py importar 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=';')

Al ejecutar el comando de fusión, reemplazo el _xsufijo con una cadena vacía y puedo eliminar las columnas que terminan con_y

output.csv

    id; ciudad; estado; unidades; branch_id; ingresos; 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

3

Sobre la base de la respuesta de @ rprog, puede combinar las diversas piezas del sufijo y el paso de filtro en una línea usando una expresión regular negativa:

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

O usando df.join:

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

La expresión regular aquí guarda todo lo que no termine con la palabra "DROP", así que asegúrese de usar un sufijo que no aparezca entre las columnas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.