Pandas mesclando 101

365
  • Como realizar uma junção ( LEFT| RIGHT| FULL) ( INNER| OUTER) com pandas?
  • Como adicionar NaNs para linhas ausentes após a mesclagem?
  • Como me livrar dos NaNs após a fusão?
  • Posso mesclar no índice?
  • Cruzar-se com pandas?
  • Como mesclar vários DataFrames?
  • merge? join? concat? update? Quem? O que? Por quê?!

... e mais. Eu já vi essas perguntas recorrentes perguntando sobre várias facetas da funcionalidade de mesclagem de pandas. Atualmente, a maioria das informações sobre mesclagem e seus vários casos de uso está fragmentada em dezenas de postagens mal formuladas e não pesquisáveis. O objetivo aqui é reunir alguns dos pontos mais importantes para a posteridade.

Este QnA deve ser a próxima parte de uma série de guias úteis do usuário sobre idiomas comuns de pandas (veja este post sobre pivotamento e este sobre concatenação , que abordarei mais adiante).

Observe que este post não pretende substituir a documentação , portanto, leia-o também! Alguns dos exemplos são retirados de lá.

cs95
fonte

Respostas:

520

Este post tem como objetivo fornecer aos leitores uma cartilha sobre a fusão com o SQL com pandas, como usá-lo e quando não usá-lo.

Em particular, aqui está o que este post passará:

  • Noções básicas - tipos de junções (ESQUERDA, DIREITA, EXTERNA, INTERNA)

    • mesclando com diferentes nomes de colunas
    • evitando a coluna da chave de mesclagem duplicada na saída
  • Mesclando com o índice sob diferentes condições
    • efetivamente usando seu índice nomeado
    • chave de mesclagem como o índice de um e coluna de outro
  • A Multiway mescla colunas e índices (exclusivos e não exclusivos)
  • Alternativas notáveis ​​para mergeejoin

O que este post não passará:

  • Discussões e horários relacionados ao desempenho (por enquanto). O mais notável menciona melhores alternativas, sempre que apropriado.
  • Tratamento de sufixos, remoção de colunas extras, renomeação de saídas e outros casos de uso específicos. Existem outras postagens (leia: melhor) que lidam com isso, então descubra!

Nota
A maioria dos exemplos padroniza as operações INNER JOIN enquanto demonstra vários recursos, a menos que especificado de outra forma.

Além disso, todos os DataFrames aqui podem ser copiados e replicados para que você possa brincar com eles. Além disso, consulte esta publicação sobre como ler DataFrames na sua área de transferência.

Por fim, todas as representações visuais das operações JOIN foram desenhadas à mão usando o Desenhos do Google. Inspiração daqui .

Chega de conversa, apenas me mostre como usar merge!

Configuração

np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})    
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})

left

  key     value
0   A  1.764052
1   B  0.400157
2   C  0.978738
3   D  2.240893

right

  key     value
0   B  1.867558
1   D -0.977278
2   E  0.950088
3   F -0.151357

Por uma questão de simplicidade, a coluna chave tem o mesmo nome (por enquanto).

Um INNER JOIN é representado por

Nota
Isso, juntamente com as próximas figuras, segue esta convenção:

  • azul indica linhas presentes no resultado da mesclagem
  • vermelho indica linhas que são excluídas do resultado (por exemplo, removidas)
  • verde indica valores ausentes que são substituídos por NaNs no resultado

Para executar uma INNER JOIN, chame mergeo DataFrame esquerdo, especificando o DataFrame correto e a chave de junção (no mínimo) como argumentos.

left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278

Isso retorna apenas linhas de lefte rightque compartilham uma chave comum (neste exemplo, "B" e "D).

Uma junção externa esquerda ou esquerda é representada por

Isso pode ser realizado especificando how='left'.

left.merge(right, on='key', how='left')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278

Observe cuidadosamente a localização dos NaNs aqui. Se você especificar how='left', somente as chaves de leftserão usadas e os dados ausentes serão rightsubstituídos pelo NaN.

E da mesma forma, para um DIREITO OUTER JOIN , ou RIGHT JOIN, que é ...

... especifique how='right':

left.merge(right, on='key', how='right')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278
2   E       NaN  0.950088
3   F       NaN -0.151357

Aqui, as chaves de rightsão usadas e os dados ausentes leftsão substituídos por NaN.

Finalmente, para o JOGO EXTERNO COMPLETO , dado por

especifique how='outer'.

left.merge(right, on='key', how='outer')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278
4   E       NaN  0.950088
5   F       NaN -0.151357

Isso usa as chaves dos dois quadros e os NaNs são inseridos para as linhas ausentes nos dois.

A documentação resume bem essas várias mesclagens:

insira a descrição da imagem aqui

Outras JOINs - excluindo à esquerda, excluindo à direita e excluindo totalmente / ANTI JOINs

Se você precisar de LEFT-Excluindo JOINs e RIGHT-Excluindo JOINs em duas etapas.

Para JOIN com exclusão à esquerda, representado como

Comece executando uma LETER OUTER JOIN ESQUERDA e, em seguida, filtrando (excluindo!) As linhas provenientes leftapenas,

(left.merge(right, on='key', how='left', indicator=True)
     .query('_merge == "left_only"')
     .drop('_merge', 1))

  key   value_x  value_y
0   A  1.764052      NaN
2   C  0.978738      NaN

Onde,

left.merge(right, on='key', how='left', indicator=True)

  key   value_x   value_y     _merge
0   A  1.764052       NaN  left_only
1   B  0.400157  1.867558       both
2   C  0.978738       NaN  left_only
3   D  2.240893 -0.977278       both

E da mesma forma, para um JOIN com exclusão RIGHT,

(left.merge(right, on='key', how='right', indicator=True)
     .query('_merge == "right_only"')
     .drop('_merge', 1))

  key  value_x   value_y
2   E      NaN  0.950088
3   F      NaN -0.151357

Por fim, se você precisar fazer uma mesclagem que apenas retenha as teclas da esquerda ou da direita, mas não ambas (IOW, executando uma ANTI-JOIN ),

Você pode fazer isso de maneira semelhante -

(left.merge(right, on='key', how='outer', indicator=True)
     .query('_merge != "both"')
     .drop('_merge', 1))

  key   value_x   value_y
0   A  1.764052       NaN
2   C  0.978738       NaN
4   E       NaN  0.950088
5   F       NaN -0.151357

Nomes diferentes para colunas-chave

Se as colunas-chave tiverem um nome diferente - por exemplo, lefttem keyLefte righttem em keyRightvez de key-, será necessário especificar left_one right_oncomo argumentos em vez de on:

left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)

left2

  keyLeft     value
0       A  1.764052
1       B  0.400157
2       C  0.978738
3       D  2.240893

right2

  keyRight     value
0        B  1.867558
1        D -0.977278
2        E  0.950088
3        F -0.151357

left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')

  keyLeft   value_x keyRight   value_y
0       B  0.400157        B  1.867558
1       D  2.240893        D -0.977278

Evitando coluna de chave duplicada na saída

Ao mesclar keyLeftde lefte keyRightpara right, se você deseja apenas um keyLeftou keyRight(mas não ambos) na saída, pode começar definindo o índice como uma etapa preliminar.

left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')

    value_x keyRight   value_y
0  0.400157        B  1.867558
1  2.240893        D -0.977278

Compare isso com a saída do comando logo antes (thst é a saída de left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')), você notará que keyLeftestá faltando. Você pode descobrir qual coluna manter com base no índice de quadro que está definido como chave. Isso pode ser importante quando, por exemplo, executar alguma operação OUTER JOIN.

Mesclando apenas uma única coluna de um dos DataFrames

Por exemplo, considere

right3 = right.assign(newcol=np.arange(len(right)))
right3
  key     value  newcol
0   B  1.867558       0
1   D -0.977278       1
2   E  0.950088       2
3   F -0.151357       3

Se você precisar mesclar apenas "new_val" (sem nenhuma das outras colunas), geralmente é possível apenas agrupar colunas antes de mesclar:

left.merge(right3[['key', 'newcol']], on='key')

  key     value  newcol
0   B  0.400157       0
1   D  2.240893       1

Se você estiver fazendo uma junção externa esquerda, uma solução com melhor desempenho envolveria map:

# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Como mencionado, isso é semelhante, mas mais rápido que

left.merge(right3[['key', 'newcol']], on='key', how='left')

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Mesclando em várias colunas

Para ingressar em mais de uma coluna, especifique uma lista para on(ou left_one right_on, conforme apropriado).

left.merge(right, on=['key1', 'key2'] ...)

Ou, caso os nomes sejam diferentes,

left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])

Outras merge*operações e funções úteis

Esta seção aborda apenas o básico e foi projetada para aguçar apenas o apetite. Para mais exemplos e casos, consulte a documentação sobre merge, joineconcat assim como os links para as especificações de função.


* -JOIN baseado em índice (+ coluna ( merges) de índice )

Configuração

np.random.seed([3, 14])
left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D'])    
right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'

left
           value
idxkey          
A      -0.602923
B      -0.402655
C       0.302329
D      -0.524349

right

           value
idxkey          
B       0.543843
D       0.013135
E      -0.326498
F       1.385076

Normalmente, uma mesclagem no índice seria assim:

left.merge(right, left_index=True, right_index=True)


         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Suporte para nomes de índice

Se o seu índice for nomeado, os usuários da v0.23 também poderão especificar o nome do nível para on(ou left_one right_onconforme necessário).

left.merge(right, on='idxkey')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Mesclando no índice de uma, coluna (s) de outra

É possível (e bastante simples) usar o índice de um e a coluna de outro para realizar uma mesclagem. Por exemplo,

left.merge(right, left_on='key1', right_index=True)

Ou vice-versa ( right_on=...e left_index=True).

right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
right2

  colkey     value
0      B  0.543843
1      D  0.013135
2      E -0.326498
3      F  1.385076

left.merge(right2, left_index=True, right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

Nesse caso especial, o índice para lefté nomeado, portanto, você também pode usar o nome do índice com left_on, assim:

left.merge(right2, left_on='idxkey', right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

DataFrame.join
Além desses, há outra opção sucinta. Você pode usar DataFrame.joinquais padrões se associam ao índice. DataFrame.joinfaz uma junção externa esquerda por padrão, então how='inner'é necessário aqui.

left.join(right, how='inner', lsuffix='_x', rsuffix='_y')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Observe que eu precisava especificar os argumentos lsuffixe, rsuffixpois join, caso contrário, haveria um erro:

left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')

Como os nomes das colunas são os mesmos. Isso não seria um problema se eles tivessem nomes diferentes.

left.rename(columns={'value':'leftvalue'}).join(right, how='inner')

        leftvalue     value
idxkey                     
B       -0.402655  0.543843
D       -0.524349  0.013135

pd.concat
Por fim, como uma alternativa para junções baseadas em índice, você pode usar pd.concat:

pd.concat([left, right], axis=1, sort=False, join='inner')

           value     value
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Omita join='inner'se você precisar de uma JUNÇÃO EXTERNA COMPLETA (o padrão):

pd.concat([left, right], axis=1, sort=False)

      value     value
A -0.602923       NaN
B -0.402655  0.543843
C  0.302329       NaN
D -0.524349  0.013135
E       NaN -0.326498
F       NaN  1.385076

Para mais informações, consulte esta postagem canônica no pd.concat@piRSquared .


Generalizando: mergeing vários DataFrames

Muitas vezes, a situação surge quando vários DataFrames devem ser mesclados. Ingenuamente, isso pode ser feito encadeando mergechamadas:

df1.merge(df2, ...).merge(df3, ...)

No entanto, isso rapidamente fica fora de controle para muitos DataFrames. Além disso, pode ser necessário generalizar para um número desconhecido de DataFrames.

Apresento aqui as pd.concatjunções de várias vias em chaves exclusivas e as DataFrame.joinjunções de várias vias em chaves não exclusivas . Primeiro, a configuração.

# Setup.
np.random.seed(0)
A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})    
B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
dfs = [A, B, C] 

# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')

dfs2 = [A2, B2, C2]

Mesclagem de várias vias em chaves exclusivas (ou índice)

Se suas chaves (aqui, a chave pode ser uma coluna ou um índice) são únicas, você pode usá-lo pd.concat. Observe que pd.concatjunta DataFrames no índice .

# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
    df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')

       valueA    valueB  valueC
key                            
D    2.240893 -0.977278     1.0

Omitir join='inner'para uma junção externa completa. Observe que você não pode especificar junções ESQUERDA ou DIREITA EXTERNA (se você precisar delas, use as joindescritas abaixo).

Mesclagem múltipla em chaves com duplicatas

concaté rápido, mas tem suas deficiências. Ele não pode manipular duplicatas.

A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})

pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)

Nesta situação, podemos usá- joinlo, pois ele pode manipular chaves não exclusivas (observe que joinse junta a DataFrames em seu índice; ele chama por mergebaixo do capô e faz uma junção externa esquerda, a menos que seja especificado de outra forma).

# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
    [df.set_index('key') for df in (B, C)], how='inner').reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')

       valueA    valueB  valueC
key                            
D    1.454274 -0.977278     1.0
D    0.761038 -0.977278     1.0
cs95
fonte
49

Uma visão visual suplementar de pd.concat([df0, df1], kwargs). Observe que, kwarg axis=0ou axis=1's significado não é tão intuitivo quanto df.mean()oudf.apply(func)


no pd.concat ([df0, df1])

eliu
fonte
9
Este é um bom diagrama. Posso perguntar como você o produziu?
cs95 20/05/19
6
"inserção ==> desenho ... ==> novo" incorporado do google doc (a partir de 2019-maio). Mas, para deixar claro: a única razão pela qual usei o google doc para esta foto é porque minhas anotações são armazenadas no google doc e eu gostaria de uma foto que possa ser modificada rapidamente no próprio google doc. Na verdade, agora que você mencionou, a ferramenta de desenho do google doc é muito legal.
eliu 21/05/19
Uau, isso é ótimo. Vindo do mundo SQL, a junção "vertical" não é uma junção na minha cabeça, pois a estrutura da tabela é sempre fixa. Agora até pense que os pandas deveriam se consolidar concate mergecom um parâmetro de direção sendo horizontalou vertical.
Ufos
2
@Ufos Não é isso exatamente o que axis=1e axis=0é?
cs95
2
sim, lá está agora mergee concate eixo e tudo. No entanto, como mostra o @eliu, é tudo o mesmo conceito de mesclagem com "esquerda" e "direita" e "horizontal" ou "vertical". Pessoalmente, tenho que examinar a documentação toda vez que preciso lembrar qual é o "eixo" 0e qual é 1.
Ufos