Pandas: combinando data frames com merge() e concat()
Diferentes estratégias para combinar tabelas
Muitas vezes estamos com pressa e precisamos encontrar uma solução rápida para um detalhe do nosso código. Todo programador passa por isso e ainda se vê dividido entre uma linha de raciocínio e o problema específico no código.
Uma excelente estratégia para solucionar problemas complexos é reduzi-los à algo mais simples, entender seu comportamento com menos informações, e assim projetar seu comportamento em situações maiores.
O objetivo deste post é apresentar os métodos merge() e concat() para unirmos data frames de maneira objetiva e com poucos dados. Assim o leitor pode encontrar a solução para um problema de unir tabelas com muitos dados a partir do entendimento do funcionamento destes métodos com dados reduzidos.
Aproveito e deixo a dica de um ótimo curso de introdução ao Python.
Antes de apresentarmos os métodos, criaremos duas tabelas para trabalharmos. Mais adiante criaremos uma terceira tabela para ilustrar outros casos:
São tabelas simples. Mas é importante que o leitor olhe para cada uma por alguns segundos e tente entende-las antes de avançar no texto. O código é apresentado caso queiram replicar os dados.
Tabela 1
import pandas as pdtabela_1 = pd.DataFrame({
'Nome':['João', 'João', 'Pedro' , 'Caio'],
'Telefone': ['12121', '343434', '565656', '787878'],
'Carros': ['azul', 'preto', 'verde' , 'amarelo']})
Tabela 2
tabela_2 = pd.DataFrame({
'Nome':['João', 'Marcelo', 'Thiago' , 'Caio'],
'Irmãos': ['1', '3', '2' , '2']})
O que temos até aqui: duas tabelas com uma única coluna em comum (Nome). Existem dois nomes em comum entre as duas tabelas (João e Caio).
Antes de unirmos as tabelas, precisamos refletir sobre o que queremos. Queremos todas as informações? Queremos somente dados para os nomes em comum entre as tabelas? Ou queremos somente os dados sobre “João”? Para isso, relembrar um pouco sobre teoria dos conjuntos pode ajudar.
Teoria dos conjuntos
Estude os tipos de combinação, na imagem abaixo, entre os conjuntos (tabelas)com que trabalharemos :
Precisamos dizer qual tipo de ligação queremos no parâmetro ‘how’. O Pandas detecta automaticamente colunas em comum. Mas para tabelas mais complexas, pode ser necessário que se especifique qual é(são) a(s) coluna(s) para se obter o resultado desejado. Aqui apontaremos a coluna ‘Nome’ no parâmetro “on”.
Merge()
Aqui, apresentaremos os diferentes tipos de merge() e seus resultados em tabelas.
how = ‘inner’
Caso queiramos a interseção exata entre as tabelas:
m = pd.merge(tabela_1, tabela_2, how = 'inner', on = 'Nome')
Obtivemos a interseção entre as duas tabelas. Somente os valores na coluna “Nome” que existem em ambas tabelas aparecem no nosso resultado.
how = ‘outer’
Caso queiramos todas as informações, de ambas tabelas, fazemos um merge “outer”:
m = pd.merge(tabela_1, tabela_2, how = 'outer')
Perceba que ‘Pedro” não possui dados para “Irmãos”. E “Marcelo” e “Thiago” não possuem dados para “Telefone” e “Carros”. Volte nas tabelas 1 e 2 veja que esse é exatamente o resultado esperado se quisermos todas as informações.
how = ‘left’ ou how= ‘right’
Um merge “left” ou “right” depende de qual tabela você deixa na direita ou esquerda. Para o seguinte cenário faremos um merge do tipo “left”. Mas o mesmo resultado pode ser obtido com um merge “right” trocando a posição das tabelas no método “merge”.
## Obtém o mesmo resultado ##
m = pd.merge(tabela_1, tabela_2, how = 'left', on = 'Nome')
m = pd.merge(tabela_2, tabela_1, how = 'right', on = 'Nome')
Para esse tipo de merge criaremos dois cenários na esperança de que o leitor encontre um que se pareça com o seu.
Cenários para merge left
Cenário 1
Suponhamos que para a sua tarefa você deva manter os dados da tabela_1 e ir acrescentando colunas conforme mais dados sobre ‘João’, ‘Pedro’ e ‘Caio’ apareçam. Em outras palavras, somente ‘Nomes’ que existam na tabela_1 serão trazidos da tabela_2. Neste exemplo deixaremos a tabela_1 à esquerda e faremos um merge ‘left’ m = pd.merge(tabela_1, tabela_2, how = 'left', on = 'Nome')
Observe que 1) Não foram incluídos ‘Marcelo’ e ‘Thiago’ pois estavam somente na tabela_2; 2) A coluna ‘Irmãos’, que estava somente na tabela_2 aparece no resultado final e 3) Não existem dados de ‘Irmãos’ para ‘Pedro’. Novamente, retorne às tabelas e veja se isso faz sentido.
Cenário 2
Neste cenário uniremos a tabela_1 à uma nova tabela que criaremos a seguir. A tabela_3:
tabela_3 = pd.DataFrame({
'Nome':['João', 'João', 'Pedro' , 'Caio'],
'Telefone': ['12121', '343434', '565656', '787878'],
'Carros': ['BRANCO', 'PRATA', 'PRATA' , 'VERMELHO']})
Perceba que a nova tabela, tabela_3, é igual a tabela_1 mas com valores novos para a coluna ‘Carros’.
O que você acha que vai acontecer se tentarmos unir tabela_1 e tabela_3? Com qual valor para ‘Carros’ devemos ficar ao efetuarmos o seguinte merge?
pd.merge(tabela_1, tabela_3, how='left, on = 'Nome')
Vejamos o resultado:
Entendendo: A nossa chave para o merge sendo ‘Nome’, todas outras colunas iguais entre as tabelas são separadas em _x e _y, onde:
- _x Corresponde aos valores que existiam na tabela da esquerda (tabela_1).
- _y Corresponde aos valores que existiam na tabela da direita (tabela_2).
OBS: Esses sufixos podem ser alterados. Ver documentação.
Atenção: Como a coluna “Telefone” aparece nas duas tabelas com os mesmos valores, poderíamos utiliza-la como chave também. E é o que faremos aqui para ilustrar o que aconteceria em m = pd.merge(tabela_1, tabela_3, how='left, on = ['Nome', 'Telefone'])
:
Tendo “Nome” e “Telefone” como chaves, nossa tabela só separa em _x e _y as colunas em comum restantes, no nosso caso, a única, “Carros”.
Concat()
Também veremos como este método se comporta em dois cenários:
Cenário 1:
Vejamos como a método se comporta no último caso, onde queríamos unir tabela_1 e tabela_3. Lembando: São tabelas com as mesmas colunas.
pd.concat([tabela_1, tabela_3]) # atenção ao uso dos colchetes
O método gerou uma tabela com todas as combinações de colunas.
Cenário 2
Criaremos uma nova tabela chamada tabela_4, idêntica à tabela_3, porém com uma coluna a mais, a coluna ‘Animais’ :
## Nova tabela com a coluna "Animais" ##tabela_4 = pd.DataFrame({
'Nome':['João', 'João', 'Pedro' ],
'Telefone': ['12121', '343434', '565656'],
'Carros': ['BRANCO', 'PRATA', 'PRATA' ],
'Animais': ['cachorro', 'gato', 'cachorro']})
Agora faremos a concatenação entre tabela_1 e tabela_4:
## Chamaremos a tabela de 't'
## sort_values organiza a tabela em sequencia alfabética na coluna 'Nome't =pd.concat([tabela_1, tabela_4]).sort_values(by='Nome')
t
Como podemos ver, as linhas que vieram da tabela_1 receberam ‘NaN’ na coluna ‘Animais’. Previsivelmente, afinal essa coluna só existe na tabela_4.
Aqui, cada linha isolada não faz sentido. Mas o usuário pode filtrar a tabela para obter a informação que deseja.
O código abaixo, por exemplo, nos retorna todos ‘Animais’ relacionados a ‘Nome’ igual a ‘João’, excluindo aqueles valores que aparecem como NaN:
t[(t['Nome'] == 'João') & (t['Animais'].notna())]['Animais']
Conclusões
Leia sobre o assunto na documentação e em outros posts, mas também realize seus testes. Trabalhando com tabelas muito grandes, pode se tornar inviável (ou pelo menos bem difícil) verificar se o resultado da união de duas tabelas está de acordo com o que se deseja.
Portanto, simplifique: gere seus próprios dados, crie hipóteses (suponha o que vai acontecer), cruze as tabelas e veja o que acontece. Estando tudo certo em uma caso similar e mais simples, a chance de que esteja certo para o caso mais complexo aumenta.
Limpe seus dados: Remova duplicidades; Verifique se existem linhas que não fazem sentido, mas não as apague necessariamente! Pois ao realizar cálculos com valores numéricos em uma tabela você pode ter valores a mais (especialmente uma gerada com concat()), porém outras colunas podem ainda ser necessárias.
Tente este processo: filtre por um, digamos, ‘Nome’ específico, obtenha uma tabela bem menor e veja os valores que lá aparecem. Se alguma linha estranha estiver presente e for necessário removê-la, filtre-a na tabela original.
Realize check points: Vá salvando arquivos temporários ao longo da limpeza, para retomar a linha de raciocínio a partir de certo ponto. Para isso, não deixe de nomear seus arquivos e de comentar seu código de maneira explícita.