Pandas: Transformação de Dados
Última atualização: 2026-08-26
A etapa final da limpeza de dados é a "transformação" — remodelar dados brutos no formato que sua análise requer: converter pontuações em notas, preços em descontos e encadear operações em múltiplas etapas. As funções map / apply / pipe / replace / assign / transform do Pandas formam um conjunto de ferramentas de transformação em múltiplos níveis, que abrange desde elementos únicos até tabelas inteiras. Esta seção aborda os casos de uso e armadilhas de desempenho de cada método.
1. What You Will Learn
- ❶ map element mapping
- ❷ apply row/column functions
- ❸ pipe chaining
- ❹ replace value replacement
- ❺ assign new columns
- ❻ transform group transformation
2. Conversão de Notas da Alice
(1) O Problema: Convertendo Notas de 0-100 para Letras ABCD
Alice tem as notas de 100 alunos em uma escala de 0-100 e precisa convertê-las para letras:
import pandas as pd
scores = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'score': [92, 78, 65, 88, 45]
})
# Como converter 0-100 → A/B/C/D/F?
> **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) Solução: map + Mapeamento por Dicionário
▶ 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.
: mapeamento por dicionário (Dificuldade ⭐)
import pandas as pd
scores = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'score': [92, 78, 65, 88, 45]
})
# Passo 1: Definir a função de mapeamento de nota
def score_to_grade(s):
if s >= 90: return 'A'
elif s >= 80: return 'B'
elif s >= 70: return 'C'
elif s >= 60: return 'D'
else: return 'F'
# Passo 2: Aplicar com map
scores['grade'] = scores['score'].map(score_to_grade)
print(scores)
# name score grade
# 0 Alice 92 A
# 1 Bob 78 C
# 2 Charlie 65 D
# 3 Carol 88 B
# 4 David 45 F
> **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.
3. Mapeamento de Elementos com map
(1) Três Maneiras de Usar o map
▶ 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 um pouco dependendo da versão do pandas.
: Três abordagens de mapeamento com map (Dificuldade ⭐⭐)
import pandas as pd
s = pd.Series([1, 2, 3, 4, 5])
# 1. Mapeamento com Dicionário
category_map = {1: 'Baixo', 2: 'Baixo', 3: 'Médio', 4: 'Alto', 5: 'Alto'}
print(s.map(category_map))
# 0 Baixo
# 1 Baixo
# 2 Médio
# 3 Alto
# 4 Alto
# 2. Mapeamento com Função
print(s.map(lambda x: x ** 2))
# 0 1
# 1 4
# 2 9
# 3 16
# 4 25
# 3. Mapeamento com Series (alinhamento por índice)
other = pd.Series({1: 'Um', 2: 'Dois', 3: 'Três'})
print(s.map(other))
# 0 NaN ← 1 existe em other → 'Um'
# 1 Dois
# 2 Três
# 3 NaN ← 4 não está em other → NaN
# 4 NaN
> **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 um pouco dependendo da versão do pandas.
(2) Quando Usar o map
| Característica | map | apply |
|---|---|---|
| Opera em | Elementos individuais da Series | Linhas/colunas da Series / DataFrame |
| Entrada | Dicionário/função/Series | Função |
| Retorna | Series (mapeamento um-para-um) | Qualquer tipo |
| Desempenho | Rápido (busca em dicionário O(1)) | Mais lento (chamadas por elemento/linha/coluna) |
| Melhor para | Mapeamento um-para-um | Lógica complexa/operações em múltiplas colunas |
4. Aplicar Funções de Linha/Coluna
(1) Series apply
▶ Exemplo
> **Saída:** Execute 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 versão do pandas.
: Series apply (Dificuldade ⭐⭐)
import pandas as pd
s = pd.Series(['alice@example.com', 'bob@company.org', 'charlie@uni.edu'])
# Extrair domínio do e-mail
domains = s.apply(lambda email: email.split('@')[1])
print(domains)
# 0 example.com
# 1 company.org
# 2 uni.edu
# Lógica complexa: classificar tipo de e-mail
def classify_email(email):
domain = email.split('@')[1]
if '.edu' in domain: return 'Acadêmico'
elif '.org' in domain: return 'Sem fins lucrativos'
else: return 'Comercial'
email_type = s.apply(classify_email)
print(email_type)
# 0 Comercial
# 1 Sem fins lucrativos
# 2 Acadêmico
> **Saída:** Execute 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 versão do pandas.
(2) DataFrame apply
▶ Exemplo
> **Saída:** Execute 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 versão do pandas.
: DataFrame apply operações de linha/coluna (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'math': [85, 92, 78, 88, 65],
'english': [90, 85, 82, 75, 70],
'science': [88, 95, 80, 90, 60]
}, index=['Alice', 'Bob', 'Charlie', 'Carol', 'David'])
# Aplicar função em cada COLUNA (axis=0, padrão)
print(df.apply(np.mean))
# math 81.6
# english 80.4
# science 82.6
# Aplicar função em cada LINHA (axis=1)
df['average'] = df.apply(lambda row: row.mean(), axis=1)
print(df['average'])
# Alice 87.67
# Bob 90.67
# Charlie 80.00
# Carol 84.33
# David 65.00
# Retornar Series por linha (mais estruturado)
def score_report(row):
return pd.Series({
'total': row.sum(),
'avg': row.mean(),
'max': row.max(),
'min': row.min()
})
report = df[['math', 'english', 'science']].apply(score_report, axis=1)
print(report)
> **Saída:** Execute 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 versão do pandas.
5. Encadeamento com pipe
(1) Fluxo de Dados Encadeado
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem 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.
: operações de pipeline com pipe (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
# Dados brutos
df = pd.DataFrame({
'name': [' Alice ', 'BOB', 'Charlie', ' carol ', 'David'],
'age': [28, -1, 25, 30, 999],
'salary': [75000, np.nan, 68000, 88000, np.nan]
})
# Passo a passo sem pipe (chamadas aninhadas)
# resultado = atribuir_depto(substituir_invalidos(limpar_nomes(df)))
# Com pipe — fluxo legível da esquerda para a direita
def limpar_nomes(df):
df = df.copy()
df['name'] = df['name'].str.strip().str.title()
return df
def substituir_invalidos(df):
df = df.copy()
df.loc[df['age'] < 0, 'age'] = np.nan
df.loc[df['age'] > 150, 'age'] = np.nan
return df
def preencher_salario(df):
df = df.copy()
df['salary'] = df['salary'].fillna(df['salary'].median())
return df
resultado = (df
.pipe(limpar_nomes)
.pipe(substituir_invalidos)
.pipe(preencher_salario)
)
print(resultado)
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem 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 pipe vs apply
| Característica | pipe | apply |
|---|---|---|
| Entrada | DataFrame inteiro | Linha/coluna/Série |
| Retorna | DataFrame (transformado) | Qualquer tipo |
| Propósito | Pipeline de múltiplas etapas | Operações em único elemento/linha/coluna |
| Encadeamento | ✅ Suportado nativamente | ❌ Requer aninhamento |
| Legibilidade | Alta (leitura de cima para baixo) | Média |
6. Substituição de Valor
(1) Substituição Exata
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
: substituição de valor (Dificuldade ⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'status': ['active', 'inactive', 'pending', 'active', 'unknown'],
'priority': [1, 2, 3, 1, 99]
})
# Substituição de valor único
df['status'] = df['status'].replace('unknown', 'inactive')
print(df['status'])
# Substituição de múltiplos valores (dicionário)
df['status'] = df['status'].replace({
'active': 'Active',
'inactive': 'Inactive',
'pending': 'Pending'
})
print(df['status'])
# Substituir valor sentinela inválido
df['priority'] = df['priority'].replace(99, np.nan)
print(df['priority'])
# Substituição com regex
text = pd.Series(['Phone: 555-0100', 'Email: test@example.com'])
cleaned = text.str.replace(r'[\d-]+', '[REDACTED]', regex=True)
print(cleaned)
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
(2) Comparação replace vs map
| Característica | replace | map |
|---|---|---|
| Valores não encontrados | Mantidos inalterados | Tornam-se NaN |
| Substituição de múltiplos valores | ✅ Dicionário | ✅ Dicionário |
| Regex | ✅ regex=True | ❌ |
| Melhor para | Substituir valores específicos | Mapeamento completo |
7. Atribuir Novas Colunas
(1) Encadeando Múltiplas Novas Colunas
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem 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.
: atribuir novas colunas (Dificuldade ⭐⭐)
import pandas as pd
df = pd.DataFrame({
'price': [100, 200, 150, 80, 300],
'quantity': [5, 3, 10, 8, 2],
'discount': [0.1, 0.2, 0.0, 0.15, 0.05]
})
# Adiciona múltiplas colunas em um único encadeamento
result = (df
.assign(
discounted_price=lambda x: x['price'] * (1 - x['discount']),
total=lambda x: x['discounted_price'] * x['quantity'],
is_bulk=lambda x: x['quantity'] >= 5
)
)
print(result)
# price quantity discount discounted_price total is_bulk
# 0 100 5 0.10 90.0 450.0 True
# 1 200 3 0.20 160.0 480.0 False
# 2 150 10 0.00 150.0 1500.0 True
# 3 80 8 0.15 68.0 544.0 True
# 4 300 2 0.05 285.0 570.0 False
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem 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.
assign, use lambda x: para referenciar o DataFrame atual. Você pode referenciar colunas criadas anteriormente na mesma chamada de assign (por exemplo, total referencia discounted_price). Isso o torna mais adequado para encadeamento do que a sintaxe df['new_col'] = ....
8. transform Transformação de Grupo
(1) transform Preserva o Formato Original
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui 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.
: transform padronização por grupo (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'student': ['Alice', 'Alice', 'Bob', 'Bob', 'Charlie', 'Charlie'],
'subject': ['Math', 'English', 'Math', 'English', 'Math', 'English'],
'score': [85, 90, 92, 88, 78, 82]
})
# Normalização Z-score dentro de cada aluno
df['z_score'] = df.groupby('student')['score'].transform(
lambda x: (x - x.mean()) / x.std()
)
print(df)
# Preencher com a média do grupo (em vez da média global)
df['score_filled'] = df.groupby('student')['score'].transform('mean')
print(df[['student', 'score', 'score_filled']])
# Ranking dentro do grupo
df['rank_in_class'] = df.groupby('subject')['score'].transform('rank', method='min')
print(df[['student', 'subject', 'score', 'rank_in_class']])
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui 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 transform vs apply
| Recurso | transform | apply |
|---|---|---|
| Formato de saída | Igual à entrada | Pode variar |
| Após groupby | Retorna resultado de mesmo tamanho por grupo | Retorna qualquer resultado por grupo |
| Agregação | ❌ Não pode reduzir | ✅ Pode |
| Broadcasting | ✅ Auto-transmite para as linhas originais | ❌ |
| Usos comuns | Padronização/preenchimento/ranking | Agregação/computação complexa |
9. Escolhendo o Método de Transformação Certo
(1) Referência Rápida para 6 Métodos de Transformação
graph TB
Q["Precisa transformar dados?"] --> TYPE{"Tipo de transformação?"}
TYPE -->|"Mapeamento um-para-um"| MAP["map()"]
TYPE -->|"Função por linha/coluna"| APPLY["apply()"]
TYPE -->|"Pipeline de múltiplas etapas"| PIPE["pipe()"]
TYPE -->|"Substituir valores específicos"| REPLACE["replace()"]
TYPE -->|"Adicionar novas colunas"| ASSIGN["assign()"]
TYPE -->|"Transformação no nível do grupo"| TRANS["transform()"]
MAP --> NOTE1["✅ dict/função<br>❌ não mapeado → NaN"]
APPLY --> NOTE2["⚠️ Lento para DFs grandes<br>✅ Flexível"]
PIPE --> NOTE3["✅ Encadeamento legível<br>✅ Etapas reutilizáveis"]
REPLACE --> NOTE4["✅ Não correspondidos mantidos<br>✅ Suporte a regex"]
ASSIGN --> NOTE5["✅ Amigável ao encadeamento<br>✅ Múltiplas colunas"]
TRANS --> NOTE6["✅ Saída de mesma forma<br>✅ Broadcast de grupo"]
> **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 tutorial. Os valores reais podem variar ligeiramente dependendo da versão do pandas.
(2) Ranking de Desempenho
| Classificação | Método | Desempenho | Razão |
|---|---|---|---|
| 1 | Operações vetorizadas | Mais rápido | Executado em nível de C, sem laço Python |
| 2 | map (dicionário) | Rápido | Busca O(1) |
| 3 | replace | Rápido | Otimizado internamente |
| 4 | map (função) | Médio | Chamadas de função por elemento |
| 5 | apply | Lento | Chamadas Python por linha/coluna |
| 6 | apply + lógica complexa | Mais lento | Sobrecarga da função composta |
df['col'] * 2, não escreva df['col'].apply(lambda x: x * 2). Operações vetorizadas são 10-100 vezes mais rápidas que o apply.
10. Exemplo Completo: Pipeline de Transformação de Dados de E-commerce
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui 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.
: Pipeline completo de transformação de dados (Dificuldade ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Exemplo abrangente: pipeline de
# transformação de dados de e-commerce
# ============================================
# 1. Dados brutos
np.random.seed(42)
df = pd.DataFrame({
'product': [f'Item_{i:03d}' for i in range(1, 21)],
'category': np.random.choice(['Electronics', 'Clothing', 'Home'], 20),
'price': np.round(np.random.uniform(10, 500, 20), 2),
'cost': np.round(np.random.uniform(5, 250, 20), 2),
'quantity_sold': np.random.randint(1, 100, 20),
'rating': np.round(np.random.uniform(2.0, 5.0, 20), 1)
})
# 2. Pipeline passo a passo com pipe
def add_margin(df):
df = df.copy()
df['margin_pct'] = ((df['price'] - df['cost']) / df['price'] * 100).round(1)
return df
def categorize_price(df):
df = df.copy()
df['price_tier'] = df['price'].map(
lambda p: 'Budget' if p < 50 else ('Mid' if p < 200 else 'Premium')
)
return df
def add_revenue(df):
return df.assign(
revenue=lambda x: x['price'] * x['quantity_sold'],
is_top_rated=lambda x: x['rating'] >= 4.0
)
def normalize_rating(df):
df = df.copy()
df['rating_zscore'] = df.groupby('category')['rating'].transform(
lambda x: (x - x.mean()) / x.std()
)
return df
# 3. Executar o pipeline
result = (df
.pipe(add_margin)
.pipe(categorize_price)
.pipe(add_revenue)
.pipe(normalize_rating)
)
# 4. Resumo
print("=== Resultado da Transformação ===")
print(result[['product', 'category', 'price_tier', 'margin_pct', 'revenue', 'is_top_rated']].head(10))
print(f"\nReceita total: ${result['revenue'].sum():,.2f}")
print(f"Itens com melhor avaliação: {result['is_top_rated'].sum()}")
print(f"Distribuição por faixa de preço:\n{result['price_tier'].value_counts()}")
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não possui 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.
❓ Perguntas Frequentes
Q: Qual a diferença entre map e apply? R: map só funciona em uma Series, realizando um mapeamento um-para-um em cada elemento (aceitando um dicionário ou função). Valores não mapeados se tornam NaN. apply funciona tanto em Series quanto em DataFrame, suportando operações no nível de linha/coluna (axis=0/1) com tipos de retorno flexíveis. Use map para mapeamentos simples (mais rápido) e apply para lógica complexa (mais flexível).
Q: Por que apply é lento? R: apply é essencialmente um laço em Python — ele chama uma função Python uma vez por linha/coluna, e a sobrecarga de chamada de função é enorme. Operações vetorizadas são executadas no nível C sem essa sobrecarga. Regra prática: se você pode escrever
df['a'] + df['b'], não escrevadf.apply(lambda r: r['a']+r['b'], axis=1). apply é um último recurso.
Q: Qual a diferença entre pipe e apply? R: pipe opera no DataFrame inteiro, aceitando e retornando um DataFrame — ele é projetado para pipelines de múltiplos passos. apply opera em linhas/colunas/Series para computação por elemento ou por linha. O valor do pipe é a legibilidade — ele transforma
f3(f2(f1(df)))emdf.pipe(f1).pipe(f2).pipe(f3), onde a leitura de cima para baixo te dá a ordem de execução.
Q: Qual a diferença entre replace e map? R: A maior diferença é como eles tratam valores incompatíveis — replace mantém o valor original inalterado, enquanto map transforma valores incompatíveis em NaN. Use replace quando você só deseja trocar alguns valores específicos; use map para mapeamento completo onde cada valor deve ser transformado. replace também suporta substituição por regex (regex=True), o que map não suporta.
Q: Qual a diferença entre assign e df['col']=...? R: assign retorna um novo DataFrame (sem modificar o original), tornando-o ideal para encadeamento.
df['col']=...modifica diretamente. Dentro do assign, você pode usar lambda para referenciar colunas criadas no mesmo passo, o que a atribuição direta não pode fazer. Recomendação: use assign para operações encadeadas; para casos simples, a atribuição direta é suficiente.
Q: Qual a diferença entre transform e apply após groupby? R: transform retorna um resultado com o mesmo tamanho que o DataFrame original (distribuição dentro de cada grupo), enquanto apply pode retornar qualquer formato. Por exemplo: groupby + transform('mean') retorna a média do grupo para cada linha (tamanho inalterado), enquanto groupby + apply('mean') retorna uma média por grupo (tamanho = número de grupos). Use transform para preservar o formato original; use apply/agg para agregação.
Q: O que exatamente significa "vetorizado"? R: Vetorização significa usar operações internas do Pandas/NumPy em vez de loops em Python.
df['a'] + df['b']é vetorizado (computação no nível C sobre o array inteiro), enquantodf.apply(lambda r: r['a']+r['b'], axis=1)não é (chamadas Python linha por linha). Operações vetorizadas são 10-100 vezes mais rápidas. Use operações internas sempre que possível em vez de apply.
📖 Resumo
- map é para mapeamento de Series um para um; mapeamento por dicionário é o mais rápido; valores não correspondentes se tornam NaN
- apply lida com lógica complexa por linha/coluna — flexível mas lento; prefira operações vetorizadas quando possível
- pipe constrói pipelines de múltiplas etapas, transformando chamadas aninhadas em uma cadeia legível de cima para baixo
- replace troca valores específicos mantendo os não correspondentes inalterados; suporta regex
- assign adiciona colunas em uma cadeia; lambda pode referenciar colunas criadas na mesma etapa
- transform realiza transformações por grupo mantendo o formato original; ideal para padronização/preenchimento/ranking
- Classificação de desempenho: vetorizado > map(dicionário) > replace > map(função) > apply
📝 Exercícios
- Básico (Dificuldade ⭐): Crie uma Série de pontuações (0-100). Use
map+ um dicionário para classificar as pontuações em Alta(≥70)/Média(≥50)/Baixa(<50), depois usereplacepara mudar Média para Med. - Intermediário (Dificuldade ⭐⭐): Crie um DataFrame de funcionários (nome/salário/departamento). Use
pipepara construir um pipeline de 3 etapas: limpar o nome (strip+title) → adicionar uma coluna de salário líquido → sinalizar se cada funcionário ganha acima da média do seu departamento. - Desafio (Dificuldade ⭐⭐⭐): Crie um conjunto de dados de produtos com 20 linhas. Use
assignpara adicionar 3 colunas de uma vez (receita/margem/nível), usegroupby+transformpara calcular escores-z dentro de cada categoria e, finalmente, usepipepara combinar todas as etapas em um único pipeline.
← Previous: Handling Duplicate Data · Next: Group Aggregation →