A engenharia de recursos (feature engineering) e o pré-processamento de dados constituem a espinha dorsal de qualquer sistema preditivo de alta performance. Em aprendizado de máquina, a qualidade, a representação estrutural e a integridade estatística dos dados de entrada determinam o teto de desempenho que um algoritmo pode atingir. Como estabelecido na obra de Oswald Campesato sobre engenharia de recursos com Python 3, este relatório apresenta uma síntese analítica e exaustiva dos métodos, pipelines e algoritmos de pré-processamento, seleção de atributos, tratamento de anomalias e redução de dimensionalidade.
A Análise Exploratória de Dados (Exploratory Data Analysis - EDA) é o estágio investigativo inicial que visa resumir as características centrais de um conjunto de dados por meio de métodos quantitativos e representações gráficas. Conforme documentado nos fundamentos da ciência de dados, a EDA busca identificar padrões latentes, detectar anomalias ou observações discrepantes (outliers) e testar premissas estatísticas fundamentais sobre a distribuição subjacente das variáveis.
Os conjuntos de dados organizam-se tipicamente em estruturas matriciais compostas por observações (linhas ou data points) e atributos (colunas ou features). As fontes originais variam desde arquivos relacionais e não relacionais (CSV, TSV, planilhas Excel, tabelas RDBMS e documentos NoSQL) até retornos de APIs e streamings de serviços web.
Para selecionar a transformação matemática apropriada na engenharia de recursos, é essencial reconhecer a taxonomia e o nível de mensuração das variáveis. A classificação estatística clássica é resumida pelo acrônimo NOIR (Nominal, Ordinal, Interval, Ratio):
0 e 1,
Sim e Não, ou verdadeiro e falso.{"Vermelho", "Verde", "Azul"}), gêneros ou nomes de
cidades. Operações matemáticas como média ou ordenação não possuem
significado biunívoco nesses dados.{"Interno", "Júnior", "Pleno", "Sênior", "Gerente"}).A tabela a seguir contrasta as características operacionais e matemáticas da escala NOIR:
| Escala / Tipo | Ordenação Intrínseca? | Zero Absoluto? | Operações Permitidas | Exemplo Típico |
|---|---|---|---|---|
| Nominal | Não | Não | Igualdade (\(=\) / \(\neq\)), Frequência / Moda | Cor dos olhos, Estado civil |
| Ordinal | Sim | Não | Comparação (\(>\), \(<\) / \(\leq\), \(\geq\)), Mediana | Nível de satisfação, Classe social |
| Intervalar | Sim | Não | Adição e Subtração (\(+\), \(-\)), Média | Temperatura (\(^\circ\text{C}\)), Ano calendário |
| Razão | Sim | Sim | Multiplicação e Divisão (\(\times\), \(\div\)), Média Geométrica | Salário, Peso, Altura |
Além do escopo NOIR, os dados numéricos subdividem-se em discretos (valores finitos ou enumeráveis resultantes de contagens) e contínuos (valores numéricos de ponto flutuante em intervalos contínuos decorrentes de medições).
Na prática de Machine Learning, variáveis numéricas discretas com uma cardinalidade extremamente alta (como preços de ações expressos em centavos) são tratadas algoritmicamente como variáveis contínuas.
A inspeção estrutural de um dataset exige a verificação de dimensões, contagem de valores não nulos, tipos de dados alocados em memória e métricas descritivas centrais (média, desvio padrão, quartis).
import numpy as np
import pandas as pd
# Carregamento e inspeção estrutural do dataset
df = pd.read_csv('titanic.csv')
# Exibição de dimensões e tipos de dados
print(f"Dimensões do Dataset: {df.shape[0]} linhas x {df.shape[1]} colunas\n")
print("=== Informações dos Tipos de Dados e Nulos ===")
print(df.info())
# Estatísticas descritivas das colunas numéricas
print("\n=== Resumo Estatístico Descritivo ===")
print(df.describe())
# Verificação de dados ausentes por feature
print("\n=== Contagem de Valores Ausentes ===")
null_counts = df.isnull().sum()
print(null_counts[null_counts > 0])
O pré-processamento limpa e prepara os dados brutos para consumo pelos modelos. A presença de ruídos, colunas redundantes ou dados formatados de forma inconsistente degrada a capacidade dos estimadores.
largura e
comprimento, a coluna area é uma dependência
direta (\(\text{área} = \text{largura} \times
\text{comprimento}\)).'M',
'male', 'Male' em um único rótulo).YYYY-MM-DD).O Data Leakage ocorre quando informações do conjunto de teste ou de fontes externas ao conjunto de treinamento são acidentalmente introduzidas durante o processo de construção e ajuste do modelo. Como alertado na literatura técnica, o vazamento gera métricas de desempenho artificialmente otimistas durante o desenvolvimento, mas o modelo falha drasticamente ao ser implantado em produção.
As principais causas de Data Leakage e as estratégias de prevenção incluem:
O Data Drift (também denominado covariate shift ou feature drift) refere-se à mudança no padrão de distribuição estatística das variáveis de entrada ao longo do tempo após a implantação do modelo em produção. Diferente de um outlier isolado, o Data Drift representa uma alteração estrutural no comportamento dos dados (como mudanças no comportamento de consumo ou inflação), o que reduz a precisão preditiva do modelo e exige o seu monitoramento e retreinamento periódico com novos dados.
A presença de dependências lineares entre as variáveis preditoras compromete a estabilidade e a interpretabilidade de modelos de regressão e modelos lineares:
\[\text{VIF}_i = \frac{1}{1 - R_i^2}\]
Onde \(R_i^2\) é o coeficiente de determinação obtido ao aplicar a regressão da variável \(X_i\) contra todas as outras variáveis preditoras.
O binning (ou discretização) transforma variáveis numéricas contínuas em intervalos discretos (bins). Embora reduza o ruído em dados contínuos, o binning inadequado pode introduzir erros de classificação e perda de informação.
Regras estatísticas programáticas para determinar o número ideal de bins (\(k\)) com base em \(n\) observações:
Regra de Sturges (indicada para distribuições Gaussianas): \[k = 1 + 3.322 \times \log_{10}(n)\]
Fórmula de Doane (ajustada para distribuições assimétricas incorporando a curtose): \[k = 1 + \log_2(n) + \log_2\left(1 + \frac{|\text{curtose}|}{\sigma_k}\right)\]
Escolha de Freedman-Diaconis (baseada no Intervalo Interquartil - IQR, resistente a outliers): \[\text{Largura do Bin} = 2 \times \frac{\text{IQR}(x)}{\sqrt[3]{n}}\]
# Exemplo de Binning com KBinsDiscretizer do Scikit-Learn
from sklearn.preprocessing import KBinsDiscretizer
# Instancia o discretizador baseado em quantis
discretizer = KBinsDiscretizer(n_bins=4, encode='ordinal', strategy='quantile')
idade_binned = discretizer.fit_transform(df[['age']].dropna())
Algoritmos baseados em métricas de distância (como kNN, k-Means, SVM) ou otimização por gradiente (Regressão Logística, Redes Neurais) exigem que as variáveis numéricas estejam na mesma escala. Algoritmos baseados em árvores de decisão (Decision Trees, Random Forest, XGBoost) são invariantes à escala.
Reajusta os valores numéricos para um intervalo fixo, tipicamente \([0, 1]\). É indicada para distribuições que não seguem uma curva Gaussiana e quando os limites do domínio são conhecidos.
\[x_{\text{norm}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}}\]
Para reescalar para um intervalo genérico \([a, b]\):
\[x_{[a,b]} = a + \frac{(x - x_{\min})(b - a)}{x_{\max} - x_{\min}}\]
Modifica os dados para que tenham média 0 (\(\mu = 0\)) e desvio padrão 1 (\(\sigma = 1\)), aproximando-os de uma distribuição normal padrão. Não limita os dados a um intervalo fixo e é menos sensível a outliers extremos do que o MinMaxScaler.
\[z = \frac{x - \mu}{\sigma}\]
Utiliza a mediana (\(Q_2\)) e o Intervalo Interquartil (\(\text{IQR} = Q_3 - Q_1\)) no lugar da média e do desvio padrão. É a escolha ideal para dados que contêm muitos outliers.
\[x_{\text{robust}} = \frac{x - \text{Mediana}}{Q_3 - Q_1}\]
Um outlier é uma observação numérica anormal que se afasta substancialmente do padrão geral dos outros pontos de dados. Outliers resultam de variabilidade natural, erros de medição ou falhas na coleta de dados. Uma anomalia é um outlier que possui um significado crítico e potencial nocivo (como fraude bancária ou falha de sistema).
Avalia a distância de um ponto em relação à média em termos de desvios padrões. Pontos com \(|Z| > 3.0\) (ou \(|Z| > 2.5\)) são classificados como outliers. Pressupõe que os dados seguem uma distribuição aproximadamente Gaussiana.
\[Z = \frac{x - \bar{x}}{s}\]
Não assume distribuição normal. Calcula o primeiro (\(Q_1\)) e o terceiro (\(Q_3\)) quartis. O limite inferior e superior são definidos por:
\[\text{Limite Inferior} = Q_1 - 1.5 \times \text{IQR}\] \[\text{Limite Superior} = Q_3 + 1.5 \times \text{IQR}\]
Onde \(\text{IQR} = Q_3 - Q_1\).
Substitui a média pela mediana, sendo resistente a valores extremos.
\[\text{MAD} = \text{Mediana}(|x_i - \text{Mediana}(x)|)\]
Para datasets de alta dimensionalidade, métodos univariados falham. Utilizam-se algoritmos avançados:
from sklearn.covariance import EllipticEnvelope
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
# 1. Isolation Forest (Baseado em Árvores de Isolamento)
iso_forest = IsolationForest(contamination=0.05, random_state=42)
outliers_iso = iso_forest.fit_predict(X) # Retorna -1 para outliers e 1 para inliers
# 2. Local Outlier Factor (LOF - Densidade Local baseada em kNN)
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.05)
outliers_lof = lof.fit_predict(X)
# 3. Elliptic Envelope (Envelope Elíptico via Minimum Covariance Determinant - MCD)
elliptic = EllipticEnvelope(contamination=0.05, random_state=42)
outliers_elliptic = elliptic.fit(X).predict(X)
Tabela comparativa dos métodos de detecção de anomalias:
| Método / Algoritmo | Pressupõe Distribuição Normal? | Sensibilidade a Outliers Extremos | Tipo de Aplicação |
|---|---|---|---|
| Z-Score | Sim (Gaussiana) | Alta (Média distorcida por outliers) | Univariado |
| IQR (Tukey) | Não | Baixa (Robusto via Quartis) | Univariado |
| MAD | Não | Muito Baixa (Robusto via Mediana) | Univariado |
| Isolation Forest | Não | Baixa | Multidimensional / Grandes Datasets |
| LOF (Local Outlier Factor) | Não | Baixa | Multidimensional / Anomalias Locais |
| Elliptic Envelope | Sim (Gaussiana Multivariada) | Moderada | Multidimensional com MCD |
A Seleção de Recursos consiste em identificar e reter o subconjunto de variáveis mais relevantes para a previsão da variável alvo. Benefícios: reduz o overfitting, melhora a acurácia do modelo e diminui o tempo e custo computacional de treinamento.
Os métodos de seleção dividem-se em três grandes categorias:
┌──────────────────────────┐
│ Métodos de Seleção de │
│ Recursos │
└────────────┬─────────────┘
│
┌──────────────────────────────┼──────────────────────────────┐
│ │ │
┌───────┴───────┐ ┌───────┴───────┐ ┌───────┴───────┐
│ Filter │ │ Wrapper │ │ Embedded │
│ (Estatísticos)│ │ (Algorítmicos)│ │ (Integrados) │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
├── VarianceThreshold ├── RFE / RFECV ├── L1 (Lasso)
├── Chi-Squared (χ²) ├── SFS / SBS ├── ElasticNet
├── ANOVA F-test └── Boruta ├── Feature Importance
└── Mutual Information └── LightGBM
Avaliam a relevância das variáveis com base em propriedades estatísticas independentemente do algoritmo de aprendizado utilizado. São computacionalmente rápidos:
VarianceThreshold): Descarta variáveis cuja
variância é inferior a um limite fixado.f_classif /
f_regression): Mede a dependência linear entre
variáveis numéricas de entrada e um alvo categórico.mutual_info_classif): Mede a quantidade de
informação compartilhada entre variáveis, capturando relações não
lineares.Utilizam um algoritmo de Machine Learning como um “envolto” para avaliar iterativamente o desempenho de diferentes combinações de recursos. Exigem alto custo computacional:
RFE /
RFECV): Remove iterativamente os recursos menos
importantes com base nos coeficientes do modelo até atingir o número
desejado, utilizando validação cruzada no RFECV.SFS /
SBS): Adiciona (Forward Selection) ou
remove (Backward Elimination) atributos incrementalmente com
base no ganho de desempenho.Realizam a seleção de atributos como parte integrante do processo de treinamento do algoritmo:
from sklearn.feature_selection import RFE, SelectKBest, VarianceThreshold, chi2
from sklearn.linear_model import LogisticRegression
# Exemplo de Filter: Seleciona as 2 melhores variáveis via Chi-Squared
X_chi2 = SelectKBest(score_func=chi2, k=2).fit_transform(X_cat, y)
# Exemplo de Wrapper: Eliminação Recursiva de Recursos (RFE)
estimator = LogisticRegression(max_iter=500)
selector_rfe = RFE(estimator=estimator, n_features_to_select=5, step=1)
X_rfe = selector_rfe.fit_transform(X_num, y)
Datasets desbalanceados ocorrem quando a classe de interesse (minorítária) possui significativamente menos registros do que a classe majoritária. Treinar estimadores diretamente nesses dados faz com que o modelo seja viesado em favor da classe dominante.
Técnicas de reamostragem:
\[c = a + \text{rand}(0,1) \times (b - a)\]
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
# Separação prévia para evitar Data Leakage
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# Pipeline do imbalanced-learn aplicando SMOTE apenas no treino
pipeline_smote = ImbPipeline([
('smote', SMOTE(random_state=42)),
('classifier', RandomForestClassifier(random_state=42)),
])
# Ajusta o pipeline (SMOTE é aplicado estritamente na dobra de treino)
pipeline_smote.fit(X_train, y_train)
Quando as variáveis numéricas possuem distribuições assimétricas (skewed), a aplicação de transformações matemáticas aproxima a distribuição de uma curva normal Gaussiana e estabiliza a variância:
Modelos de aprendizado exigem entradas numéricas. A conversão de variáveis categóricas utiliza estratégias distintas dependendo da escala:
{'Pequeno': 1, 'Médio': 2, 'Grande': 3}).Atenção à Alta Cardinalidade: Se uma variável categórica
tiver muitas categorias distintas, o One-Hot Encoding gerará uma matriz
extremamente esparsa (preenchida predominantemente por zeros),
aumentando o risco de overfitting e o consumo de memória. Para
contornar esse problema, utiliza-se a junção de categorias infrequentes
em um rótulo 'Outros' ou aplica-se o Feature
Hashing.
# Exemplo de One-Hot Encoding com redução de multicolinearidade (drop='first')
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(drop='first', sparse_output=False, handle_unknown='ignore')
encoded_cats = ohe.fit_transform(df[['departamento', 'genero']])
O Feature Hashing aplica uma função de hash
determinística para mapear categorias de texto ou nomes de atributos
para índices inteiros em um vetor de tamanho fixo retido em memória
(n_features).
from sklearn.feature_extraction import FeatureHasher
# Instancia o FeatureHasher com tamanho de vetor fixo
hasher = FeatureHasher(n_features=8, input_type='dict')
data_dicts = [{'dog': 1, 'cat': 2}, {'dog': 2, 'run': 5}]
hashed_features = hasher.fit_transform(data_dicts).toarray()
Tabela de referência dos algoritmos em relação à necessidade de escalonamento prévio de atributos:
| Algoritmo | Exige Escalonamento Numérico? | Razão Técnica |
|---|---|---|
| kNN (k-Nearest Neighbors) | Sim | Baseado na Distância Euclidiana entre pontos |
| Regressão Logística / Linear | Sim | Otimização por Gradiente Descendente e Penalidades L1/L2 |
| Redes Neurais (ANN) | Sim | Sensibilidade das Funções de Ativação e Gradientes |
| SVM (Support Vector Machines) | Sim | Cálculo do Hiperplano de Margem Máxima |
| PCA (Principal Component Analysis) | Sim | Algoritmo baseado na Variância do espaço de atributos |
| Árvores de Decisão (Decision Trees) | Não | Divisões invariantes à escala em nós com limiares |
| Random Forest / Gradient Boosting | Não | Ensembles de árvores invariantes à escala |
| Naive Bayes | Não | Baseado na probabilidade condicional de frequências |
Quando o conjunto de dados possui um número excessivo de atributos (alta dimensionalidade), os pontos tornam-se esparsos no espaço vetorial, fenômeno conhecido como a Maldição da Dimensionalidade (Curse of Dimensionality). A redução de dimensionalidade projeta ou extrai componentes em um espaço de menor dimensão.
A covariância mede a direção da relação linear entre duas variáveis aleatórias \(X\) e \(Y\):
\[\text{Cov}(X, Y) = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{n}\]
A Matriz de Covariância \(C\) é uma matriz quadrada e simétrica \(n \times n\) cujos elementos da diagonal principal representam as variâncias das variáveis (\(\text{Cov}(X_i, X_i) = \text{Var}(X_i)\)) e os elementos fora da diagonal representam as covariâncias cruzadas.
Como a covariância é afetada pela escala de medição das variáveis, utiliza-se a Matriz de Correlação (que divide a covariância pelo produto dos desvios padrões), obtendo-se valores padronizados no intervalo \([-1, 1]\):
\[\text{Corr}(X, Y) = \frac{\text{Cov}(X, Y)}{\sigma_X \sigma_Y}\]
O PCA é um algoritmo não supervisionado de redução linear de dimensionalidade. Ele identifica os eixos de maior variância nos dados e projeta o espaço vetorial original sobre esses novos eixos ortogonais, chamados de Componentes Principais.
\[C v = \lambda v\]
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# 1. Padronização prévia necessária para o PCA
X_scaled = StandardScaler().fit_transform(X)
# 2. Instanciação e ajuste do PCA mantendo 95% da variância explicada
pca = PCA(n_components=0.95)
X_pca = pca.fit_transform(X_scaled)
print(f"Número de componentes retidos: {pca.n_components_}")
print(f"Razão de variância explicada acumulada: {np.sum(pca.explained_variance_ratio_):.2%}")
Quando os dados possuem estruturas não lineares complexas (como o Swiss Roll), o PCA falha em capturar as relações. Utilizam-se algoritmos não lineares de Aprendizado de Variedades:
from sklearn.manifold import TSNE
import umap
# Projeção não linear para visualização 2D com t-SNE
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)
# Projeção escalável com UMAP
reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X_scaled)
A escolha da métrica de distância define como os pontos são comparados no espaço de atributos:
Distância Euclidiana (Métrica L2 padrão no espaço \(n\)-dimensional): \[d(p, q) = \sqrt{\sum_{i=1}^{n} (p_i - q_i)^2}\]
Distância de Manhattan / Taxicab (Métrica L1, soma das diferenças absolutas): \[d(p, q) = \sum_{i=1}^{n} |p_i - q_i|\]
Distância de Mahalanobis: Mede a distância entre um ponto \(P\) e uma distribuição de probabilidade, considerando a covariância entre as variáveis.
Similaridade de Cosseno: Mede o cosseno do ângulo entre dois vetores. Utilizada em PNL e espaço vetorial de palavras, sendo invariante à magnitude do vetor: \[\text{Simil}(A, B) = \frac{A \cdot B}{\|A\| \|B\|}\]
Índice / Similaridade de Jaccard: Utilizado para conjuntos ou variáveis booleans. Mede a razão entre a interseção e a união dos conjuntos: \[J(A, B) = \frac{|A \cap B|}{|A \cup B|}\]
Locality Sensitive Hashing (LSH): Algoritmo de redução de dimensionalidade que maximiza propositalmente as colisões de hash para agrupar pontos similares em vetores de alta dimensão em buckets de busca rápida em tempo constante.
A engenharia de recursos e o pré-processamento de dados constituem uma disciplina técnica que une rigor estatístico e pragmatismo algorítmico. A construção de pipelines robustos em Python exige o cumprimento estrito das seguintes diretrizes: