Pandas: Mesclar e Juntar
Última atualização: 2026-08-26
Dados do mundo real nunca vivem em uma única tabela — informações de usuários em uma tabela, registros de pedidos em outra, detalhes de produtos em mais outra. A análise exige uni-los, e é exatamente isso que o mesclagem faz. O mesclagem do Pandas implementa todos os tipos de JOIN do SQL. Nesta seção, usamos diagramas Mermaid para ilustrar o efeito de todos os 4 tipos de junção, para que você possa entender claramente "o que é mantido da tabela da esquerda e o que é mantido da tabela da direita."
1. What You Will Learn
- ❶ The 4 mesclagem join types
- ❷ on / left_on / right_on
- ❸ Multi-key merges
- ❹ suffixes and indicator
- ❺ validate and cross join
2. Associação Usuário-Pedido do Bob
(1) O Problema: Duas Tabelas Separadas, Sem Forma de Analisar
O Bob tem uma tabela de usuários e uma tabela de pedidos. Ele quer ver "o que cada usuário comprou":
import pandas as pd
users = pd.DataFrame({
'user_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'Carol']
})
orders = pd.DataFrame({
'order_id': ['O001', 'O002', 'O003'],
'user_id': [1, 2, 2],
'amount': [120, 85, 200]
})
# Como combinar? Precisamos vincular por user_id
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o exemplo. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
(2) A Solução: mesclagem em Uma Única Linha
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o exemplo. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: Merge básico (Dificuldade ⭐)
import pandas as pd
users = pd.DataFrame({
'user_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'Carol']
})
orders = pd.DataFrame({
'order_id': ['O001', 'O002', 'O003'],
'user_id': [1, 2, 2],
'amount': [120, 85, 200]
})
# Inner join (padrão) — apenas user_ids que correspondem
result = pd.merge(users, orders, on='user_id')
print(result)
# user_id name order_id amount
# 0 1 Alice O001 120
# 1 2 Bob O002 85
# 2 2 Bob O003 200
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o exemplo. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
3. Os 4 Tipos de Junção
(1) Diagrama Mermaid
graph TB
subgraph Inner["inner — manter apenas correspondências"]
I1["user_id 1: Alice+O001"]
I2["user_id 2: Bob+O002"]
I3["user_id 2: Bob+O003"]
end
subgraph Left["left — manter todas as linhas da esquerda"]
L1["user_id 1: Alice+O001"]
L2["user_id 2: Bob+O002"]
L3["user_id 2: Bob+O003"]
L4["user_id 3: Charlie+NaN"]
L5["user_id 4: Carol+NaN"]
end
subgraph Right["right — manter todas as linhas da direita"]
R1["user_id 1: Alice+O001"]
R2["user_id 2: Bob+O002"]
R3["user_id 2: Bob+O003"]
end
subgraph Outer["outer — manter todas as linhas de ambos os lados"]
O1["user_id 1: Alice+O001"]
O2["user_id 2: Bob+O002"]
O3["user_id 2: Bob+O003"]
O4["user_id 3: Charlie+NaN"]
O5["user_id 4: Carol+NaN"]
end
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
(2) Comparação dos 4 Tipos de Junção
| Tipo | como | Tabela esquerda sem correspondência | Tabela direita sem correspondência | Contagem de linhas |
|---|---|---|---|---|
| inner | 'inner' | Descartadas | Descartadas | ≤mínimo(esquerda, direita) |
| left | 'left' | Mantidas (preenchidas com NaN) | Descartadas | = linhas da esquerda × correspondências |
| right | 'right' | Descartadas | Mantidas (preenchidas com NaN) | = linhas da direita × correspondências |
| outer | 'outer' | Mantidas (preenchidas com NaN) | Mantidas (preenchidas com NaN) | ≥máximo(esquerda, direita) |
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: Comparação dos 4 tipos de junção (Dificuldade ⭐⭐)
import pandas as pd
left = pd.DataFrame({
'id': [1, 2, 3],
'name': ['Alice', 'Bob', 'Charlie']
})
right = pd.DataFrame({
'id': [2, 3, 4],
'score': [85, 92, 78]
})
# Inner — somente id 2, 3 (ambas as tabelas os têm)
print("INNER:")
print(pd.merge(left, right, on='id', how='inner'))
# id name score
# 0 2 Bob 85
# 1 3 Charlie 92
# Left — todas as linhas da esquerda, direita preenche com NaN onde não há correspondência
print("\nLEFT:")
print(pd.merge(left, right, on='id', how='left'))
# id name score
# 0 1 Alice NaN ← sem correspondência na direita
# 1 2 Bob 85.0
# 2 3 Charlie 92.0
# Right — todas as linhas da direita
print("\nRIGHT:")
print(pd.merge(left, right, on='id', how='right'))
# id name score
# 0 2 Bob 85
# 1 3 Charlie 92
# 2 4 NaN 78 ← sem correspondência na esquerda
# Outer — todas as linhas de ambos os lados
print("\nOUTER:")
print(pd.merge(left, right, on='id', how='outer'))
# id name score
# 0 1 Alice NaN
# 1 2 Bob 85.0
# 2 3 Charlie 92.0
# 3 4 NaN 78.0
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
4. Merges com Múltiplas Chaves e Nomes de Colunas Diferentes
(1) Merge com Múltiplas Chaves
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
: Merge com múltiplas chaves (Dificuldade ⭐⭐)
import pandas as pd
sales = pd.DataFrame({
'region': ['North', 'North', 'South', 'South'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing'],
'revenue': [5000, 800, 2000, 600]
})
targets = pd.DataFrame({
'region': ['North', 'North', 'South', 'South'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing'],
'target': [4500, 1000, 2500, 500]
})
# Merge em múltiplas chaves
result = pd.merge(sales, targets, on=['region', 'category'])
print(result)
# region category revenue target
# 0 North Electronics 5000 4500
# 1 North Clothing 800 1000
# 2 South Electronics 2000 2500
# 3 South Clothing 600 500
# Calcular taxa de cumprimento
result['achievement'] = (result['revenue'] / result['target'] * 100).round(1)
print(result[['region', 'category', 'achievement']])
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
(2) Merge com Nomes de Colunas Diferentes
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
: left_on/right_on (Dificuldade ⭐⭐)
import pandas as pd
employees = pd.DataFrame({
'emp_id': [1, 2, 3],
'name': ['Alice', 'Bob', 'Charlie']
})
salaries = pd.DataFrame({
'employee_id': [1, 2, 3],
'salary': [75000, 92000, 68000]
})
# Nomes de colunas diferem: emp_id vs employee_id
result = pd.merge(
employees, salaries,
left_on='emp_id', right_on='employee_id',
how='inner'
)
print(result)
# emp_id name employee_id salary
# 0 1 Alice 1 75000
# 1 2 Bob 2 92000
# 2 3 Charlie 3 68000
# Remover coluna redundante
result = result.drop(columns='employee_id')
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
5. sufixos e indicador
(1) Lidando com Conflitos de Nomes de Colunas
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: sufixos para nomes de colunas duplicados (Dificuldade ⭐)
import pandas as pd
df1 = pd.DataFrame({
'id': [1, 2],
'value': [100, 200]
})
df2 = pd.DataFrame({
'id': [1, 2],
'value': [300, 400]
})
# Sufixos padrão: _x e _y
result = pd.merge(df1, df2, on='id')
print(result)
# id value_x value_y
# 0 1 100 300
# 1 2 200 400
# Sufixos personalizados
result2 = pd.merge(df1, df2, on='id', suffixes=('_esquerdo', '_direito'))
print(result2)
# id value_esquerdo value_direito
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
(2) indicador para Diagnosticar Fontes da Junção
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: indicador para rastrear origens das linhas (Dificuldade ⭐⭐)
import pandas as pd
left = pd.DataFrame({'id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie']})
right = pd.DataFrame({'id': [2, 3, 4], 'score': [85, 92, 78]})
# Adiciona a coluna _merge para ver de onde veio cada linha
result = pd.merge(left, right, on='id', how='outer', indicator=True)
print(result)
# id name score _merge
# 0 1 Alice NaN left_only
# 1 2 Bob 85.0 both
# 2 3 Charlie 92.0 both
# 3 4 NaN 78.0 right_only
# Filtrar pela fonte da junção
only_left = result[result['_merge'] == 'left_only']
print(f"Somente na tabela esquerda: {len(only_left)} linhas") # 1 (Alice)
# Validar a integridade dos dados — encontrar registros não combinados
unmatched = result[result['_merge'] != 'both']
print(f"Registros não combinados: {len(unmatched)}") # 2
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
6. Validar e Cross Join
(1) Validar para Verificar Relações de Merge
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
: validação (Dificuldade ⭐⭐)
import pandas as pd
users = pd.DataFrame({'user_id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie']})
orders = pd.DataFrame({'order_id': ['O1', 'O2', 'O3'], 'user_id': [1, 2, 2]})
# Validar: cada usuário deve ter no máximo um pedido (1:1)
# Isso FALHARÁ porque o usuário 2 tem 2 pedidos
try:
result = pd.merge(users, orders, on='user_id', validate='1:1')
except Exception as e:
print(f"Falha na validação: {e}")
# Validar: cada usuário pode ter muitos pedidos (1:m) — passa
result = pd.merge(users, orders, on='user_id', validate='1:m')
print(result) # OK
# Tipos de validação: '1:1', '1:m', 'm:1', 'm:m'
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
(2) Cross Join
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
: cross join (Dificuldade ⭐)
import pandas as pd
colors = pd.DataFrame({'color': ['Red', 'Blue']})
sizes = pd.DataFrame({'size': ['S', 'M', 'L']})
# Toda combinação (produto cartesiano)
result = pd.merge(colors, sizes, how='cross')
print(result)
# color size
# 0 Red S
# 1 Red M
# 2 Red L
# 3 Blue S
# 4 Blue M
# 5 Blue L
# 6 linhas = 2 cores × 3 tamanhos
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da versão do pandas.
7. mesclagem vs join
| Recurso | mesclagem | join |
|---|---|---|
| Estilo de chamada | pd.merge(df1, df2) |
df1.join(df2) |
| Chave de junção padrão | Coluna especificada por on | Índice |
| Junção de múltiplas colunas | ✅ on=['a','b'] | ❌ Apenas índice |
| Tipos de junção | inner/left/right/outer | left (padrão) |
| Flexibilidade | Alta | Baixa |
| Melhor para | Junções de uso geral | Alinhamento de índices |
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: atalho de join (Dificuldade ⭐)
import pandas as pd
df1 = pd.DataFrame({'A': [1, 2, 3]}, index=['x', 'y', 'z'])
df2 = pd.DataFrame({'B': [4, 5]}, index=['x', 'y'])
# join usa o Índice por padrão
result = df1.join(df2) # how='left' por padrão
print(result)
# A B
# x 1 4.0
# y 2 5.0
# z 3 NaN ← a junção left mantém todas as linhas da esquerda
# join com parâmetro how
result2 = df1.join(df2, how='inner')
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
8. Exemplo Completo: Análise de Junção de Três Tabelas
▶ Exemplo
> **Saída:** Execute em um ambiente local do Python (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e siga along. Os valores reais podem variar ligeramente dependendo da versão do pandas.
: Fluxo de trabalho de mesclagem de três tabelas (Dificuldade ⭐⭐⭐)
import pandas as pd
# ============================================
# Exemplo abrangente: mesclagem de 3 tabelas
# usuários + pedidos + produtos → análise completa
# ============================================
# 1. Tabela de usuários
users = pd.DataFrame({
'user_id': [1, 2, 3, 4, 5],
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'region': ['Norte', 'Sul', 'Leste', 'Norte', 'Oeste']
})
# 2. Tabela de pedidos
orders = pd.DataFrame({
'order_id': ['O001', 'O002', 'O003', 'O004', 'O005', 'O006'],
'user_id': [1, 2, 2, 3, 1, 5],
'product_id': ['P01', 'P02', 'P03', 'P01', 'P04', 'P02'],
'quantity': [2, 1, 3, 1, 5, 2]
})
# 3. Tabela de produtos
products = pd.DataFrame({
'product_id': ['P01', 'P02', 'P03', 'P04', 'P05'],
'product_name': ['Laptop', 'Phone', 'Tablet', 'Mouse', 'Keyboard'],
'price': [999, 699, 349, 29, 79]
})
# Passo 1: pedidos + produtos → detalhes do pedido com preço
order_details = pd.merge(orders, products, on='product_id', how='left')
order_details['total'] = order_details['quantity'] * order_details['price']
# Passo 2: order_details + usuários → visão completa
full = pd.merge(order_details, users, on='user_id', how='left')
# Passo 3: Análise por região
region_sales = full.groupby('region')['total'].sum().sort_values(ascending=False)
print("=== Vendas por Região ===")
print(region_sales)
# Passo 4: Melhores clientes
top_customers = full.groupby('name')['total'].sum().sort_values(ascending=False)
print("\n=== Melhores Clientes ===")
print(top_customers)
# Passo 5: Produtos sem correspondência (nos produtos, mas nunca pedidos)
unmatched = pd.merge(
products, orders[['product_id']].drop_duplicates(),
on='product_id', how='left', indicator=True
)
never_ordered = unmatched[unmatched['_merge'] == 'left_only']
print(f"\n=== Produtos Nunca Pedidos ===")
print(never_ordered[['product_id', 'product_name']])
> **Saída:** Execute em um ambiente local do Python (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e siga along. Os valores reais podem variar ligeramente dependendo da versão do pandas.
❓ Perguntas Frequentes
pd.merge(a, b, left_on='emp_id', right_on='employee_id'). Após a junção, ambas as colunas são mantidas — use drop para remover a redundante. Se uma chave for um Índice, use left_index=True / right_index=True.df.rename(columns={'value': 'value_a'}).📖 Resumo
- mesclagem possui 4 tipos de junção: interna (somente correspondências) / esquerda (mantém todas as linhas da esquerda) / direita (mantém todas as linhas da direita) / externa (mantém tudo de ambos os lados)
- Merge com múltiplas chaves usa on=['chave1','chave2']; nomes de colunas diferentes usam left_on/right_on
- suffixes lida com nomes de colunas duplicados; indicator rastreia a origem das linhas
- validate verifica relacionamentos de junção (1:1 / 1:m / m:1) para evitar surpresas
- junção cruzada produz um produto cartesiano — use somente com tabelas pequenas
- join é um atalho para mesclagem (padrão é Índice + junção à esquerda)
- Sempre verifique a contagem de linhas e o indicator após a junção para confirmar a correção
📝 Exercícios
- Básico (Dificuldade ⭐): Crie uma tabela de alunos e uma tabela de notas (compartilhando student_id). Realize inner/left/outer joins e observe como a contagem de linhas muda.
- Intermediário (Dificuldade ⭐⭐): Crie uma tabela de departamentos (dept_id/dept_name) e uma tabela de funcionários (emp_id/dept_id/salary). Mescle-as, calcule o salário médio por departamento e use o indicador para encontrar departamentos sem funcionários.
- Desafio (Dificuldade ⭐⭐⭐): Simule três tabelas (usuários/pedidos/produtos) e complete o seguinte: inner join para uní-las → calcular o gasto total por usuário → left join para encontrar usuários com gasto zero → validar para verificar relacionamentos → indicador para diagnosticar origens.
← Previous: GroupBy and Aggregation · Next: Concat and Append →