Ingeniería de Características: Guía Exhaustiva y Práctica

OLTP vs OLAP: Fundamentos de los Sistemas de Datos

Diferencias Fundamentales

OLTP (Online Transaction Processing) y OLAP (Online Analytical Processing) representan dos paradigmas distintos en el manejo de datos empresariales. Mientras que OLTP está diseñado para procesar transacciones en tiempo real de manera rápida y eficiente, OLAP se enfoca en el análisis de datos históricos agregados para la toma de decisiones estratégicas.

Características de OLTP

Los sistemas OLTP se caracterizan por:

  • Procesamiento transaccional en tiempo real con operaciones CRUD frecuentes
  • Estructura de datos normalizada para minimizar redundancia
  • Tiempos de respuesta rápidos (milisegundos) para transacciones individuales
  • Datos operacionales actuales de un solo origen
  • Arquitectura de tres niveles: presentación, lógica de negocio y almacenamiento

Ejemplo práctico de OLTP:

# Sistema de ventas en línea
transacciones = pd.DataFrame({
    'transaction_id': [1, 2, 3, 4, 5],
    'customer_id': [101, 102, 101, 103, 102],
    'product_id': [201, 202, 203, 201, 202],
    'quantity': [2, 1, 3, 1, 2],
    'price': [25.99, 15.50, 35.00, 25.99, 15.50],
    'timestamp': pd.to_datetime(['2023-05-20 10:30:00', '2023-05-20 11:15:00', 
                                '2023-05-20 12:00:00', '2023-05-20 14:30:00', 
                                '2023-05-20 15:45:00'])
})

Características de OLAP

Los sistemas OLAP presentan:

  • Análisis multidimensional de datos históricos agregados
  • Estructura desnormalizada optimizada para consultas complejas
  • Tiempos de respuesta más lentos (segundos o minutos) pero con análisis profundo
  • Datos de múltiples fuentes integrados en cubos dimensionales
  • Operaciones analíticas: Roll-up, Drill-down, Slice, Dice y Pivot

Ejemplo práctico de OLAP:

# Cubo de análisis de ventas
cubo_ventas = pd.DataFrame({
    'año': [2023, 2023, 2023, 2023],
    'mes': ['Mayo', 'Mayo', 'Junio', 'Junio'],
    'region': ['Norte', 'Sur', 'Norte', 'Sur'],
    'categoria_producto': ['Electrónicos', 'Ropa', 'Electrónicos', 'Ropa'],
    'total_ventas': [15000, 8500, 18000, 9200],
    'unidades_vendidas': [150, 85, 180, 92]
})

Data Warehouse y Proceso ETL

Conceptos Fundamentales del Data Warehouse

Un Data Warehouse es un almacén centralizado que integra datos de múltiples fuentes para análisis y reporting. Utiliza una arquitectura optimizada para consultas analíticas complejas, típicamente implementando esquemas en estrella o copo de nieve.

El Proceso ETL (Extract, Transform, Load)

El proceso ETL constituye el pipeline fundamental para mover datos desde sistemas operacionales hacia el data warehouse.

1. Extract (Extracción)

La fase de extracción recopila datos de múltiples fuentes:

  • Bases de datos transaccionales
  • APIs y servicios web
  • Archivos planos (CSV, XML, JSON)
  • Sistemas legacy
def extraer_datos():
    # Múltiples fuentes de datos
    crm = pd.read_csv('crm_data.csv')
    ventas = pd.read_csv('sales_data.csv') 
    productos = pd.read_csv('product_data.csv')
    return crm, ventas, productos

2. Transform (Transformación)

La transformación aplica reglas de negocio para limpiar, enriquecer y estructurar los datos:

  • Limpieza: eliminación de duplicados, valores nulos
  • Normalización: estandarización de formatos
  • Agregación: cálculos y métricas derivadas
  • Validación: aplicación de reglas de calidad
def transformar_datos(crm, ventas, productos):
    # Limpieza de datos
    crm['email'] = crm['email'].str.lower()
    ventas['precio'] = pd.to_numeric(ventas['precio'], errors='coerce')
    
    # Unión de tablas
    datos_unidos = ventas.merge(crm, on='cliente_id')
    datos_unidos = datos_unidos.merge(productos, on='producto_id')
    
    # Crear características derivadas
    datos_unidos['categoria_precio'] = pd.cut(datos_unidos['precio'], 
                                             bins=[0, 100, 500, 1000], 
                                             labels=['Bajo', 'Medio', 'Alto'])
    return datos_unidos

3. Load (Carga)

La carga transfiere los datos transformados al destino final, típicamente separando en tablas de hechos y dimensiones:

def cargar_warehouse(datos_transformados):
    # Separar en tablas de hechos y dimensiones
    fact_ventas = datos_transformados[['venta_id', 'cliente_id', 
                                      'producto_id', 'precio', 'fecha']]
    dim_clientes = datos_transformados[['cliente_id', 'nombre', 
                                       'email']].drop_duplicates()
    
    # Cargar al data warehouse
    fact_ventas.to_sql('fact_ventas', engine, if_exists='replace')
    dim_clientes.to_sql('dim_clientes', engine, if_exists='replace')

Tabla Analítica de Datos (TAD): El Corazón del Machine Learning

Principios Fundamentales de la TAD

Una Tabla Analítica de Datos (TAD) representa la estructura optimizada para algoritmos de machine learning. Siguiendo los principios establecidos:

  • Una fila = una unidad de análisis (cliente, transacción, producto)
  • Matriz de valores continuos, finitos y no nulos
  • Cada columna representa una característica medible
  • Preparada para alimentar directamente los algoritmos

La Analogía de Cocina en Machine Learning

Esta analogía clarifica el rol de cada componente:

  • 🥕 Ingredientes (Variables): Las características extraídas de los datos
  • 📝 Receta (Modelo): El algoritmo que combina las características
  • 🍽️ Platillo (Predicción): El resultado final del modelo
def crear_tad(datos_raw):
    # 1. Definir unidad de análisis (ej: cliente)
    tad = datos_raw.groupby('cliente_id').agg({
        # Variables demográficas
        'edad': 'first',
        'ingresos': 'first', 
        
        # Variables de comportamiento (agregadas)
        'num_compras': 'count',
        'total_gastado': 'sum',
        'precio_promedio': 'mean'
    }).reset_index()
    
    # 2. Crear variables derivadas
    tad['gasto_promedio_mensual'] = tad['total_gastado'] / 12
    tad['frecuencia_compra'] = tad['num_compras'] / 365
    
    # 3. Verificar calidad TAD
    print(f"Valores nulos: {tad.isnull().sum().sum()}")
    print(f"Valores infinitos: {np.isinf(tad.select_dtypes(include=[np.number])).sum().sum()}")
    
    return tad

Ejemplo específico de la analogía:

  • Ingrediente principal: edad = 45 años
  • Ingrediente secundario: ingresos =$52,000
  • Platillo resultante: probabilidad_compra = 0.73

Reducción de Dimensionalidad: PCA y Selección de Características

Análisis de Componentes Principales (PCA)

PCA es un método clásico que proyecta datos de alta dimensión a un espacio de menor dimensión, maximizando la varianza conservada. El método busca una transformación ortogonal que identifica las direcciones de máxima variabilidad en los datos.

Fundamentos Matemáticos de PCA

El proceso matemático involucra:

  1. Estandarización de las variables originales
  2. Cálculo de la matriz de covarianza
  3. Descomposición en valores y vectores propios
  4. Selección de componentes principales basada en varianza explicada
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

def aplicar_pca(X, varianza_objetivo=0.95):
    # Estandarizar datos
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # PCA completo para analizar varianza
    pca_full = PCA()
    pca_full.fit(X_scaled)
    
    # Encontrar componentes para varianza objetivo
    cumulative_variance = np.cumsum(pca_full.explained_variance_ratio_)
    n_components = np.argmax(cumulative_variance >= varianza_objetivo) + 1
    
    # Aplicar PCA reducido
    pca_final = PCA(n_components=n_components)
    X_reducido = pca_final.fit_transform(X_scaled)
    
    print(f"Reducción: {X.shape}{n_components} características")
    print(f"Varianza conservada: {cumulative_variance[n_components-1]:.1%}")
    
    return X_reducido, pca_final

Ventajas de PCA

  • Maximiza la varianza capturada en cada componente
  • Garantiza ortogonalidad entre componentes
  • Reduce la multicolinealidad efectivamente
  • Útil para visualización de datos complejos

Selección de Características (Feature Selection)

La selección de características identifica las variables más relevantes sin crear combinaciones lineales. Existen tres enfoques principales:

Métodos de Filtrado

Evalúan características independientemente del modelo usando métricas estadísticas:

  • Correlación de Pearson
  • Test Chi-cuadrado
  • Información Mutua

Métodos Wrapper

Utilizan el rendimiento del modelo como criterio de evaluación:

  • Sequential Feature Selector
  • Recursive Feature Elimination

Métodos Embedded

Integran la selección durante el entrenamiento:

  • LASSO Regression
  • Random Forest Feature Importance
from sklearn.feature_selection import SelectKBest, f_regression

def seleccionar_caracteristicas(X, y, k=10):
    # Selección basada en F-score
    selector = SelectKBest(score_func=f_regression, k=k)
    X_selected = selector.fit_transform(X, y)
    
    # Características seleccionadas
    selected_features = X.columns[selector.get_support()].tolist()
    print(f"Características seleccionadas: {selected_features}")
    
    return X_selected, selector

Comparación: PCA vs Selección de Características

AspectoPCASelección de Características
InterpretabilidadBaja (componentes sintéticos)Alta (variables originales)
Varianza capturadaMáxima por diseñoVariable según método
MulticolinealidadElimina completamentePuede persistir
Complejidad computacionalMediaVariable según método
Uso recomendadoDatos muy correlacionadosInterpretabilidad importante

Series de Tiempo: Análisis de Acciones de Apple

Características Específicas de Series Temporales

El análisis de series de tiempo requiere características especializadas que capturen patrones temporales, tendencias y volatilidad. Para el análisis de acciones como Apple, se emplean múltiples categorías de características:

Indicadores Técnicos Fundamentales

Medias Móviles

Las medias móviles suavizan fluctuaciones revelando tendencias subyacentes:

def crear_medias_moviles(df, precio_col='precio_cierre'):
    # Medias móviles simples
    for window in [5, 10, 20, 50, 200]:
        df[f'ma_{window}'] = df[precio_col].rolling(window).mean()
        df[f'precio_vs_ma{window}'] = (df[precio_col] / df[f'ma_{window}'] - 1) * 100
    
    return df

Indicadores de Volatilidad

Miden la variabilidad de los precios:

# Volatilidad histórica
df['volatilidad_20'] = df['retorno_diario'].rolling(20).std()
df['volatilidad_anualizada'] = df['volatilidad_20'] * np.sqrt(252)

# Bandas de Bollinger
df['bb_superior'] = df['ma_20'] + (2 * df['precio_cierre'].rolling(20).std())
df['bb_inferior'] = df['ma_20'] - (2 * df['precio_cierre'].rolling(20).std())
df['bb_posicion'] = (df['precio_cierre'] - df['bb_inferior']) / (df['bb_superior'] - df['bb_inferior'])

RSI (Relative Strength Index)

Mide condiciones de sobrecompra/sobreventa:

def calcular_rsi(df, precio_col='precio_cierre', window=14):
    delta = df[precio_col].diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
    rs = gain / loss
    df['rsi'] = 100 - (100 / (1 + rs))
    return df

Características de Retraso (Lags)

Las características lag capturan dependencias temporales:

def crear_lags(df, columnas, max_lag=10):
    for col in columnas:
        for lag in range(1, max_lag + 1):
            df[f'{col}_lag_{lag}'] = df[col].shift(lag)
    return df

Ejemplo Completo: Análisis de Apple

Aplicando el análisis completo a datos de Apple:

def analisis_completo_apple(df):
    # 1. Retornos básicos
    df['retorno_diario'] = df['precio_cierre'].pct_change()
    df['log_retorno'] = np.log(df['precio_cierre'] / df['precio_cierre'].shift(1))
    
    # 2. Medias móviles y señales
    df = crear_medias_moviles(df)
    
    # 3. Volatilidad y riesgo
    df['volatilidad_20'] = df['retorno_diario'].rolling(20).std()
    df['var_95'] = df['retorno_diario'].rolling(50).quantile(0.05)  # Value at Risk
    
    # 4. Indicadores técnicos
    df = calcular_rsi(df)
    
    # 5. Características temporales
    df['dia_semana'] = df['fecha'].dt.dayofweek
    df['mes'] = df['fecha'].dt.month
    df['trimestre'] = df['fecha'].dt.quarter
    
    # 6. Variables lag
    df = crear_lags(df, ['precio_cierre', 'retorno_diario', 'volatilidad_20'], max_lag=5)
    
    return df

Métricas Relevantes para Apple

Para el período analizado, los datos muestran:

  • Retorno anualizado: 11.2%
  • Volatilidad anualizada: 30.1%
  • Ratio Sharpe estimado: 0.39
  • Rango de precios:99.6099.60 -153.00

TAD para Series Temporales

La creación de TAD para series temporales utiliza ventanas deslizantes:

def crear_tad_temporal(df, ventana=60):
    tad_rows = []
    for i in range(ventana, len(df)):
        row = {}
        ventana_data = df.iloc[i-ventana:i]
        
        # Estadísticas de ventana
        row['precio_medio'] = ventana_data['precio_cierre'].mean()
        row['volatilidad_media'] = ventana_data['volatilidad_20'].mean()
        row['rsi_promedio'] = ventana_data['rsi'].mean()
        
        # Objetivo (día siguiente)
        row['precio_siguiente'] = df.iloc[i]['precio_cierre']
        
        tad_rows.append(row)
    return pd.DataFrame(tad_rows)

Plantillas de Código Implementadas

Se han desarrollado plantillas completas para cada concepto, disponibles en el archivo adjunto que incluye:

  • Código OLTP vs OLAP: Estructuras transaccionales y analíticas
  • Proceso ETL completo: Extract, Transform, Load
  • Creación de TAD: Tabla Analítica optimizada
  • Reducción dimensional: PCA y selección de características
  • Series temporales: Análisis técnico completo

Estas plantillas proporcionan una base sólida para implementar ingeniería de características en proyectos reales de machine learning, siguiendo las mejores prácticas de la industria y los principios fundamentales establecidos en el transcript de ingeniería de características.

Ingeniería de Características - Fundamentos de Machine Learning y Análisis de Datos

Author

Juan Fuentes

Publish Date

04 - 29 - 2023