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á:
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 merge
o 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 left
e right
que 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 left
serão usadas e os dados ausentes serão right
substituí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 right
são usadas e os dados ausentes left
sã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:
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 left
apenas,
(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, left
tem keyLeft
e right
tem em keyRight
vez de key
-, será necessário especificar left_on
e right_on
como 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 keyLeft
de left
e keyRight
para right
, se você deseja apenas um keyLeft
ou 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 keyLeft
está 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_on
e 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
, join
econcat
assim como os links para as especificações de função.
* -JOIN baseado em índice (+ coluna ( merge
s) 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_on
e right_on
conforme 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.join
quais padrões se associam ao índice. DataFrame.join
faz 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 lsuffix
e, rsuffix
pois 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: merge
ing vários DataFrames
Muitas vezes, a situação surge quando vários DataFrames devem ser mesclados. Ingenuamente, isso pode ser feito encadeando merge
chamadas:
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.concat
junções de várias vias em chaves exclusivas e as DataFrame.join
junçõ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.concat
junta 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 join
descritas 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á- join
lo, pois ele pode manipular chaves não exclusivas (observe que join
se junta a DataFrames em seu índice; ele chama por merge
baixo 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
concat
emerge
com um parâmetro de direção sendohorizontal
ouvertical
.axis=1
eaxis=0
é?merge
econcat
e 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"0
e qual é1
.