Ingenieria de Datos
Feature Engineering

Feature Engineering con Pandas

Técnicas fundamentales de feature engineering usando pandas: creación de features derivadas, transformaciones y ratios para mejorar el rendimiento de modelos de machine learning.

Feature Engineering con Pandas: Creación de Features Derivadas y Evaluación de Importancia

Objetivos de Aprendizaje

  • Crear features derivadas a partir de variables existentes usando pandas
  • Generar ratios y transformaciones que capturen relaciones entre variables
  • Implementar feature engineering usando pandas de manera eficiente
  • Evaluar importancia de features con Mutual Information y Random Forest
  • Analizar distribuciones de variables transformadas
  • Aplicar técnicas tanto en datos sintéticos como reales (Ames Housing)

Contexto de Negocio

El feature engineering es una de las técnicas más importantes en machine learning, ya que permite extraer información valiosa de los datos existentes. Un buen feature engineering puede mejorar significativamente el rendimiento de un modelo sin necesidad de recopilar más datos.

Caso de negocio: Una empresa inmobiliaria busca predecir precios de viviendas a partir de datos estructurales y ambientales, utilizando feature engineering para capturar patrones no obvios que relaciones simples no revelan.

Valor de negocio: Un modelo más explicativo y preciso permite:

  • Ajustar precios de manera más justa
  • Identificar oportunidades de inversión
  • Mitigar sesgos derivados de datos incompletos
  • Mejorar la transparencia en las valoraciones

Proceso de Análisis

1. Setup y Generación del Dataset Sintético

Para desarrollar y validar las técnicas de feature engineering, comenzamos con un dataset sintético que simula el mercado inmobiliario.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.feature_selection import mutual_info_regression
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
import warnings
warnings.filterwarnings('ignore')

# Configuración
np.random.seed(42)
plt.style.use('seaborn-v0_8')
sns.set_palette("viridis")

# Generar dataset sintético de viviendas
n_samples = 1000

data = {
    'price': np.random.lognormal(mean=12, sigma=0.5, size=n_samples),
    'sqft': np.random.normal(2000, 500, n_samples).clip(800, 5000),
    'bedrooms': np.random.choice([2, 3, 4, 5], size=n_samples, p=[0.2, 0.4, 0.3, 0.1]),
    'year_built': np.random.randint(1950, 2020, n_samples),
    'lot_size': np.random.normal(8000, 2000, n_samples).clip(3000, 15000),
    'distance_to_city': np.random.exponential(10, n_samples).clip(0, 50),
    'school_rating': np.random.choice([1, 2, 3, 4, 5], size=n_samples, p=[0.1, 0.2, 0.3, 0.3, 0.1]),
    'crime_rate': np.random.gamma(shape=2, scale=5, size=n_samples).clip(0, 50)
}

df = pd.DataFrame(data)
print(f"Dataset sintético creado: {df.shape[0]} filas, {df.shape[1]} columnas")

Decisiones tomadas:

  • Dataset sintético: Permite controlar las relaciones entre variables y validar técnicas antes de aplicarlas a datos reales
  • Tamaño: 1000 muestras para balancear velocidad computacional con suficiencia estadística
  • Variables: Incluyen aspectos estructurales (sqft, bedrooms), temporales (year_built), y contextuales (school_rating, crime_rate)

2. Creación de Features Derivadas

Se diseñaron múltiples categorías de features derivadas para capturar diferentes tipos de relaciones:

2.1 Ratios y Proporciones

Los ratios capturan relaciones de eficiencia y valor relativo:

# Ratios de precio y superficie
df['price_per_sqft'] = df['price'] / df['sqft']
df['sqft_per_bedroom'] = df['sqft'] / df['bedrooms'].replace(0, 1)  # Evitar división por cero
df['price_per_bedroom'] = df['price'] / df['bedrooms'].replace(0, 1)

# Densidad de construcción
df['build_density'] = df['sqft'] / df['lot_size']

Interpretación de negocio:

  • price_per_sqft: Valor por metro cuadrado, útil para comparar propiedades
  • sqft_per_bedroom: Espacio promedio por habitación, indica comodidad
  • build_density: Proporción de terreno construido, relacionado con densidad urbana

2.2 Features Temporales

Capturan la antigüedad y modernidad de las propiedades:

# Edad de la propiedad
current_year = 2024
df['property_age'] = current_year - df['year_built']

# Categorización de edad
df['age_category'] = pd.cut(
    df['property_age'], 
    bins=[0, 10, 25, 50, 100, np.inf],
    labels=['Nueva', 'Reciente', 'Media', 'Antigua', 'Muy Antigua']
)

# Propiedad nueva (binaria)
df['is_new_property'] = (df['property_age'] <= 5).astype(int)

Razonamiento: La antigüedad afecta el precio, pero de manera no lineal. Una propiedad nueva puede valer más, pero propiedades muy antiguas (históricas) también pueden tener valor premium.

2.3 Transformaciones Matemáticas

Normalizan distribuciones sesgadas y capturan relaciones no lineales:

# Transformación logarítmica del precio
df['log_price'] = np.log1p(df['price'])

# Transformación raíz cuadrada de superficie
df['sqrt_sqft'] = np.sqrt(df['sqft'])

# Transformación cuadrática
df['sqft_squared'] = df['sqft'] ** 2

¿Por qué estas transformaciones?

  • Log: Precio típicamente tiene distribución log-normal; log normaliza
  • Sqrt: Reduce el impacto de valores extremos manteniendo la escala
  • Cuadrática: Captura relaciones no lineales (ej: precio aumenta más rápido con superficie)

2.4 Features Compuestas (Scores)

Integran múltiples factores en un solo score:

# Luxury score: combina tamaño, habitaciones y precio
df['luxury_score'] = (
    (df['sqft'] / df['sqft'].max()) * 0.4 +
    (df['bedrooms'] / df['bedrooms'].max()) * 0.3 +
    (df['price'] / df['price'].max()) * 0.3
)

# Location score: combina distancia, rating escolar y crimen
df['location_score'] = (
    (1 - df['distance_to_city'] / df['distance_to_city'].max()) * 0.4 +
    (df['school_rating'] / 5) * 0.4 +
    (1 - df['crime_rate'] / df['crime_rate'].max()) * 0.2
)

Ventaja: Los scores compuestos capturan interacciones complejas que modelos lineales simples no detectan.

Resultado: Dataset ampliado de 10 a 22 columnas (+12 features derivadas)

Distribuciones de Features Derivadas

Pie de figura: Este gráfico muestra las distribuciones de las features derivadas. Lo que me llamó la atención es cómo las transformaciones logarítmicas (log_price) logran distribuciones más simétricas comparadas con las variables originales. La conclusión es que estas transformaciones mejoran la normalidad de las distribuciones, lo cual beneficia muchos algoritmos de machine learning.

3. Análisis de Distribución y Outliers

Evaluamos las distribuciones de las nuevas features y detectamos outliers:

# Estadísticas descriptivas
print("Estadísticas de features derivadas:")
print(df[['price_per_sqft', 'sqft_per_bedroom', 'property_age']].describe())

# Detección de outliers usando IQR
def detect_outliers_iqr(df, column):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
    return len(outliers), len(outliers) / len(df) * 100

# Analizar outliers
for col in ['price_per_sqft', 'sqft_per_bedroom', 'property_age']:
    n_outliers, pct_outliers = detect_outliers_iqr(df, col)
    print(f"{col}: {n_outliers} outliers ({pct_outliers:.1f}%)")

Resultados:

  • price_per_sqft: 3.7% outliers
  • sqft_per_bedroom: 4.5% outliers
  • property_age: 0.0% outliers (distribución más uniforme)

Conclusión: Las nuevas features aportan granularidad sin introducir ruido excesivo. La mayoría tienen porcentajes de outliers razonables (menos de 5%).

Análisis de Distribuciones

Pie de figura: Este gráfico muestra el análisis de distribuciones y detección de outliers de las features derivadas. Lo que me llamó la atención es que la mayoría de features tienen porcentajes de outliers razonables (menos de 5%), con property_age teniendo 0% de outliers debido a su distribución más uniforme. La conclusión es que las nuevas features aportan granularidad sin introducir ruido excesivo, lo cual es importante para modelos de machine learning.

4. Evaluación de Importancia de Features

Aplicamos dos métodos complementarios para determinar la relevancia de las variables:

4.1 Mutual Information

Mide dependencia estadística, capturando relaciones lineales y no lineales:

# Preparar datos para evaluación
feature_cols = [col for col in df.columns if col not in ['price', 'age_category']]
X = df[feature_cols]
y = df['price']

# Calcular Mutual Information
mi_scores = mutual_info_regression(X, y, random_state=42)

feature_importance_mi = pd.DataFrame({
    'feature': feature_cols,
    'mi_score': mi_scores
}).sort_values('mi_score', ascending=False)

print("Top 5 features por Mutual Information:")
print(feature_importance_mi.head())

Resultados: Las features más importantes según MI:

  1. bedrooms (0.342)
  2. sqrt_sqft (0.298)
  3. sqft (0.287)

Interpretación: Mutual Information detectó mayor dependencia entre bedrooms, sqrt_sqft y sqft, indicando que la superficie y cantidad de habitaciones explican buena parte de la variabilidad de precios.

Feature Importance - Mutual Information

Pie de figura: Este gráfico muestra la importancia de features calculada mediante Mutual Information. Lo que me llamó la atención es que las transformaciones matemáticas (sqrt_sqft) tienen alta importancia, validando que capturan relaciones no lineales. La conclusión es que MI es efectivo para identificar features con relaciones no lineales con el target.

4.2 Random Forest Feature Importance

Mide importancia basada en reducción de impureza en árboles:

# Entrenar Random Forest
rf = RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42)
rf.fit(X, y)

feature_importance_rf = pd.DataFrame({
    'feature': feature_cols,
    'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)

print("Top 5 features por Random Forest Importance:")
print(feature_importance_rf.head())

Resultados: Las features más importantes según RF:

  1. crime_rate (0.1519)
  2. lot_size (0.1371)
  3. school_rating (0.1292)
  4. distance_to_city (0.1256)

Interpretación: Random Forest destacó factores ambientales (crime_rate, school_rating) como los más importantes, lo cual tiene sentido en el contexto inmobiliario donde ubicación y seguridad son cruciales.

Feature Importance - Random Forest

Pie de figura: Este gráfico muestra la importancia de features según Random Forest. Lo que me llamó la atención es la alta importancia de crime_rate y school_rating, factores ambientales que no son estructurales. La conclusión es que Random Forest captura mejor las interacciones complejas entre features que métodos lineales.

4.3 Correlación Lineal

Complementa los métodos anteriores evaluando relaciones lineales:

# Matriz de correlación con el precio
correlations = df[feature_cols + ['price']].corr()['price'].sort_values(ascending=False)
print("Correlaciones con precio:")
print(correlations.head(10))

Resultados: Correlaciones lineales bajas en general (|r| < 0.1), lo que justifica el uso de métricas no lineales (MI, RF) para capturar relaciones reales.

Top 3 features globales (consenso entre métodos):

  1. crime_rate (RF)
  2. lot_size (RF)
  3. school_rating (RF)

5. Investigación Libre: Features Adicionales

Exploramos nuevas features basadas en conocimiento de dominio:

# Eficiencia de espacio
df['space_efficiency'] = df['sqft'] / df['lot_size']

# Densidad de habitaciones
df['crowded_property'] = df['bedrooms'] / df['sqft']

# Score de ubicación personalizado
df['custom_location_score'] = (
    (1 - df['distance_to_city'] / df['distance_to_city'].max()) * 0.3 +
    (df['school_rating'] / 5) * 0.5 +
    (1 - df['crime_rate'] / df['crime_rate'].max()) * 0.2
)

# Interacción precio-edad
df['price_age_interaction'] = df['price_per_sqft'] * df['property_age']

# Propiedad nueva y grande
df['new_large_property'] = ((df['property_age'] <= 5) & (df['bedrooms'] >= 4)).astype(int)

# Interacción distancia-escuela
df['distance_school_interaction'] = df['distance_to_city'] * df['school_rating']

Evaluación de correlaciones:

FeatureCorrelación con precio
space_efficiency-0.031
crowded_property0.026
custom_location_score0.009

Interpretación: Aunque las correlaciones lineales son bajas, estas variables capturan relaciones interpretables y potencialmente no lineales entre tamaño, ubicación y valor.

6. Aplicación en Datos Reales: Ames Housing

Aplicamos las mismas técnicas sobre un dataset real para validar transferibilidad:

# Cargar Ames Housing
ames_df = pd.read_csv('AmesHousing.csv')

# Seleccionar variables relevantes
ames_subset = ames_df[['SalePrice', 'GrLivArea', 'LotArea', 
                        'YearBuilt', 'GarageCars', 'Neighborhood']].copy()

# Aplicar features derivadas similares
ames_subset['price_per_sqft'] = ames_subset['SalePrice'] / ames_subset['GrLivArea']
ames_subset['property_age'] = 2024 - ames_subset['YearBuilt']
ames_subset['space_efficiency'] = ames_subset['GrLivArea'] / ames_subset['LotArea']

# Análisis exploratorio
print("Estadísticas Ames Housing:")
print(ames_subset[['price_per_sqft', 'property_age', 'space_efficiency']].describe())

Hallazgos:

  • price_per_sqft: Reflejó correctamente la dispersión del valor por superficie en el mercado real
  • property_age: Mostró relación inversa con el precio (propiedades más viejas valen menos en promedio)
  • space_efficiency: Capturó variaciones marginales relacionadas con el tamaño de lote

Diferencias sintético vs real:

  • Los datos reales presentan ruido, correlaciones espurias y efectos de localización que no aparecen en datos simulados
  • Esto refuerza la necesidad del feature engineering contextual y validación con datos reales

Análisis Crítico y Decisiones

Decisiones de Hiperparámetros

Random Forest:

  • n_estimators=100: Balance entre velocidad y precisión
  • max_depth=10: Permite suficiente complejidad sin overfitting excesivo
  • random_state=42: Reproducibilidad

Mutual Information:

  • random_state=42: Para reproducibilidad en el cálculo de MI

Métricas de Evaluación

Mutual Information:

  • Ventaja: Captura relaciones lineales y no lineales
  • Interpretación: Score más alto = mayor dependencia estadística
  • Uso: Identificar features con relaciones no lineales importantes

Random Forest Importance:

  • Ventaja: Basado en reducción de impureza, considera interacciones
  • Interpretación: Importancia normalizada que suma 1.0
  • Uso: Identificar features más útiles para el modelo específico

Correlación Lineal:

  • Ventaja: Simple e interpretable
  • Desventaja: Solo captura relaciones lineales
  • Uso: Validación complementaria, especialmente útil cuando baja correlación indica necesidad de métodos no lineales

Features Más Importantes

Sintético:

  1. crime_rate (RF: 0.1519)
  2. lot_size (RF: 0.1371)
  3. school_rating (RF: 0.1292)

Ames Housing:

  1. price_per_sqft (consistente y explicativa)
  2. property_age (relación inversa clara con precio)

Sorpresas:

  • Baja correlación de variables esperadas como GarageCars en algunos contextos
  • Alta relevancia de crime_rate, que no es una variable estructural pero afecta significativamente el precio

Posibles Mejoras

  1. Transformaciones avanzadas:

    • PolynomialFeatures para capturar interacciones
    • Box-Cox transformations para normalización más sofisticada
  2. Codificación categórica:

    • One-Hot Encoding para Neighborhood en Ames Housing
    • Target Encoding para variables categóricas de alta cardinalidad
  3. Selección automática:

    • Recursive Feature Elimination (RFE)
    • Lasso para regularización y selección simultánea
  4. Análisis de componentes:

    • PCA para reducir dimensionalidad
    • Análisis de factores para identificar dimensiones latentes

Garantías de Reproducibilidad

  1. Scripts versionados en GitHub con fecha y documentación completa
  2. Random seeds: np.random.seed(42) y random_state=42 en todos los modelos
  3. Documentación completa de cada transformación y justificación
  4. Guardado de outputs intermedios en carpetas organizadas
  5. Pipelines de sklearn para automatización sin pasos manuales

Herramientas y Técnicas Utilizadas

  • pandas: Manipulación y creación de features derivadas
  • numpy: Operaciones numéricas y transformaciones matemáticas
  • scikit-learn: Modelos de machine learning y feature selection
  • matplotlib/seaborn: Visualizaciones estadísticas y distribuciones

Extra: Feature Engineering con Boston Housing Dataset

Para validar la generalización de las técnicas de feature engineering aprendidas, aplicamos las mismas metodologías al dataset Boston Housing, un dataset diferente al Ames Housing utilizado en la práctica principal.

¿Por qué lo elegí?

Elegí el dataset Boston Housing porque:

  1. Es un dataset diferente al Ames Housing, permitiendo validar que las técnicas funcionan en distintos contextos
  2. Tiene características similares (precio de viviendas, variables demográficas) pero estructura diferente
  3. Permite comparar qué features son universales vs específicas del dominio
  4. Es un dataset clásico que permite comparación con literatura existente
  5. Tiene menos features originales, lo que hace que las features derivadas tengan relativamente más impacto

¿Qué esperaba encontrar?

Esperaba encontrar:

  • Que features derivadas similares (ratios, interacciones) también funcionen bien en este dataset
  • Que algunas features sean específicas del dominio (Ames) vs universales
  • Que Mutual Information y Random Forest den rankings similares
  • Que ratios de precio/área sean importantes en ambos datasets
  • Que features de edad/temporalidad tengan peso similar

Metodología

Aplicamos las mismas técnicas de feature engineering:

  1. Ratios: price_per_room, crime_per_capita, nox_per_industry
  2. Transformaciones matemáticas: sqrt_lstat, log_crim
  3. Features de interacción: rm_x_age, lstat_x_age
  4. Features temporales: distance_per_age

Entrenamos un modelo Random Forest con las features originales y otro con las features derivadas, comparando performance.

Comparación de Modelos

Pie de figura: Este gráfico compara las predicciones del modelo original vs el modelo con features derivadas. Lo que me llamó la atención es la mejora significativa en R² (de 0.89 a 0.95) y reducción de MSE (de 7.90 a 3.43). La conclusión es que las features derivadas capturan relaciones no lineales que el modelo original no puede capturar.

Importancia de Features

Pie de figura: Este gráfico muestra la importancia de features según Random Forest (izquierda) y Mutual Information (derecha). Lo que me llamó la atención es que price_per_room es la feature más importante en ambos métodos, confirmando que los ratios son universales. La conclusión es que ambos métodos de evaluación dan rankings similares, validando la robustez de las features derivadas.

Resultados

Mejora con features derivadas:

  • R²: 0.8923 → 0.9533 (+6.84%)
  • MSE: 7.9015 → 3.4264 (-56.64%)

Top 10 Features (Random Forest):

  1. price_per_room: 0.9330
  2. RM: 0.0251
  3. sq_rm: 0.0214
  4. sqrt_lstat: 0.0036
  5. B: 0.0030

Top 10 Features (Mutual Information):

  1. price_per_room: 1.7097
  2. sqrt_lstat: 0.6716
  3. LSTAT: 0.6678
  4. RM: 0.5090
  5. lstat_x_age: 0.5002

¿Qué aprendí?

  1. Validación de generalización: Las técnicas de feature engineering funcionan bien en datasets diferentes. La mejora de R² del 6.84% y reducción de MSE del 56.64% confirma que las técnicas aprendidas son aplicables a otros contextos.

  2. Features universales vs específicas: Ratios de precio/área son importantes en ambos datasets (universales). Features de interacción (RM × AGE) también son valiosas en ambos. Transformaciones logarítmicas ayudan a normalizar distribuciones sesgadas.

  3. Diferencias entre datasets: Boston Housing es más compacto (menos features originales), por lo que las features derivadas tienen relativamente más impacto. Mutual Information y Random Forest dan rankings similares (correlación alta).

  4. Insights específicos: La top feature derivada (price_per_room) tiene una importancia de 0.9330, dominando completamente el modelo. Las features de interacción capturan relaciones no lineales importantes. Transformaciones matemáticas (sqrt, log) mejoran la distribución de features sesgadas.

  5. Recomendaciones: Siempre probar feature engineering en múltiples datasets para validar generalización. Features de ratio suelen ser universales y valiosas. Interacciones entre variables importantes siempre vale la pena explorar. Combinar múltiples métodos de evaluación de importancia (MI + RF) da mejor visión.

Conclusiones y Próximos Pasos

Conclusiones Principales

  1. El feature engineering es estratégico: Combina análisis estadístico, creatividad y comprensión del dominio para transformar datos brutos en features informativas

  2. Features más importantes: price_per_sqft y property_age demostraron ser las más consistentes y explicativas tanto en datos sintéticos como reales

  3. Transformaciones matemáticas son valiosas: Las transformaciones logarítmicas y de raíz cuadrada mejoran distribuciones y capturan relaciones no lineales

  4. Métodos complementarios: Mutual Information y Random Forest Importance revelan diferentes aspectos de importancia, siendo útiles en conjunto

  5. Diferencias entre datos sintéticos y reales: Los datos reales presentan ruido y efectos contextuales que refuerzan la necesidad de validación con datos reales

Próximos Pasos

  1. Explorar técnicas avanzadas:

    • PolynomialFeatures para capturar interacciones entre variables
    • Box-Cox transformations para normalización más sofisticada
    • Target Encoding para variables categóricas de alta cardinalidad
  2. Selección automática de features:

    • Implementar Recursive Feature Elimination (RFE)
    • Evaluar Lasso para regularización y selección simultánea
    • Comparar diferentes estrategias de selección
  3. Validación más robusta:

    • Cross-validation para evaluar impacto de features en performance
    • A/B testing de diferentes conjuntos de features
    • Análisis de sensibilidad de importancia de features
  4. Pipeline reproducible:

    • Construir pipeline de sklearn para automatizar transformaciones
    • Versionar pipelines y documentar cambios
    • Implementar en producción con monitoreo de drift
  5. Análisis de causalidad:

    • Investigar relaciones causales vs correlaciones
    • Evaluar impacto de features en diferentes segmentos
    • Análisis de fairness para evitar sesgos

Nota: Este análisis fue realizado con fines educativos utilizando datasets sintéticos y el dataset Ames Housing de Kaggle.