Skip to content

Abmboss/feature-engineering-patterns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

feature-engineering-patterns

Referência prática de padrões de feature engineering em SQL (BigQuery) e Python para sistemas de ML em produção — deduplicação, enriquecimento, features temporais, encoders e validação.


Visão Geral

Feature engineering é onde a maioria dos projetos de ML ganha ou perde qualidade. Este repositório documenta os padrões que resolvem os problemas mais recorrentes em pipelines de dados reais:

  • Deduplicação — o mesmo registro aparece em múltiplas fontes ou foi ingerido em duplicata
  • Features temporais — janelas deslizantes, lag features e point-in-time correto (sem data leakage)
  • Enriquecimento — target encoding seguro, imputação hierárquica, features geográficas e de produto
  • Encoders Python — implementações scikit-learn compatíveis para target encoding, WOE, cíclico e frequência
  • Validação — detecta leakage, drift de distribuição (PSI), variância zero e multicolinearidade antes do treino
  • Feature Store — padrão Dataform para feature store incremental no BigQuery

Estrutura

feature-engineering-patterns/
│
├── sql/
│   ├── deduplication/
│   │   └── deduplication_patterns.sql    # 6 padrões: latest row, CPF dedup, SCD2, eventos, anti-fan-out, qualidade
│   ├── temporal/
│   │   └── temporal_features.sql         # Rolling windows, lag, sazonalidade sin/cos, time-to-event, PIT-correct
│   ├── enrichment/
│   │   └── enrichment_patterns.sql       # Geográfico, target encoding com smoothing, imputação, cross-sell, interações
│   └── aggregation/
│
├── python/
│   ├── encoders/
│   │   └── categorical_encoders.py       # TargetEncoder (K-fold OOF), WOEEncoder + IV, CyclicEncoder, FrequencyEncoder
│   └── validators/
│       └── feature_validator.py          # FeatureValidator: missing, leakage, variance, cardinality, PSI drift
│
├── dataform/definitions/features/
│   └── feat_master.sqlx                  # Feature store incremental: une behavioral + produto + CRM + RFM
│
└── data/

SQL Patterns

Deduplicação

Padrão 1 — Registro mais recente por chave:

SELECT * EXCEPT(rn) FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY lead_id ORDER BY updated_at DESC) AS rn
  FROM raw_leads
) WHERE rn = 1

Padrão 4 — Dedup de eventos com tolerância temporal (mesma interação dentro do mesmo dia):

ROW_NUMBER() OVER (
  PARTITION BY lead_id, event_type, DATE(event_date), channel
  ORDER BY event_date ASC
) AS rn

Padrão 5 — Anti-fan-out: agregar ANTES do JOIN:

-- ERRADO: leads × eventos → cardinalidade explode
SELECT l.*, e.event_type FROM leads l JOIN events e USING (lead_id)

-- CORRETO: uma linha por lead_id antes do JOIN
WITH event_summary AS (
  SELECT lead_id, COUNT(*) AS n_events FROM events GROUP BY lead_id
)
SELECT l.*, e.n_events FROM leads l LEFT JOIN event_summary e USING (lead_id)

Features Temporais

Múltiplas janelas sem múltiplos scans (eficiente em BigQuery):

COUNTIF(event_date >= cutoff_7d)   AS w7d_events,
COUNTIF(event_date >= cutoff_30d)  AS w30d_events,
COUNT(*)                            AS w90d_events
-- Uma única passagem pelos dados, três janelas de resultado

Point-in-time correto — evita data leakage no treino:

-- Filtro crítico: estritamente ANTES da data de observação
LEFT JOIN events AS e
  ON e.lead_id = tp.lead_id
  AND DATE(e.event_date) < tp.observation_date  -- ← sem isso, há leakage

Sazonalidade com sin/cos (preserva ciclicidade para modelos lineares):

SIN(2 * ACOS(-1) * EXTRACT(DAYOFWEEK FROM event_date) / 7) AS dow_sin,
COS(2 * ACOS(-1) * EXTRACT(DAYOFWEEK FROM event_date) / 7) AS dow_cos

Encoders Python

Target Encoding com K-fold (sem leakage)

from python.encoders.categorical_encoders import TargetEncoder

encoder = TargetEncoder(smoothing=10.0, n_folds=5)

# No treino: encoding out-of-fold (nunca vê o próprio target)
X_train_encoded = encoder.fit_transform_train(X_train, y_train)

# No teste/produção: usa o mapeamento fit no treino completo
X_test_encoded = encoder.transform(X_test)

WOE + Information Value

from python.encoders.categorical_encoders import WOEEncoder

encoder = WOEEncoder(regularization=0.5)
encoder.fit(X_train, y_train)

print(encoder.iv_summary())
# variavel            iv      classificacao
# broker_segment      0.312   alto (verificar leakage)
# lead_source         0.148   médio
# income_band         0.087   fraco
# uf                  0.019   sem poder

Variáveis Cíclicas

from python.encoders.categorical_encoders import CyclicEncoder

encoder = CyclicEncoder(col_periods={"hour": 24, "day_of_week": 7, "month": 12})
X_encoded = encoder.fit_transform(X)
# Cria: hour_sin, hour_cos, day_of_week_sin, day_of_week_cos, month_sin, month_cos

Validação de Features

from python.validators.feature_validator import FeatureValidator

validator = FeatureValidator(
    target_col="converted",
    missing_threshold=0.30,
    leakage_threshold=0.90,
)

# Validação antes do treino
result = validator.validate(df_train)
print(result.summary())

if not result.passed:
    raise ValueError("Validação falhou — corrigir antes de treinar")

# Detecção de drift antes do scoring (PSI)
result_drift = validator.validate_drift(df_train, df_score, psi_threshold=0.2)
# PSI > 0.2 → retreinar o modelo

Feature Store (Dataform)

feat_master.sqlx implementa uma feature store incremental no BigQuery:

  • Particionada por snapshot_date → queries de treino e scoring filtram por data sem full scan
  • Clusterizada por lead_tier, broker_segment → joins com mart de scoring são eficientes
  • Incremental → só processa a partição do dia atual, não reprocessa histórico
  • Point-in-time safe → cada snapshot usa apenas features disponíveis naquela data
# Deploy via Dataform
dataform run --tags feature_store

Conceitos Fundamentais

Por que target encoding precisa de K-fold?

Sem K-fold, a linha de treino usa seu próprio target para calcular o encoding — o modelo aprende encoded_value → target trivialmente. O K-fold garante que cada linha seja encoded usando apenas as outras linhas.

O que é PSI e quando retreinar?

Population Stability Index mede se a distribuição das features em produção mudou em relação ao treino. PSI > 0.2 indica que o modelo foi treinado em dados que não representam mais a população atual — o score deixa de ser confiável.

Anti-fan-out: por que é silencioso e perigoso?

Um JOIN de leads (10k linhas) × events (500k linhas) sem GROUP BY anterior produz uma tabela com até 50M linhas. Modelos treinados nessa tabela superestimam a importância de leads com mais eventos. A solução é sempre agregar a tabela maior antes do JOIN.

Codificação seno/cosseno: por que não usar hora/dia diretamente?

Um modelo linear trata hora 23 e hora 0 como distantes (|23-0|=23). Com sin/cos, a distância circular é correta — hora 23 e hora 0 estão próximas no espaço encodado.


Como Usar

# Instala dependências Python
pip install pandas numpy scikit-learn scipy xgboost

# Testa encoders
python -c "
from python.encoders.categorical_encoders import TargetEncoder, WOEEncoder
print('Encoders OK')
"

# Testa validador
python -c "
from python.validators.feature_validator import FeatureValidator
print('Validator OK')
"

Licença

MIT License.

About

Padrões de feature engineering em SQL e Python — dedup, features temporais, encoders e validação

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages