Pandas Merge - Como evitar colunas duplicadas

96

Estou tentando uma fusão entre dois quadros de dados. Cada quadro de dados tem dois níveis de índice (data, cúspide). Nas colunas, algumas colunas correspondem entre os dois (moeda, data de ajuste), por exemplo.

Qual é a melhor maneira de mesclar estes por índice, mas não levar duas cópias da moeda e data de ajuste.

Cada quadro de dados tem 90 colunas, então estou tentando evitar escrever tudo à mão.

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

Se eu fizer:

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

eu recebo

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

Obrigado! ...

user1911092
fonte

Respostas:

149

Você pode calcular as colunas que estão em apenas um DataFrame e usar isso para selecionar um subconjunto de colunas na mesclagem.

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

Em seguida, execute a mesclagem (observe que este é um objeto de índice, mas tem um tolist()método útil ).

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

Isso evitará que as colunas colidam na mesclagem.

EdChum
fonte
4
E se a chave for uma coluna e tiver o mesmo nome? Seria descartado com o primeiro passo.
Guerra
94

Eu uso a suffixesopção em .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)

Obrigado @ijoseph

rprog
fonte
18
Seria uma resposta mais útil se incluísse o código para filtering (que é bastante simples, mas ainda assim demorado para pesquisar / sujeito a erros de lembrar). ie dfNew.drop(list(dfNew.filter(regex='_y$')), axis=1, inplace=True)
ijoseph
6

Com base na resposta de @rprog, você pode combinar as várias partes do sufixo e etapa do filtro em uma linha usando um regex negativo:

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

Ou usando df.join:

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

A regex aqui está mantendo tudo que não termine com a palavra "DROP", então certifique-se de usar um sufixo que já não apareça entre as colunas.

Elliott Collins
fonte
5

Eu sou novo no Pandas, mas queria conseguir a mesma coisa, evitando automaticamente nomes de coluna com _x ou _y e removendo dados duplicados. Eu finalmente o fez usando essa resposta e este um dos Stackoverflow

sales.csv

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

receita.csv

    branch_id; cidade; receita; estado_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=';')

Ao executar o comando de mesclagem, substituo o _xsufixo por uma string vazia e posso remover as colunas que terminam com_y

output.csv

    id; cidade; estado; unidades; branch_id; receita; estado_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
JulienD
fonte
0

Isso é um pouco para contornar o problema, mas escrevi uma função que basicamente lida com as colunas extras:

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

Parece funcionar bem com minhas mesclagens!

sophods
fonte
0

você não pode apenas definir subconjuntos das colunas em qualquer um dos df primeiro?

[i para i em df.columns se eu não estiver em df2.columns]

dfNew = merge (df [i for i in df.columns se i não estiver em df2.columns] , df2, left_index = True, right_index = True, how = 'outer')

user6046760
fonte