Vazamento de Dado: o Jeito Mais Fácil de Enganar a Si Mesmo
Os capítulos 1 e 2 montaram o dataframe mestre e mostraram seis ângulos exploratórios. Agora eu preciso escolher, pra cada um dos quatro cenários definidos lá no capítulo 1, quais features fazem sentido usar. Notebook completo, executado, público no Colab.
Uma feature nova primeiro: distância cliente-vendedor
O capítulo 2 mostrou que estado do cliente correlaciona com tempo de entrega. Antes de entrar nos cenários, calculei uma distância de verdade, não só "mesmo estado ou não": latitude/longitude média por prefixo de CEP (olist_geolocation_dataset, mais de 1 milhão de linhas, por isso a média primeiro), e a fórmula de Haversine entre cliente e vendedor.
geo_media = geolocation.groupby('geolocation_zip_code_prefix')[['geolocation_lat', 'geolocation_lng']].mean().reset_index()
def haversine(lat1, lon1, lat2, lon2):
R = 6371
phi1, phi2 = np.radians(lat1), np.radians(lat2)
dphi = np.radians(lat2 - lat1)
dlambda = np.radians(lon2 - lon1)
a = np.sin(dphi / 2) ** 2 + np.cos(phi1) * np.cos(phi2) * np.sin(dlambda / 2) ** 2
return 2 * R * np.arcsin(np.sqrt(a))
Achei um bug de verdade nessa etapa antes de publicar: na primeira rodada, o merge entre customer_zip_code_prefix/seller_zip_code_prefix e a tabela de geolocalização perdeu 70% dos dados (só 30% de cobertura), porque o tipo da coluna de CEP não veio garantidamente igual dos dois lados em todo ambiente. Forcei int64 explícito nos três antes do merge, e a cobertura voltou pro esperado: só 581 pedidos de 118.310 ficaram sem distância (99,5% de cobertura). A distância média cliente-vendedor no Brasil inteiro é 597 km, com máximo de 8.678 km, uma prova em número de como o país é gigante.
Cenário 1: Atraso na entrega, e a demonstração de vazamento de dado
Candidatas: distance_km, product_weight_g, price, freight_value, tempo de aprovação (approval_hours), mês da compra. O alvo atrasado é definido comparando order_delivered_customer_date com order_estimated_delivery_date. E aqui mora a armadilha clássica de todo curso de ML: se eu incluir atraso_dias (a própria diferença entre essas duas datas) como feature, o modelo não aprende a prever atraso, ele lê a resposta direto da prova.
features_honestas = ['distance_km', 'product_weight_g', 'price', 'freight_value', 'approval_hours', 'month']
features_vazadas = features_honestas + ['atraso_dias']
Treinei os dois, RandomForest simples, mesma seed, mesma divisão treino/teste:
Carregando dados reais...
O modelo honesto chega em AUC 0,7388 (95.968 pedidos usados, taxa real de atraso de 6,76%). O modelo vazado chega em AUC 1,0, perfeito, porque atraso_dias > 0 praticamente já É a definição do alvo. Um detalhe que vale destacar: a acurácia do modelo honesto sozinha (93,24%) parece ótima à primeira vista, mas como só 6,76% dos pedidos atrasam, um modelo bobo que sempre chuta "no prazo" já acerta 93,24% sem aprender nada. Acurácia sozinha engana quando as classes são desbalanceadas desse jeito, AUC é a métrica que conta a história real aqui.
Repara também a importância de feature do modelo honesto:
Carregando dados reais...
month lidera disparado, com 0,36 de importância, praticamente o dobro da segunda colocada (distance_km, 0,18). Isso conecta direto com o achado do capítulo 2: novembro (Black Friday) sobrecarrega a logística de um jeito que nem distância física consegue explicar sozinha. Sazonalidade importa mais pra prever atraso do que o quão longe o vendedor está do cliente.
Cenário 2: Nota da avaliação
Candidatas: atraso_dias, tempo de entrega, preço, frete, número de parcelas. Como review_score é uma escala ordinal bimodal (o capítulo 2 já mostrou isso), usei informação mútua em vez de correlação de Pearson simples:
mi = mutual_info_classif(X2, y2, random_state=42)
| Feature | Informação mútua |
|---|---|
atraso_dias | 0,0684 |
delivery_days | 0,0571 |
freight_value | 0,0077 |
price | 0,0074 |
payment_installments | 0,0026 |
atraso_dias e delivery_days dominam, quase 10 vezes mais informativas que preço ou frete (95.829 pedidos usados). Confirma com uma técnica diferente exatamente o que a correlação do capítulo 2 já tinha apontado.
Cenário 3: Valor do frete
Candidatas: peso e as três dimensões do produto. Correlação de Pearson simples já resolve aqui (98.650 pedidos usados):
| Feature | Correlação com freight_value |
|---|---|
product_weight_g | 0,615 |
product_height_cm | 0,393 |
product_width_cm | 0,331 |
product_length_cm | 0,317 |
Peso domina moderadamente sobre qualquer dimensão isolada, o que faz sentido físico: transportadora cobra por peso volumétrico, mas peso real puxa mais a conta que qualquer lado da caixa sozinho.
Cenário 4: Segmentação de clientes (RFM)
Aqui não é seleção de feature no sentido supervisionado, é a engenharia das três variáveis que vão alimentar o clustering do capítulo 5: Recência, Frequência e Valor monetário por cliente.
rfm = oc.groupby('customer_unique_id').agg(
recencia_dias=('order_purchase_timestamp', lambda x: (data_max - x.max()).days),
frequencia=('order_id', 'nunique'),
valor_monetario=('payment_value', 'sum'),
).reset_index()
95.560 clientes únicos. E o número que mais chamou atenção: só 2.924 clientes (3,06% do total) fizeram mais de um pedido. A frequência média é 1,034, ou seja, a marketplace inteira é dominada por compra única. Isso muda completamente como pensar em segmentação no capítulo 5: não vai ter "cliente fiel" como categoria grande, o RFM aqui provavelmente separa mais por valor gasto e recência do que por frequência de compra.
Fechando o capítulo
Distância cliente-vendedor calculada (e um bug de merge real corrigido no processo), vazamento de dado demonstrado na prática (a diferença entre AUC 0,74 honesto e AUC 1,0 vazado é a lição mais concreta que dá pra dar sobre o assunto), e as features de cada cenário definidas com técnica apropriada pro tipo de problema. Sazonalidade se provou mais importante que distância pro atraso, atraso domina a nota de avaliação, peso domina o frete, e a base de clientes é majoritariamente de compra única. No próximo capítulo eu treino os modelos de verdade pra cada cenário, com tracking de experimento e comparação de métrica.