🗂️ O Estado Inicial — 5 Fontes sem Padrão Comum
Todo projeto começa aqui: dados caóticos de múltiplas origens. A tentação é ir direto para o código. O engenheiro experiente gasta 30 minutos entendendo o estado inicial antes de escrever uma linha — e economiza horas de retrabalho.
Inventário Inicial — O que mapear
💡 Regra de Ouro
Nunca tente juntar fontes antes de entender cada uma individualmente. O JOIN parece funcionar mas os dados estão silenciosamente errados — e você só descobre quando o cliente faz uma pergunta que não fecha com o relatório do mês passado.
1️⃣ Passo 1 — Auditar Cada Fonte Individualmente
Aplicar os 4 eixos de auditoria em cada fonte separadamente antes de qualquer tentativa de junção. Cada fonte recebe seu próprio relatório — com problemas identificados e anotação do que precisará de transformação.
Resultado Esperado — Tabela de Diagnóstico
| Fonte | Linhas | Problemas | Chave Candidata |
|---|---|---|---|
| Stripe | 18.000 | JSON embutido em description | customer_email |
| HubSpot | 12.000 | 47% nulos em "source" | |
| Time Tracker | ~8.000 | 3 formatos de data incompatíveis | consultor_nome (normalizado) |
| QuickBooks | ~2.400 | Schema muda a cada mês | período (year_month) |
| Founder Tracker | ~500 | Datas misturadas (3 formatos) | client_id |
2️⃣ Passo 2 — Normalizar Tipos, Datas e Moedas
A etapa de transformação mais trabalhosa: converter tudo para um padrão único. Datas para ISO 8601, moedas para uma base comum, tipos para os corretos. Cada decisão de normalização precisa ser documentada explicitamente.
Normalização de Moedas — 3 Opções
Opção A — Taxa Fixa Atual
Use a taxa de câmbio do dia de hoje para converter todos os históricos.
Prós: simples, zero dependência externa. Contras: impreciso para análises históricas — $1.000 de 2020 não vale o mesmo que $1.000 de 2024.
Opção B — Taxa da Data da Transação via API
Buscar a taxa histórica exata para cada transação via API (ex: Open Exchange Rates, ECB).
Prós: máxima precisão histórica. Contras: dependência de API externa, latência, custo, possível indisponibilidade.
Opção C — Taxa Semanal Fornecida pelo Cliente ✓ Recomendada
O cliente fornece uma tabela de taxas semanais ou mensais. Você aplica a taxa do período correspondente.
Prós: alinhado com como o cliente controla internamente, sem dependência de API, razoavelmente preciso. Esta é a opção padrão para projetos de consultoria.
Normalização de Datas — Código Python
import re
from datetime import datetime
def normalizar_data(valor):
if pd.isna(valor):
return pd.NaT
s = str(valor).strip()
# Unix timestamp
if re.match(r'^\d{10}$', s):
return pd.to_datetime(int(s), unit='s')
# DD/MM/YYYY
if re.match(r'^\d{2}/\d{2}/\d{4}$', s):
return pd.to_datetime(s, format='%d/%m/%Y')
# YYYY-MM-DD ou datetime
return pd.to_datetime(s, errors='coerce')
df['date_normalized'] = df['data_original'].apply(normalizar_data)
# Sempre salvar a coluna original
# df['date_original'] permanece intacta
3️⃣ Passo 3 — Criar IDs de Junção entre Tabelas
Sem chave de junção confiável, qualquer análise cross-source é invenção. A maioria dos projetos reais não tem IDs universais — você precisa criar a chave a partir dos dados disponíveis.
✓ Estratégias de chave
- ✓Email normalizado — lowercase + strip() funciona para ~90% dos projetos B2B
- ✓Hash de campos compostos — MD5(nome + empresa + domínio) para deduplicação
- ✓ID fornecido pelo cliente — se existe, sempre preferir sobre derivados
- ✓Surrogate key sequencial — ID novo gerado na fase de processamento
✗ Armadilhas de junção
- ✗JOIN por nome — "Acme Corp." ≠ "ACME Corp" ≠ "Acme" — fuzzy match obrigatório
- ✗Fanout não-detectado — JOIN 1:N sem GROUP BY gera linhas duplicadas
- ✗JOIN sem validar integridade — sempre checar linhas sem match antes de prosseguir
Validação de Integridade Referencial
# Antes do JOIN, sempre verificar:
# Quantos registros do Stripe NÃO têm match no HubSpot?
sem_match = stripe_df[
~stripe_df['email_norm'].isin(hubspot_df['email_norm'])
]
print(f"Registros sem match: {len(sem_match)} ({len(sem_match)/len(stripe_df)*100:.1f}%)")
# Se > 10%: investigar antes de continuar
# Se < 2%: documentar e prosseguir com LEFT JOIN
4️⃣ Passo 4 — Eliminar Colunas Mortas
Colunas mortas são artefatos de migrações passadas — existem no dataset mas nunca são usadas, frequentemente com 95%+ de nulos ou valor único constante. Eliminá-las é obrigatório antes de entregar um warehouse limpo.
Critérios para Eliminar
- •Nulos > 95%: df.isnull().mean() > 0.95 — coluna essencialmente vazia
- •Variância zero: df['col'].nunique() == 1 — valor único constante (ex: "BR" em todas as linhas)
- •Nomenclatura de migração: colunas "_old", "_backup", "_v1", "_legacy", "_deprecated"
- •Duplicatas de colunas: "customer_id" e "client_id" com os mesmos valores — manter apenas um
Código — Detecção Automática de Colunas Mortas
def detectar_colunas_mortas(df, threshold_nulos=0.95):
candidatas = []
for col in df.columns:
null_rate = df[col].isnull().mean()
unique_count = df[col].nunique()
is_legacy = any(s in col.lower() for s in
['_old', '_backup', '_v1', '_legacy', '_deprecated'])
if null_rate > threshold_nulos:
candidatas.append((col, f"Nulos: {null_rate:.1%}"))
elif unique_count <= 1:
candidatas.append((col, "Variância zero"))
elif is_legacy:
candidatas.append((col, "Nomenclatura de migração"))
return candidatas
# SEMPRE documentar antes de eliminar
colunas_mortas = detectar_colunas_mortas(df_processado)
print("Candidatas à eliminação:", colunas_mortas)
# Revisar manualmente, depois eliminar
df_limpo = df_processado.drop(columns=[c[0] for c in colunas_mortas])
⚠️ Atenção: Sempre documente o que foi removido e por quê antes de deletar. Uma coluna com 96% de nulos pode ser crítica para os 4% de registros onde ela existe — verifique com o cliente antes de eliminar colunas que parecem "visivelmente vazias".
5️⃣ Passo 5 — Entregar o Warehouse Limpo e Documentado
O warehouse sem documentação é inútil em 6 meses. A documentação é parte do entregável, não um bônus. Sem ela, o próximo engenheiro (ou você mesmo) vai ter que refazer toda a auditoria.
Data Dictionary
Para cada coluna: nome, tipo, descrição em linguagem natural, exemplo de valor, campo obrigatório (sim/não), e SLA de completude. Formato: CSV ou Markdown.
README do Pipeline
Instruções para reprocessar o pipeline do zero: quais arquivos de entrada, em que ordem rodar os scripts, quais variáveis de ambiente configurar, e o que verificar para confirmar que a carga foi bem-sucedida.
Registro de Decisões (ADR)
Cada decisão não-óbvia documentada: "Usamos taxa de câmbio semanal fornecida pelo cliente porque X"; "Eliminamos a coluna Y porque 97% de nulos — validado com o cliente em 14/05/2026". Salvar em decisions.md.
Testes de Validação
Scripts que verificam automaticamente o warehouse entregue: contagem de linhas esperada, ausência de nulos em campos obrigatórios, range de datas correto, totais financeiros batem com os relatórios-fonte.
✅ Resumo do Módulo
Próximo Módulo:
2.4 — Tabelas-Resumo + DuckDB: transformar dados brutos em tabelas que agentes consultam sem gastar tokens