Ingeniería S&OP II: Demand Planning, de la Adivinación a la Probabilidad

Published: (March 7, 2026 at 03:36 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Resumen Ejecutivo

Un forecast probabilístico no te dice “venderás 100”. Te dice “con un 95 % de probabilidad, venderás entre 35 y 157”. Esa banda de incertidumbre es la base matemática para calcular tu Safety Stock sin recurrir a reglas de dedo.

Diseño de la tabla demand_forecasts

CREATE TABLE demand_forecasts (
    id              UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    execution_date  DATE NOT NULL,     -- Cuándo corrimos el modelo
    ds              DATE NOT NULL,     -- Fecha futura predicha
    yhat            NUMERIC NOT NULL,  -- Predicción central
    yhat_lower      NUMERIC NOT NULL,  -- Límite inferior (Safety Stock)
    yhat_upper      NUMERIC NOT NULL,  -- Límite superior (Riesgo)
    model_version   TEXT NOT NULL,     -- Trazabilidad
    UNIQUE(execution_date, ds)         -- Un forecast por ejecución y fecha
);

¿Por qué execution_date?
Dentro de 6 meses, cuando quieras auditar la precisión del modelo, necesitarás saber cuándo se hizo la predicción y contra qué datos reales se comparó. Esta práctica, conocida como Snapshotting en MLOps, permite evaluar el drift del modelo a lo largo del tiempo. Sin esta columna tienes un modelo; con ella tienes un sistema auditable.

Modelo Prophet

Prophet es un motor de series temporales desarrollado por Meta, diseñado para datos de negocio irregulares: gaps, festivos y cambios de tendencia. Es ideal para una cadena de suministro real.

Clase ProphetPredictor

def train_model(self, country_code='ES'):
    """
    Entrena Prophet con dos configuraciones clave para S&OP:
    - interval_width: Ancho del intervalo de confianza
    - country_holidays: Contexto operativo del país
    """
    self.model = Prophet(
        interval_width=0.95,     # Intervalo de confianza del 95 %
        weekly_seasonality=True,
        yearly_seasonality=True
    )
    # Festivos que alteran patrones de carga/descarga
    self.model.add_country_holidays(country_name=country_code)
    self.model.fit(self.ts_df)

Decisiones de ingeniería

  • interval_width=0.95: No es decorativo. El límite superior (yhat_upper) representa la demanda máxima probable con un 95 % de confianza y sirve directamente para calcular el Safety Stock:
    Safety Stock = yhat_upper - yhat.

  • add_country_holidays('ES'): Los festivos son anomalías operativas (cierre de fábrica, almacén, transporte). Si el modelo ignora, por ejemplo, el 15 de agosto en España, interpretará la caída de pedidos como una tendencia a la baja, corrompiendo la predicción de septiembre.

Visualización y XAI (Explainable AI)

Los puntos negros son tu demanda histórica real. En la visualización:

  • Línea azul: Predicción central (yhat).
  • Banda sombreada: Intervalo de confianza al 95 %.

A mayor volatilidad histórica, más ancha es la banda → más Safety Stock necesitas → más capital inmovilizas. Esta banda es la conversación que deberías tener con tu CFO.

El modelo separa:

  • Tendencia (crecimiento o decrecimiento del negocio)
  • Estacionalidad (picos por época del año)
  • Efecto de festivos

Esto permite al Director General aprobar el plan de operaciones con evidencia visual.

Entorno interactivo

He preparado un notebook aislado donde puedes entrenar el modelo sobre un snapshot anonimizado de los datos limpiados en el Capítulo 1. No necesitas instalar Python, Prophet ni configurar Supabase; solo un navegador.

  • 📎 Abrir el Notebook interactivo en Google Colab
    Ejecuta las celdas, observa cómo se genera un forecast probabilístico con bandas de incertidumbre y experimenta modificando horizonte, país e intervalo de confianza.

Próximos pasos

En el Capítulo 3 introduciremos Optimización Matemática (PuLP) y Teoría de Restricciones para transformar las predicciones en decisiones de suministro: cuánto comprar, cuándo producir y cómo distribuir recursos finitos minimizando costes.

La diferencia entre un Director de Operaciones que reacciona y uno que decide es un modelo matemático entre los datos y la acción.

0 views
Back to Blog

Related posts

Read more »

AI, Humanity, and the Loops We Break

🌅 Echoes of Experience — Standing in the Horizon There was a time when chaos shaped me. But the moment I chose myself—truly chose myself—everything shifted. I...