🎯 Objetivo del Post: Dominarás el arte de crear características poderosas y manejar variables categóricas, habilidades que pueden convertir un modelo mediocre en uno excepcional.
🍳 ¿Qué es la Ingeniería de Características?
La ingeniería de características es el arte y la ciencia de crear nuevas variables a partir de los datos existentes para mejorar el rendimiento de nuestros modelos de Machine Learning. Es como ser un chef experto que toma ingredientes básicos y los transforma en platos deliciosos.
💡 Dato Impactante: La ingeniería de características puede ser la diferencia entre un modelo mediocre y uno excelente. Es donde ocurre la verdadera magia del Machine Learning.
🎯 ¿Por qué es Importante?
- 🤖 Los algoritmos solo ven números: No entienden conceptos como "BMW es una marca de lujo"
- 🔄 Relaciones no lineales: Podemos capturar patrones complejos que los algoritmos no ven
- 📉 Reducción de dimensionalidad: Crear características más informativas y compactas
- 🚀 Mejora del rendimiento: Variables bien diseñadas = modelo significativamente mejor
Tipos de Ingeniería de Características
1. Características Derivadas
Crear nuevas variables a partir de las existentes.
2. Transformaciones
Cambiar la escala o distribución de variables.
3. Codificación de Variables Categóricas
Convertir texto a números que el modelo pueda entender.
4. Interacciones
Combinar múltiples variables para crear nuevas relaciones.
Características Derivadas: Ejemplos Prácticos
Ejemplo 1: Edad del Auto
import pandas as pd
import numpy as np
# Datos de ejemplo
df = pd.DataFrame({
'año': [2020, 2019, 2018, 2015, 2010],
'precio': [25000, 22000, 20000, 18000, 12000]
})
# Crear edad del auto
año_actual = 2024
df['edad_auto'] = año_actual - df['año']
print("Edad del auto:")
print(df[['año', 'edad_auto', 'precio']])
Ejemplo 2: Kilometraje Anual
# Agregar kilometraje
df['kilometraje'] = [25000, 30000, 35000, 50000, 80000]
# Calcular kilometraje anual promedio
df['km_anuales'] = df['kilometraje'] / df['edad_auto']
print("\nKilometraje anual:")
print(df[['edad_auto', 'kilometraje', 'km_anuales', 'precio']])
Ejemplo 3: Categorías de Uso
# Categorizar por uso del auto
def categorizar_uso(km_anuales):
if km_anuales < 10000:
return 'Bajo'
elif km_anuales < 20000:
return 'Normal'
else:
return 'Alto'
df['categoria_uso'] = df['km_anuales'].apply(categorizar_uso)
print("\nCategorías de uso:")
print(df[['km_anuales', 'categoria_uso', 'precio']])
Variables Categóricas: El Desafío
¿Qué son las Variables Categóricas?
Variables que toman valores de un conjunto finito de categorías:
- Marca: Toyota, BMW, Honda, Ford
- Color: Rojo, Azul, Blanco, Negro
- Transmisión: Manual, Automática
- Combustible: Gasolina, Diésel, Híbrido
¿Por qué son Problemáticas?
Los algoritmos de Machine Learning solo entienden números. Si les damos "BMW", no saben qué hacer con eso.
Ejemplo del Problema
# Datos con variables categóricas
df_categorico = pd.DataFrame({
'marca': ['Toyota', 'BMW', 'Honda', 'BMW', 'Toyota'],
'precio': [20000, 35000, 18000, 40000, 22000]
})
print("Datos categóricos:")
print(df_categorico)
# Intentar calcular correlación (esto fallará)
try:
correlacion = df_categorico.corr()
print(correlacion)
except:
print("Error: No se puede calcular correlación con texto")
Técnicas de Codificación de Variables Categóricas
1. Label Encoding (Codificación de Etiquetas)
Asigna un número único a cada categoría.
from sklearn.preprocessing import LabelEncoder
# Crear datos de ejemplo
marcas = ['Toyota', 'BMW', 'Honda', 'Ford', 'BMW', 'Toyota']
precios = [20000, 35000, 18000, 25000, 40000, 22000]
df = pd.DataFrame({'marca': marcas, 'precio': precios})
# Aplicar Label Encoding
le = LabelEncoder()
df['marca_encoded'] = le.fit_transform(df['marca'])
print("Label Encoding:")
print(df[['marca', 'marca_encoded', 'precio']])
print(f"\nMapeo: {dict(zip(le.classes_, le.transform(le.classes_)))}")
Problema: Implica un orden que puede no existir (BMW = 0, Ford = 1, Honda = 2, Toyota = 3).
2. One-Hot Encoding (Codificación One-Hot)
Crea una columna binaria para cada categoría.
from sklearn.preprocessing import OneHotEncoder
# One-Hot Encoding con pandas
df_onehot = pd.get_dummies(df, columns=['marca'], prefix='marca')
print("One-Hot Encoding:")
print(df_onehot)
# One-Hot Encoding con scikit-learn
ohe = OneHotEncoder(sparse_output=False)
marca_encoded = ohe.fit_transform(df[['marca']])
marca_df = pd.DataFrame(marca_encoded, columns=ohe.get_feature_names_out(['marca']))
print("\nOne-Hot Encoding con scikit-learn:")
print(marca_df)
Ventajas:
- No implica orden entre categorías
- Cada categoría es independiente
Desventajas:
- Crea muchas columnas (curse of dimensionality)
- Puede causar overfitting con muchas categorías
3. Target Encoding (Codificación por Objetivo)
Usa el valor promedio del objetivo para cada categoría.
# Target Encoding manual
target_encoding = df.groupby('marca')['precio'].mean()
print("Target Encoding:")
print(target_encoding)
# Aplicar target encoding
df['marca_target_encoded'] = df['marca'].map(target_encoding)
print("\nDatos con Target Encoding:")
print(df[['marca', 'precio', 'marca_target_encoded']])
Ventajas:
- Captura la relación con el objetivo
- No aumenta la dimensionalidad
Desventajas:
- Puede causar overfitting
- Necesita validación cruzada para evitar data leakage
Implementación Completa con Nuestro Dataset
Paso 1: Cargar y Preparar Datos
# Cargar datos
df = pd.read_csv('car_data_limpio.csv')
print("Información del dataset:")
print(df.info())
print(f"\nVariables categóricas:")
categorical_cols = df.select_dtypes(include=['object']).columns
print(categorical_cols.tolist())
Paso 2: Análisis de Variables Categóricas
# Análisis de marcas
print("Distribución de marcas:")
marca_counts = df['marca'].value_counts()
print(marca_counts)
# Visualización
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
marca_counts.head(10).plot(kind='bar', color='skyblue')
plt.title('Top 10 Marcas por Frecuencia')
plt.xlabel('Marca')
plt.ylabel('Cantidad de Autos')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# Precio promedio por marca
precio_por_marca = df.groupby('marca')['precio'].agg(['mean', 'count']).round(2)
precio_por_marca = precio_por_marca[precio_por_marca['count'] >= 10] # Solo marcas con al menos 10 autos
precio_por_marca = precio_por_marca.sort_values('mean', ascending=False)
print("\nPrecio promedio por marca (mínimo 10 autos):")
print(precio_por_marca.head(10))
Paso 3: Estrategia de Codificación
def estrategia_codificacion(df, columna, min_freq=10):
"""Estrategia inteligente de codificación"""
# Contar frecuencias
frecuencias = df[columna].value_counts()
# Identificar categorías frecuentes
categorias_frecuentes = frecuencias[frecuencias >= min_freq].index.tolist()
# Crear nueva columna
df[f'{columna}_encoded'] = df[columna].apply(
lambda x: x if x in categorias_frecuentes else 'Otros'
)
return df, categorias_frecuentes
# Aplicar estrategia a marca
df, marcas_frecuentes = estrategia_codificacion(df, 'marca', min_freq=20)
print(f"Marcas frecuentes: {marcas_frecuentes}")
print(f"Distribución después de agrupar:")
print(df['marca_encoded'].value_counts())
Paso 4: One-Hot Encoding Optimizado
# One-Hot Encoding solo para categorías frecuentes
df_encoded = pd.get_dummies(df, columns=['marca_encoded'], prefix='marca')
print("Columnas después de One-Hot Encoding:")
print([col for col in df_encoded.columns if col.startswith('marca_')])
# Hacer lo mismo para transmisión
df_encoded = pd.get_dummies(df_encoded, columns=['transmision'], prefix='transmision')
print(f"\nForma del dataset: {df_encoded.shape}")
Paso 5: Crear Características Derivadas
# Características derivadas más sofisticadas
def crear_caracteristicas_derivadas(df):
"""Crear características derivadas útiles"""
df = df.copy()
# 1. Edad del auto
df['edad_auto'] = 2024 - df['año']
# 2. Kilometraje anual promedio
df['km_anuales'] = df['kilometraje'] / df['edad_auto']
# 3. Categoría de uso basada en kilometraje anual
df['uso_bajo'] = (df['km_anuales'] < 10000).astype(int)
df['uso_normal'] = ((df['km_anuales'] >= 10000) & (df['km_anuales'] < 20000)).astype(int)
df['uso_alto'] = (df['km_anuales'] >= 20000).astype(int)
# 4. Auto nuevo (menos de 2 años)
df['auto_nuevo'] = (df['edad_auto'] <= 2).astype(int)
# 5. Auto viejo (más de 10 años)
df['auto_viejo'] = (df['edad_auto'] > 10).astype(int)
# 6. Kilometraje alto (más de 100,000 km)
df['km_alto'] = (df['kilometraje'] > 100000).astype(int)
# 7. Relación precio/edad (depreciación)
df['precio_por_año'] = df['precio'] / df['edad_auto']
return df
# Aplicar transformaciones
df_final = crear_caracteristicas_derivadas(df_encoded)
print("Nuevas características creadas:")
nuevas_caracteristicas = ['edad_auto', 'km_anuales', 'uso_bajo', 'uso_normal', 'uso_alto',
'auto_nuevo', 'auto_viejo', 'km_alto', 'precio_por_año']
print(nuevas_caracteristicas)
Paso 6: Selección de Características
# Seleccionar características para el modelo
caracteristicas_numericas = ['año', 'kilometraje', 'edad_auto', 'km_anuales']
caracteristicas_binarias = ['uso_bajo', 'uso_normal', 'uso_alto', 'auto_nuevo', 'auto_viejo', 'km_alto']
caracteristicas_categoricas = [col for col in df_final.columns if col.startswith(('marca_', 'transmision_'))]
# Combinar todas las características
todas_las_caracteristicas = caracteristicas_numericas + caracteristicas_binarias + caracteristicas_categoricas
print(f"Total de características: {len(todas_las_caracteristicas)}")
print(f"Características numéricas: {len(caracteristicas_numericas)}")
print(f"Características binarias: {len(caracteristicas_binarias)}")
print(f"Características categóricas (one-hot): {len(caracteristicas_categoricas)}")
# Preparar datos para el modelo
X = df_final[todas_las_caracteristicas]
y = df_final['precio']
print(f"\nForma final de X: {X.shape}")
Evaluación del Impacto de Feature Engineering
Comparar Modelos
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# Dividir datos
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# Modelo 1: Solo características básicas
X_basico = X_train[['año', 'kilometraje']]
X_val_basico = X_val[['año', 'kilometraje']]
modelo_basico = LinearRegression()
modelo_basico.fit(X_basico, y_train)
y_pred_basico = modelo_basico.predict(X_val_basico)
# Modelo 2: Con feature engineering
modelo_completo = LinearRegression()
modelo_completo.fit(X_train, y_train)
y_pred_completo = modelo_completo.predict(X_val)
# Comparar resultados
def comparar_modelos(y_real, y_pred, nombre):
rmse = np.sqrt(mean_squared_error(y_real, y_pred))
r2 = r2_score(y_real, y_pred)
print(f"{nombre}:")
print(f" RMSE: ${rmse:,.2f}")
print(f" R²: {r2:.3f}")
return rmse, r2
print("Comparación de Modelos:")
rmse_basico, r2_basico = comparar_modelos(y_val, y_pred_basico, "Modelo Básico")
rmse_completo, r2_completo = comparar_modelos(y_val, y_pred_completo, "Modelo con Feature Engineering")
# Calcular mejora
mejora_rmse = ((rmse_basico - rmse_completo) / rmse_basico) * 100
mejora_r2 = ((r2_completo - r2_basico) / r2_basico) * 100
print(f"\nMejoras:")
print(f"RMSE: {mejora_rmse:.1f}% mejor")
print(f"R²: {mejora_r2:.1f}% mejor")
Análisis de Importancia de Características
# Analizar coeficientes del modelo
coef_df = pd.DataFrame({
'caracteristica': X.columns,
'coeficiente': modelo_completo.coef_,
'abs_coeficiente': np.abs(modelo_completo.coef_)
})
# Ordenar por importancia
coef_df = coef_df.sort_values('abs_coeficiente', ascending=False)
print("Top 10 características más importantes:")
print(coef_df.head(10))
# Visualizar importancia
plt.figure(figsize=(12, 8))
top_features = coef_df.head(15)
plt.barh(range(len(top_features)), top_features['abs_coeficiente'])
plt.yticks(range(len(top_features)), top_features['caracteristica'])
plt.xlabel('Importancia (Valor Absoluto del Coeficiente)')
plt.title('Importancia de Características')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()
Mejores Prácticas
1. Evitar Data Leakage
# MAL: Calcular estadísticas usando todos los datos
precio_promedio_global = df['precio'].mean()
# BIEN: Calcular estadísticas solo en datos de entrenamiento
precio_promedio_train = X_train['precio'].mean()
2. Manejar Categorías Raras
# Agrupar categorías con pocas muestras
def agrupar_categorias_raras(serie, min_freq=5):
frecuencias = serie.value_counts()
categorias_raras = frecuencias[frecuencias < min_freq].index
return serie.replace(categorias_raras, 'Otros')
3. Validación Cruzada para Target Encoding
from sklearn.model_selection import KFold
def target_encoding_cv(X, y, columna, cv=5):
"""Target encoding con validación cruzada"""
kf = KFold(n_splits=cv, shuffle=True, random_state=42)
X_encoded = X.copy()
for train_idx, val_idx in kf.split(X):
# Calcular encoding solo con datos de entrenamiento
encoding_map = X.iloc[train_idx].groupby(columna)[y.iloc[train_idx]].mean()
# Aplicar a datos de validación
X_encoded.iloc[val_idx, X_encoded.columns.get_loc(columna)] = \
X.iloc[val_idx, X.columns.get_loc(columna)].map(encoding_map)
return X_encoded
4. Documentar Transformaciones
# Crear pipeline de transformaciones
transformaciones = {
'fecha': '2024-01-15',
'caracteristicas_derivadas': [
'edad_auto = 2024 - año',
'km_anuales = kilometraje / edad_auto',
'auto_nuevo = edad_auto <= 2',
'auto_viejo = edad_auto > 10'
],
'codificaciones': [
'marca: one-hot encoding (mínimo 20 muestras)',
'transmision: one-hot encoding'
],
'caracteristicas_finales': len(todas_las_caracteristicas)
}
print("Documentación de transformaciones:")
for key, value in transformaciones.items():
print(f"{key}: {value}")
Errores Comunes
1. Curse of Dimensionality
- Crear demasiadas características puede causar overfitting
- Solución: Selección de características, regularización
2. Data Leakage
- Usar información del futuro para predecir el pasado
- Solución: Validación cruzada, separación temporal
3. Ignorar Categorías Raras
- No agrupar categorías con pocas muestras
- Solución: Agrupar en "Otros" o usar técnicas especializadas
4. No Validar Transformaciones
- Aplicar transformaciones sin verificar su impacto
- Solución: Comparar modelos antes y después
Conclusión
La ingeniería de características y el manejo de variables categóricas son habilidades fundamentales en Machine Learning. Pueden transformar un modelo mediocre en uno excelente.
Puntos clave:
- La ingeniería de características puede ser más importante que el algoritmo
- Las variables categóricas necesitan codificación especial
- One-Hot Encoding es simple pero puede crear muchas dimensiones
- Target Encoding es poderoso pero requiere cuidado con data leakage
- Siempre valida el impacto de tus transformaciones
Próximos pasos:
- Experimentar con diferentes técnicas de codificación
- Crear características de interacción
- Aplicar selección de características
- Usar validación cruzada para evitar overfitting
En el siguiente post, exploraremos la regularización y el tuning de modelos para mejorar aún más nuestro rendimiento.
¿Qué características derivadas crees que serían más útiles para predecir precios de autos? ¿Has trabajado con variables categóricas antes?
Top comments (0)