Ir para o conteúdo

Pré-processamento de Dados

Dados brutos quase nunca estão prontos para um algoritmo de aprendizado. Os atributos vivem em escalas diferentes, categorias são armazenadas como strings, valores estão faltando e distribuições são assimétricas. O pré-processamento transforma atributos brutos em uma representação que o algoritmo consegue explorar — e, feito errado, é também o lugar mais fácil de vazar informação (ver Validação & Vazamento de Dados).

Por que escalonar atributos?

Muitos algoritmos são baseados em distância ou em gradiente, e ambos são distorcidos quando os atributos têm magnitudes muito diferentes:

  • o k-NN calcula distâncias euclidianas: um atributo na casa dos milhares (renda) abafa um na casa das dezenas (idade);
  • o gradiente descendente converge devagar em superfícies de perda alongadas criadas por atributos não escalonados;
  • SVMs e modelos regularizados (Ridge/Lasso) penalizam coeficientes como se os atributos fossem comparáveis;
  • o PCA encontra direções de variância máxima — o atributo de maior escala vence por padrão.

Modelos baseados em árvores (árvores de decisão, random forests, gradient boosting) são a exceção notável: eles dividem por limiares, então transformações monotônicas não os afetam.

Métodos de escalonamento

Padronização (z-score): centrar em zero, variância unitária —

\[ x' = \frac{x - \mu}{\sigma} \]

Normalização min-max: comprimir para \([0, 1]\)

\[ x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}} \]

Escalonamento robusto: usar mediana e IQR em vez de média e desvio padrão —

\[ x' = \frac{x - \text{mediana}(x)}{\text{IQR}(x)} \]

A escolha importa quando há outliers. O min-max é totalmente determinado pelos dois pontos mais extremos; a padronização é um pouco distorcida por eles; o escalonamento robusto os ignora:

Dados brutos vs StandardScaler, MinMaxScaler e RobustScaler

Escalonador Âncoras da fórmula Sensível a outliers? Uso típico
StandardScaler média, desvio moderadamente padrão para modelos lineares, SVM, PCA
MinMaxScaler mín, máx muito quando é preciso um intervalo limitado (ex.: valores de pixel, algumas redes neurais)
RobustScaler mediana, IQR robusto dados com caudas pesadas / outliers
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # aprende μ, σ APENAS no TREINO
X_test_scaled = scaler.transform(X_test)        # aplica os MESMOS μ, σ

Ajuste no treino, transforme no teste

O fit aprende os parâmetros (\(\mu, \sigma\), mín/máx...). Chamar fit (ou fit_transform) no conjunto de teste — ou no conjunto completo antes da divisão — vaza informação sobre a distribuição de teste para o treino. Esse é o bug de vazamento mais comum de todos, e os Pipelines existem em grande parte para evitá-lo.

Atributos assimétricos

Atributos assimétricos à direita (renda, preços, contagens) muitas vezes se beneficiam de uma transformação logarítmica, \(x' = \log(1 + x)\), que comprime a cauda longa e torna a distribuição mais simétrica. Ferramentas mais gerais: PowerTransformer (Box-Cox, Yeo-Johnson) e QuantileTransformer.

Codificação de atributos categóricos

Algoritmos consomem números, não strings. Os dois cavalos de batalha:

One-hot encoding — uma coluna binária por categoria:

from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(handle_unknown='ignore')  # categorias não vistas → tudo zero
  • Seguro para categorias nominais (sem ordem): cidade, cor, tipo de produto;
  • Explode a dimensionalidade para atributos de alta cardinalidade (CEPs → milhares de colunas).

Codificação ordinal — mapear categorias para inteiros:

from sklearn.preprocessing import OrdinalEncoder
enc = OrdinalEncoder(categories=[['pequeno', 'médio', 'grande']])
  • Correta para categorias ordinais (pequeno < médio < grande);
  • Errada para as nominais: codificar cidades como São Paulo=0, Rio=1, Recife=2 inventa uma ordem e distâncias que não existem — modelos lineares e o k-NN explorarão alegremente a ficção.

Para atributos de alta cardinalidade, considere target encoding (substituir cada categoria por uma média suavizada do alvo) — poderoso, mas propenso a vazamento: precisa ser ajustado dentro da validação cruzada.

Imputação de valores faltantes

Complementando a discussão da EDA sobre mecanismos de falta:

from sklearn.impute import SimpleImputer, KNNImputer

SimpleImputer(strategy='median')         # numérico: padrão robusto
SimpleImputer(strategy='most_frequent')  # categórico
KNNImputer(n_neighbors=5)                # imputar a partir de linhas semelhantes

Duas práticas úteis:

  • adicionar uma coluna de indicador de falta (add_indicator=True) — o fato de um valor estar faltando muitas vezes é preditivo;
  • imputar dentro de um Pipeline, para que as estatísticas de imputação sejam aprendidas apenas dos folds de treino.

Nunca impute o alvo

Linhas com alvo faltante devem ser descartadas do treino supervisionado — inventar rótulos fabrica sinal que não existe.

O mapa do pré-processamento

flowchart TD
    A[Atributo bruto] --> B{Tipo?}
    B -->|numérico| C{Assimétrico?}
    C -->|sim| D[transformação log / power] --> E
    C -->|não| E{Outliers?}
    E -->|sim| F[RobustScaler]
    E -->|não| G[StandardScaler]
    B -->|categórico| H{Ordenado?}
    H -->|sim| I[OrdinalEncoder]
    H -->|não| J{Cardinalidade?}
    J -->|baixa| K[OneHotEncoder]
    J -->|alta| L[target encoding / agrupamento]

Material de aula

Notebook da aula (em português)

Notebook prático usado em sala — Aula 03 — Normalização: abrir no Colab


Quiz