← Voltar pro projeto

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)
FeatureInformação mútua
atraso_dias0,0684
delivery_days0,0571
freight_value0,0077
price0,0074
payment_installments0,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):

FeatureCorrelação com freight_value
product_weight_g0,615
product_height_cm0,393
product_width_cm0,331
product_length_cm0,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.