Hay un dicho en el aprendizaje automático: entra basura, sale basura. Puedes tener el algoritmo más sofisticado del mundo, pero si lo alimentas con datos mal preparados, los resultados serán pobres. Por el contrario, un algoritmo simple con datos excelentes y cuidadosamente preparados puede superar a un algoritmo complejo con datos sin procesar y desordenados.
La ingeniería de características es el arte de transformar datos sin procesar en representaciones de las que los algoritmos de aprendizaje automático puedan aprender de manera efectiva. Podría decirse que es la habilidad más impactante en el aprendizaje automático aplicado, y es donde el conocimiento profundo del dominio se encuentra con la ciencia de datos.
En el aprendizaje automático, una característica es cualquier propiedad o atributo medible de lo que estás intentando predecir. Las características son las entradas a su modelo; la etiqueta (u objetivo) es la salida.
Para un modelo de predicción del precio de la vivienda:
Para un clasificador de spam de correo electrónico:
La calidad, cantidad y relevancia de sus funciones suelen ser más importantes que el algoritmo que elija.
Los datos del mundo real son confusos. Los algoritmos de aprendizaje automático tienen requisitos específicos que los datos sin procesar casi nunca satisfacen:
Iniciar sesión unirse a la discusión
La ingeniería de características es el proceso de cerrar la brecha entre los datos sin procesar y las entradas listas para el modelo.
Cuando las características tienen escalas muy diferentes, los modelos pueden ser engañosos. Una característica con valores de miles puede dominar una característica con valores entre 0 y 1.
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X_train)
# Before: age=[22, 65, 34], income=[18000, 95000, 42000]
# After: age=[0.0, 1.0, 0.27], income=[0.0, 1.0, 0.31]
Más robusto a los valores atípicos que el escalado mínimo-máximo:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
# Transforms each feature to have mean=0, standard deviation=1
# Feature value → (value - mean) / std_dev
Regla general: utilice la estandarización de forma predeterminada. Utilice min-max cuando necesite valores en un rango acotado específico (por ejemplo, entradas de redes neuronales).
Categorías como "rojo/verde/azul" o "Londres/Manchester/Birmingham" no tienen un orden numérico natural. Debe codificarlos antes de alimentarlos a la mayoría de los algoritmos.
Crea una columna binaria para cada categoría. Mejor cuando las categorías no tienen un orden inherente:
import pandas as pd
# Original: colour = ['red', 'green', 'blue', 'red']
df = pd.DataFrame({'colour': ['red', 'green', 'blue', 'red']})
encoded = pd.get_dummies(df['colour'], prefix='colour')
print(encoded)
# colour_blue colour_green colour_red
# 0 0 0 1
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
Asigna cada categoría a un número entero. Úselo únicamente cuando las categorías tengan un orden natural:
from sklearn.preprocessing import LabelEncoder
# Works for: ['low', 'medium', 'high'] → [0, 1, 2]
# DANGER: Don't use for unordered categories like cities —
# the model will incorrectly assume London(0) < Paris(1) < Tokyo(2)
le = LabelEncoder()
df['education_level'] = le.fit_transform(df['education_level'])
# 'school' → 0, 'undergraduate' → 1, 'postgraduate' → 2
Los datos faltantes son casi universales en los conjuntos de datos del mundo real. Tienes varias opciones:
Complete los valores faltantes con un sustituto calculado:
from sklearn.impute import SimpleImputer
import numpy as np
# Strategy options: 'mean', 'median', 'most_frequent', 'constant'
imputer = SimpleImputer(strategy='median')
X_imputed = imputer.fit_transform(X)
# For numerical data: median is robust to outliers
# For categorical data: use 'most_frequent'
A veces, el hecho de que falten datos es en sí mismo informativo:
# Create a binary flag: 1 if income was missing, 0 if present
df['income_missing'] = df['income'].isna().astype(int)
# Then impute the original column
df['income'].fillna(df['income'].median(), inplace=True)
Elimine una columna si tiene más de ~70-80% de valores faltantes (rara vez informativo). Elimine una fila solo si tiene muchos datos y la fila falta aleatoriamente (no sistemáticamente).
Aquí es donde la ingeniería de funciones se convierte en un arte. Utiliza el conocimiento del dominio para crear funciones que capturen patrones que no son visibles en los datos sin procesar.
df['purchase_datetime'] = pd.to_datetime(df['purchase_datetime'])
# Extract meaningful components
df['hour_of_day'] = df['purchase_datetime'].dt.hour
df['day_of_week'] = df['purchase_datetime'].dt.dayofweek
df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
df['month'] = df['purchase_datetime'].dt.month
df['is_holiday'] = df['purchase_datetime'].isin(uk_holidays).astype(int)
Una marca de tiempo sin formato no le dice nada directamente al modelo; estas características derivadas exponen patrones como "picos de fraude a las 3 a.m." o "picos de ventas los fines de semana".
# Square footage × number of bathrooms might be more predictive
# than either feature alone for house prices
df['size_per_bathroom'] = df['sqft'] / df['bathrooms']
# Ratio of income to loan amount — a classic credit risk feature
df['debt_to_income'] = df['loan_amount'] / df['annual_income']
from sklearn.feature_extraction.text import TfidfVectorizer
# Convert raw text to numerical feature matrix
vectorizer = TfidfVectorizer(max_features=1000, stop_words='english')
text_features = vectorizer.fit_transform(df['review_text'])
# Each word becomes a feature; its value reflects how important
# the word is in that document relative to the whole corpus
Una vez que haya creado un modelo, puede medir qué características le resultaron más útiles. Esto es valioso tanto para comprender su modelo como para decidir qué características conservar o eliminar:
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Get feature importances
importances = pd.Series(
model.feature_importances_,
index=feature_names
).sort_values(ascending=False)
print(importances.head(10))
# income_to_debt_ratio 0.187
# days_since_last_payment 0.143
# credit_utilisation 0.121
# ...
Las características con una importancia cercana a cero generalmente se pueden eliminar sin afectar el rendimiento, y eliminarlas simplifica el modelo, reduce el tiempo de entrenamiento y puede mejorar la generalización.
Cuando tienes cientos o miles de características, los modelos pueden tener dificultades; esto se llama la maldición de la dimensionalidad. Las técnicas de reducción de dimensionalidad comprimen características preservando los patrones más importantes:
from sklearn.decomposition import PCA
# Reduce 100 features to 10 components that capture 95% of variance
pca = PCA(n_components=10)
X_reduced = pca.fit_transform(X_scaled)
print(f"Variance explained: {pca.explained_variance_ratio_.sum():.2%}")
# Variance explained: 94.7%
PCA (Análisis de Componentes Principales) es el enfoque más común, pero otros incluyen t-SNE (para visualización) y UMAP (para preservar la estructura local).
Tiene un conjunto de datos con una columna de ”ciudad” que contiene 50 nombres de ciudades diferentes. ¿Qué enfoque de codificación debería utilizar?