Pandas: Índice Múltiplo
Última atualização: 2026-08-26
Quando os dados possuem uma estrutura hierárquica — agrupados por cidade e depois por produto, ou por ano e depois por trimestre — um índice de nível único não é suficiente. O MultiIndex (indexação hierárquica) oferece uma indexação em camadas para dados estruturados em camadas: selecione o primeiro nível e depois aprofunde-se no segundo, navegando exatamente como uma hierarquia de pastas. Esta seção aborda a criação, seleção, reordenação e combinação de MultiIndex com stack/unstack.
1. O Que Você Aprenderá
- ❶ Conceitos de MultiIndex
- ❷ Métodos de criação (from_tuples / from_product / from_frame)
- ❸ Seleção hierárquica (loc / xs)
- ❹ swaplevel / reorder_levels
- ❺ sort_index e achatamento (flattening)
2. Dados de Vendas de Múltiplas Cidades e Categorias do Bob
(1) Ponto de Dificuldade: Resultados Agrupados em Dois Níveis São Difíceis de Selecionar
Após agrupar por cidade + categoria, Bob acaba com um índice de dois níveis e não sabe como selecionar uma cidade específica:
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
print(result)
# city category
# Chicago Clothing 400
# Electronics 2000
# LA Clothing 600
# Electronics 3000
# NYC Clothing 800
# Electronics 5000
# Como selecionar apenas NYC?
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.
(2) Solução: Seleção com MultiIndex
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.
: Seleção com MultiIndex (Dificuldade ⭐)
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
# Selecionar pelo rótulo MultiIndex
print(result.loc['NYC'])
# category
# Clothing 800
# Electronics 5000
# Selecionar específica (cidade, categoria)
print(result.loc[('NYC', 'Electronics')]) # 5000
# xs: seleção de seção transversal
print(result.xs('Electronics', level='category'))
# city
# Chicago 2000
# LA 3000
# NYC 5000
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.
3. Criando um MultiIndex
(1) Três Métodos de Criação
▶ Exemplo
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: Métodos de Criação de MultiIndex (Dificuldade ⭐⭐)
import pandas as pd
# 1. from_tuples — explicit pairs
index = pd.MultiIndex.from_tuples([
('NYC', 'Electronics'), ('NYC', 'Clothing'),
('LA', 'Electronics'), ('LA', 'Clothing')
], names=['city', 'category'])
df1 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index)
print("from_tuples:")
print(df1)
# 2. from_product — cartesian product (most common)
index2 = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago'], ['Electronics', 'Clothing']],
names=['city', 'category']
)
df2 = pd.DataFrame({
'sales': [5000, 800, 3000, 600, 2000, 400]
}, index=index2)
print("\nfrom_product:")
print(df2)
# 3. from_frame — from DataFrame
df_index = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing']
})
index3 = pd.MultiIndex.from_frame(df_index)
df3 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index3)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
4. Seleção Hierárquica
(1) loc vs xs
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Métodos de Seleção Hierárquica (Dificuldade ⭐⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Electronics', 'Clothing', 'Home']],
names=['city', 'category']
)
df = pd.DataFrame({
'sales': [5000, 800, 300, 3000, 600, 200],
'profit': [1500, 200, 50, 800, 100, 30]
}, index=index)
# loc: seleciona o primeiro nível
print(df.loc['NYC'])
# loc: seleciona ambos os níveis
print(df.loc[('NYC', 'Electronics')])
# xs: seção transversal (seleciona em QUALQUER nível)
print(df.xs('Electronics', level='category'))
# Retorna os dados de Eletrônica de todas as cidades
# xs com drop_level=False
print(df.xs('NYC', level='city', drop_level=False))
# Seleção parcial com slice
print(df.loc[('NYC', slice(None)), :]) # todas as categorias em NYC
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
(2) Comparação loc vs xs
| Característica | loc | xs |
|---|---|---|
| Selecionar um nível | ✅ loc['NYC'] |
✅ xs('NYC', level=0) |
| Selecionar segundo nível | Complexo loc[(slice(None),'Elec')] |
Simples xs('Elec', level=1) |
| Vários níveis de uma vez | ✅ loc[('NYC','Elec')] |
❌ Apenas um nível por vez |
| Preservar nível | Preservado por padrão | Controlado por drop_level |
5. swaplevel / sort_index
(1) Trocando Níveis
▶ Exemplo
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: swaplevel para Reordenar Níveis (Dificuldade ⭐⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'quarter']
)
df = pd.DataFrame({
'sales': [1200, 1300, 1100, 1400, 800, 900, 700, 1000]
}, index=index)
# Troca os níveis
df_swapped = df.swaplevel()
print(df_swapped.head(4))
# city quarter
# Q1 NYC 1200
# Q2 NYC 1300
# Q3 NYC 1100
# Q4 NYC 1400
# sort_index após a troca (importante!)
df_sorted = df_swapped.sort_index()
print(df_sorted.head(4))
# reorder_levels: reordenação arbitrária
df_reordered = df.reorder_levels(['quarter', 'city'])
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
6. MultiIndex com stack/unstack
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: stack/unstack com MultiIndex (Dificuldade ⭐⭐)
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing'],
'Q1': [1200, 400, 800, 200],
'Q2': [1300, 500, 900, 300]
})
# set_index → DataFrame MultiIndex
multi_df = df.set_index(['city', 'category'])
print(multi_df)
# unstack: nível category → colunas
wide = multi_df.unstack(level='category')
print(wide)
# Q1 Q2
# category Clothing Electronics Clothing Electronics
# city
# LA 200 800 300 900
# NYC 400 1200 500 1300
# stack: colunas → nível do índice (inverso)
back = wide.stack(level='category')
print(back)
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
7. Achatamento com reset_index
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Achatamento de um MultiIndex (Dificuldade ⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2']],
names=['city', 'quarter']
)
df = pd.DataFrame({'sales': [1200, 1300, 800, 900]}, index=index)
# reset_index: MultiIndex → colunas regulares
flat = df.reset_index()
print(flat)
# city quarter sales
# 0 NYC Q1 1200
# 1 NYC Q2 1300
# 2 LA Q1 800
# 3 LA Q2 900
# Achatamento do MultiIndex das colunas
wide = df.unstack()
flat_cols = wide.copy()
flat_cols.columns = [f'{a}_{b}' for a, b in flat_cols.columns]
print(flat_cols)
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
8. Exemplo Completo: Análise de Vendas Multi-Cidade, Multi-Categoria
(6) ▶ Estrutura Hierárquica MultiIndex
graph TB
A[DataFrame] --> B[Outer index: city]
B --> C[NYC]
B --> D[LA]
B --> E[Chicago]
C --> F[Inner index: category]
F --> G[Electronics]
F --> H[Clothing]
F --> I[Home]
D --> J[Electronics]
D --> K[Clothing]
E --> L[Electronics]
E --> M[Clothing]
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Análise de Fluxo de Trabalho MultiIndex Completo (Dificuldade ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Exemplo abrangente: análise de vendas
# multi-cidade multi-categoria com MultiIndex
# ============================================
# 1. Criar dados hierárquicos
np.random.seed(42)
index = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago', 'Houston'],
['Electronics', 'Clothing', 'Home'],
['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'category', 'quarter']
)
df = pd.DataFrame({
'sales': np.random.randint(100, 5000, 48),
'profit': np.random.randint(10, 1500, 48)
}, index=index)
# 2. Visualizar estrutura
print(f"Níveis: {df.index.names}")
print(f"Formato: {df.shape}")
# 3. Selecionar por cidade
print("\n=== Vendas de NYC ===")
print(df.loc['NYC', 'sales'].unstack(level='quarter'))
# 4. xs: todos os Electronics nas cidades
print("\n=== Electronics por Cidade ===")
elec = df.xs('Electronics', level='category')[['sales']]
print(elec.unstack(level='quarter'))
# 5. swaplevel → sort → select
df2 = df.swaplevel('city', 'quarter').sort_index()
print("\n=== Q1 em todas as cidades ===")
print(df2.loc['Q1'])
# 6. Unstack para relatório amplo
report = df['sales'].unstack(level=['category', 'quarter'])
print("\n=== Relatório Amplo (primeiras 2 cidades) ===")
print(report.head(2))
# 7. Reset para exportação
flat = df.reset_index()
print(f"\nFormato achatado: {flat.shape}")
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
❓ Perguntas Frequentes
Q: Quando devo usar um MultiIndex? R: Quando seus dados têm uma estrutura hierárquica natural — cidade + categoria, ano + trimestre, departamento + equipe. Agrupar por múltiplas colunas automaticamente produz um MultiIndex, e o set_index com múltiplas colunas também faz isso. Para dados simples, um índice de nível único é mais conveniente — não force um MultiIndex onde ele não é necessário.
Q: Qual é a diferença entre xs e loc? R: O xs pode selecionar em qualquer nível (ex: selecionar diretamente category='Electronics' sem selecionar a cidade primeiro). O loc só pode selecionar do nível mais externo para o interno (cidade primeiro, depois categoria). O xs é mais flexível, mas não pode selecionar múltiplos níveis simultaneamente; o loc suporta
loc[('NYC','Electronics')], mas a sintaxe de seleção do segundo nível é mais complexa.
Q: O swaplevel altera os dados? R: Não — o swaplevel apenas troca a ordem dos níveis do índice; os dados em si permanecem inalterados. No entanto, você deve chamar sort_index() após a troca, caso contrário seleções subsequentes podem produzir erros ou desempenho ruim. Fluxo de trabalho recomendado: swaplevel → sort_index → selecionar.
Q: Como achatar um MultiIndex? R: Para MultiIndex no nível das linhas → use
df.reset_index()para converter os níveis em colunas regulares. Para MultiIndex no nível das colunas → renomeie as colunas unindo os valores dos níveis:['_'.join(col) for col in df.columns]. Após o groupby, use as_index=False para evitar criar um MultiIndex (mas você perde a capacidade de seleção hierárquica).
Q: Como é o desempenho do MultiIndex? R: As consultas ao MultiIndex são O(1) (baseado em hash subjacente), então o desempenho é excelente. No entanto, a eficiência só é garantida após o sort_index() — uma seleção em um MultiIndex não ordenado pode degradar para O(n). Para grandes conjuntos de dados: chame sort_index() imediatamente após a criação, e use is_unique para verificar a unicidade.
Q: Para que serve get_level_values? R:
df.index.get_level_values('city')retorna todos os valores em um determinado nível (ex: ['NYC','NYC','LA','LA',...]). Útil para seleção condicional:df[df.index.get_level_values('city') == 'NYC']. Também funciona com groupby:df.groupby(df.index.get_level_values(0)).sum().
Q: Como escolher entre from_product e from_tuples? R: O from_product gera o produto cartesiano (todas as combinações) — ideal para dados de grade completa (3 cidades × 4 trimestres = 12 linhas). O from_tuples especifica combinações exatas — ideal para dados incompletos (algumas cidades faltando certas categorias). O from_product é mais conciso; o from_tuples é mais preciso.
📖 Resumo
- O MultiIndex fornece indexação hierárquica para dados em camadas, permitindo navegação em estilo de pastas
- Criação: from_product (grade completa), from_tuples (combinações específicas), from_frame (converter de DataFrame)
- Seleção: loc vai de fora para dentro; xs faz seleção cruzada em qualquer nível
- swaplevel/reorder_levels trocam a ordem dos níveis — sempre chame sort_index() depois
- unstack move níveis de linhas para colunas; stack move níveis de colunas para linhas — ambos funcionam naturalmente com MultiIndex
- reset_index achata o MultiIndex de nível de linha; renomear colunas achata o MultiIndex de nível de coluna
📝 Exercícios
- Básico (Dificuldade ⭐): Use from_product para criar um DataFrame MultiIndex com 3 cidades × 2 categorias, em seguida use loc e xs para selecionar uma cidade específica e uma categoria específica, respectivamente.
- Intermediário (Dificuldade ⭐⭐): Crie um DataFrame MultiIndex cidade × trimestre, use swaplevel para trocar os níveis → sort_index → unstack para uma tabela ampla → stack de volta e verifique a conversão completa.
- Desafio (Dificuldade ⭐⭐⭐): Simule 48 linhas de dados com 4 regiões × 3 categorias × 4 trimestres, em seguida complete: seleção cruzada de nível com xs → reordenação com swaplevel → unstack multinível para formato amplo → reset_index para exportação → groupby para reagregar.