lección 6
Comparaciones temporales y el efecto base
Contra el mes anterior, contra el año anterior, y por qué un mal punto de partida engaña.
⏱ 55 min
En abril de 2021 la producción industrial española creció un 49,7 % interanual. El titular sonaba a milagro. La realidad era aritmética: abril de 2020 fue el mes más duro del confinamiento, con las fábricas paradas. Crecer un 49 % sobre un suelo artificial no es un logro, es una ilusión óptica del denominador. Este fenómeno tiene nombre — efecto base — y no es exclusivo de la macroeconomía: aparece cada vez que comparas contra un periodo anormalmente bajo (o alto). Un e-commerce que sufrió una caída del servidor en marzo del año pasado mostrará un +300 % en marzo de este año sin haber vendido ni un pedido más de lo habitual. El porcentaje es correcto. La conclusión que invita a sacar es falsa. Y eso lo convierte en una de las trampas más peligrosas del análisis temporal: no da error, no rompe nada y suena bien en una presentación.
Las comparaciones temporales son la columna vertebral del análisis de negocio. Cada vez que alguien dice "hemos crecido", "hemos caído" o "estamos estancados", hay una comparación implícita: crecido respecto a QUÉ. El periodo anterior, el mismo mes del año pasado, la media de los últimos seis meses. Elegir mal el comparador — o no entender sus limitaciones — convierte un número correcto en una conclusión falsa. Y eso es exactamente lo que vamos a aprender a evitar.
### Por qué comparamos con el pasado
Un número solo no dice nada. "Hemos facturado 150.000 euros este mes". Vale. ¿Eso es bueno? ¿Es malo? ¿Es normal? No lo sabes hasta que lo pones al lado de otro número: lo que facturaste el mes pasado, lo que facturaste en el mismo mes hace un año, lo que presupuestaste. Comparar es lo que convierte un dato en información. Y la forma más natural de comparar es contra el tiempo: ayer, la semana pasada, el trimestre anterior.
Históricamente, los informes financieros mensuales se inventaron para esto: el cierre contable del mes te daba un punto de comparación fijo. Cuando llegaron las hojas de cálculo en los años 80, la gente empezó a poner "este mes vs el anterior" en la celda de al lado porque era fácil. Y cuando los dashboards en tiempo real se hicieron posibles en los 2000, aparecieron los tres acrónimos que dominan hoy cualquier reunión de negocio: WoW (week-over-week, semana contra semana anterior), MoM (month-over-month, mes contra mes anterior), y YoY (year-over-year, contra el mismo periodo del año pasado). Son la jerga del analista, y detrás de cada uno hay una decisión sobre qué comparar y por qué.
### MoM, WoW, YoY: cuándo usar cada uno
Cada comparación tiene un caso de uso natural y un punto ciego. MoM (mes contra mes anterior) es la más intuitiva: "vendimos más que el mes pasado". Pero tiene un problema grave: la estacionalidad. Si vendes helados, febrero siempre será peor que julio, y comparar febrero con enero no te dice nada útil — solo que hace frío. WoW (semana contra semana) es rápida y detecta cambios bruscos, pero hereda el ruido de un solo día malo. YoY (contra el mismo mes del año pasado) es la que elimina la estacionalidad: comparas julio con julio, diciembre con diciembre. El precio es que un año es mucho tiempo — puede que el negocio haya cambiado tanto que la comparación no tenga sentido.
- MoM (mes contra mes anterior): detecta cambios recientes. Falla con estacionalidad. Usa para productos sin ciclo anual marcado.
- WoW (semana contra semana anterior): detecta anomalías rápido. Muy ruidosa. Usa para operaciones diarias y alertas.
- YoY (contra el mismo periodo hace un año): elimina estacionalidad. Falla si el negocio ha cambiado de tamaño o modelo. Usa para informes estratégicos.
- QoQ (quarter-over-quarter): a mitad de camino entre MoM y YoY. Suficiente para suavizar meses, no tanto para eliminar estacionalidad.
Consejo de senior: en la práctica, un buen informe muestra DOS comparaciones a la vez: MoM para la tendencia reciente y YoY para el contexto estructural. Si las dos suben, tienes crecimiento real. Si MoM sube pero YoY baja, puede que estés recuperándote de un mal mes anterior (efecto base). Si MoM baja pero YoY sube, puede ser estacionalidad normal. Las dos juntas cuentan la historia completa.
### Calcular variaciones con LAG
Ya conoces LAG de cuando vimos comparaciones entre filas. Ahora lo vamos a usar para su caso de uso más importante en el mundo real: calcular la variación de una métrica respecto al periodo anterior. La idea es sencilla: para cada fila (un mes, una semana), traes el valor de la fila anterior, y con esos dos números calculas la variación absoluta (cuánto ha cambiado en unidades) y la variación porcentual (cuánto ha cambiado en proporción al punto de partida).
1-- Variacion MoM de ingresos mensuales2WITH mensual AS (3 SELECT4 DATE_TRUNC('month', fecha) AS mes,5 SUM(importe) AS ingresos6 FROM ventas7 GROUP BY DATE_TRUNC('month', fecha)8)9SELECT10 mes,11 ingresos,12 LAG(ingresos) OVER (ORDER BY mes) AS ingresos_mes_anterior,13 ingresos - LAG(ingresos) OVER (ORDER BY mes) AS variacion_absoluta,14 ROUND(15 100.0 * (ingresos - LAG(ingresos) OVER (ORDER BY mes))16 / NULLIF(LAG(ingresos) OVER (ORDER BY mes), 0),17 118 ) AS variacion_pct19FROM mensual20ORDER BY mes;
LAG(ingresos) OVER (ORDER BY mes) trae el valor del mes anterior. La fórmula del porcentaje: (actual - anterior) / anterior * 100.
### YoY: comparar con el mismo mes del año pasado
Para la comparación interanual, LAG con offset 12 es tu mejor amigo. Si tus datos están agrupados por mes y ordenados cronológicamente, LAG(ingresos, 12) te trae el valor de hace exactamente 12 filas — es decir, el mismo mes del año anterior. Esta es la comparación que elimina la estacionalidad: julio contra julio, diciembre contra diciembre.
1-- Variacion YoY: contra el mismo mes del ano anterior2WITH mensual AS (3 SELECT4 DATE_TRUNC('month', fecha) AS mes,5 SUM(importe) AS ingresos6 FROM ventas7 GROUP BY DATE_TRUNC('month', fecha)8)9SELECT10 mes,11 ingresos,12 LAG(ingresos, 12) OVER (ORDER BY mes) AS ingresos_yoy,13 ROUND(14 100.0 * (ingresos - LAG(ingresos, 12) OVER (ORDER BY mes))15 / NULLIF(LAG(ingresos, 12) OVER (ORDER BY mes), 0),16 117 ) AS variacion_yoy_pct18FROM mensual19ORDER BY mes;
LAG(ingresos, 12) salta 12 meses atrás. NULLIF protege contra división por cero.
Consejo de senior: NULLIF es tu salvavidas con variaciones porcentuales. División por cero no es un caso raro — es el caso exacto del efecto base. Si un producto no existía hace un año, o si una tienda estaba cerrada, el denominador es cero. NULLIF(x, 0) convierte ese cero en NULL y la división devuelve NULL en vez de romper. Siempre envuelve el denominador con NULLIF cuando calcules variaciones.
### El efecto base: cuando el denominador miente
Ahora entramos en el concepto que da nombre a esta lección y que separa al analista junior del senior. El efecto base ocurre cuando el periodo de referencia (el denominador de tu variación porcentual) es anormalmente bajo o anormalmente alto, lo que distorsiona el porcentaje de cambio hasta hacerlo inútil o engañoso.
La analogía: imagina que tu amigo pesaba 50 kg el año pasado (estaba enfermo). Este año pesa 65 kg. Ha "engordado un 30%". Suena alarmante. Pero 65 kg para un adulto de 1,75 m es perfectamente saludable — lo anormal era el 50, no el 65. El porcentaje de cambio te está contando una historia dramática sobre una situación normal, porque el punto de partida era el que estaba mal. Eso es efecto base.
El efecto base es especialmente traicionero porque el número es CORRECTO. El 200% está bien calculado. La división es correcta. El código no tiene errores. Pero la conclusión que se saca de él es falsa. Y eso lo hace invisible para cualquier validación técnica — solo lo detecta alguien que CONOCE el contexto del negocio y se pregunta "¿qué pasó en el periodo base?".
Ejemplos reales que se han visto en los últimos años: toda empresa que comparó 2021 con 2020 vio crecimientos estratosféricos en muchas métricas, porque 2020 fue el año del confinamiento y la base estaba destruida. Restaurantes con +500% de facturación YoY que en realidad seguían por debajo de 2019. Tiendas online con -40% YoY en 2022 que en realidad seguían muy por encima de 2019. El denominador les mentía a todos.
Periodos anómalos comunes que generan efecto base: pandemias y crisis (2020), Black Friday y Navidad (noviembre-diciembre), lanzamientos de producto (el mes del lanzamiento infla todo), caídas técnicas (CRM caído, web caída, pasarela de pago rota), huelgas o fenómenos meteorológicos. Antes de presentar una variación YoY, abre el mes base y pregúntate: "¿fue un mes NORMAL?". Si no lo fue, anota el contexto junto al número. Nunca presentes un porcentaje sin mirar el denominador.
### Variación absoluta vs porcentual: cuándo usar cada una
El efecto base es un problema de la variación porcentual. La variación absoluta (cuántos euros más o menos) no sufre este problema — pero tiene el suyo propio: no da escala. "Hemos crecido 50.000 euros" significa algo muy distinto si facturas 100.000 al mes (has crecido un 50%) que si facturas 5 millones (has crecido un 1%). La variación absoluta te dice EL TAMAÑO del cambio en unidades reales. La porcentual te dice LA PROPORCIÓN del cambio respecto al punto de partida. Necesitas las dos.
La regla práctica: presenta SIEMPRE las dos juntas. "Hemos crecido 50.000 euros (+8% MoM)" es mucho mejor que cualquiera de las dos solas. El absoluto le dice al director financiero cuánto ingreso de más hay en caja. El porcentaje le dice al director de marketing si su campaña ha tenido impacto proporcional al esfuerzo. Nunca un porcentaje solo. Nunca un absoluto solo.
### Detectar el efecto base con SQL
Un analista responsable no presenta una variación YoY sin comprobar si el periodo base era normal. La forma de hacerlo: comparar el periodo base con la media de ese mismo mes en los años disponibles. Si el periodo base se desvía mucho de la media histórica, hay efecto base y debes anotarlo. Veamos cómo implementar esa comprobación.
1-- Detectar efecto base: marcar meses anomalos2WITH mensual AS (3 SELECT4 DATE_TRUNC('month', fecha) AS mes,5 EXTRACT(MONTH FROM fecha) AS num_mes,6 SUM(importe) AS ingresos7 FROM ventas8 GROUP BY DATE_TRUNC('month', fecha), EXTRACT(MONTH FROM fecha)9),10media_historica AS (11 SELECT12 num_mes,13 AVG(ingresos) AS media_mes,14 STDDEV(ingresos) AS desviacion_mes15 FROM mensual16 GROUP BY num_mes17)18SELECT19 m.mes,20 m.ingresos,21 LAG(m.ingresos, 12) OVER (ORDER BY m.mes) AS base_yoy,22 ROUND(100.0 * (m.ingresos - LAG(m.ingresos, 12) OVER (ORDER BY m.mes))23 / NULLIF(LAG(m.ingresos, 12) OVER (ORDER BY m.mes), 0), 1) AS var_yoy_pct,24 CASE25 WHEN ABS(LAG(m.ingresos, 12) OVER (ORDER BY m.mes) - h.media_mes)26 > 1.5 * h.desviacion_mes27 THEN 'EFECTO BASE - periodo anomalo'28 ELSE 'OK'29 END AS alerta_base30FROM mensual m31JOIN media_historica h ON h.num_mes = EXTRACT(MONTH FROM m.mes)32ORDER BY m.mes;
Si el periodo base se desvía más de 1,5 desviaciones típicas de la media histórica de ese mes, se marca como anómalo.
### Qué hacer cuando hay efecto base
Detectar el efecto base es el primer paso. El segundo es decidir qué haces con él. No puedes simplemente borrar el número — el director comercial ya lo ha visto y quiere saber por qué "el crecimiento no es real". Tienes tres estrategias, y la correcta depende del contexto:
- 01.Comparar con un periodo "normal" alternativo: en vez de YoY contra 2020 (anómalo), compara contra 2019. Es lo que hicieron muchas empresas en 2021: presentaban "vs 2019" porque era el último año comparable.
- 02.Añadir contexto narrativo: presenta el número YoY pero acompáñalo de una nota: "El +200% refleja la normalización tras la caída técnica de marzo 2023, no un crecimiento orgánico". El número sigue ahí, pero su interpretación cambia.
- 03.Usar la variación absoluta como ancla: "Ingresos de marzo: 36.000 EUR. Variación vs media histórica de marzo: -5%. Variación YoY: +200% (base anómala por caída de CRM)". La media histórica da el contexto que el YoY no puede dar.
Consejo de senior: cuando detectes efecto base, NO lo ocultes. Lo peor que puedes hacer es no presentar el YoY "porque está mal". Tu trabajo es presentar el número CON su contexto. El director necesita saber que el +200% existe, por qué no es lo que parece, y cuál es la forma correcta de leer la situación. Eso es más valioso que un número limpio.
### Comparaciones WoW con LAG sobre datos semanales
Para comparaciones semanales, el patrón es el mismo pero agrupando por semana en vez de por mes. DuckDB tiene DATE_TRUNC que acepta week como unidad, lo que simplifica mucho el cálculo. La variación WoW es la favorita de los equipos de producto digital porque detecta cambios rápido: si un despliegue rompe algo el martes, el WoW del miércoles ya lo refleja.
1-- Variacion WoW (semana contra semana anterior)2WITH semanal AS (3 SELECT4 DATE_TRUNC('week', fecha) AS semana,5 SUM(importe) AS ingresos6 FROM ventas7 GROUP BY DATE_TRUNC('week', fecha)8)9SELECT10 semana,11 ingresos,12 LAG(ingresos) OVER (ORDER BY semana) AS ingresos_sem_anterior,13 ingresos - LAG(ingresos) OVER (ORDER BY semana) AS var_absoluta,14 ROUND(15 100.0 * (ingresos - LAG(ingresos) OVER (ORDER BY semana))16 / NULLIF(LAG(ingresos) OVER (ORDER BY semana), 0),17 118 ) AS var_wow_pct19FROM semanal20ORDER BY semana;
El patrón es idéntico al MoM: cambia DATE_TRUNC(month) por DATE_TRUNC(week).
### Ejemplo guiado completo: informe con MoM y YoY
Vamos a construir la consulta que un analista usaría en la vida real para preparar el informe mensual de ventas: cada mes con sus ingresos, la variación MoM (mes contra mes anterior), la variación YoY (contra el mismo mes del año pasado), y una columna que marque si hay efecto base. Es la query que alimenta la diapositiva que el director comercial presenta cada primer lunes del mes.
1-- Informe mensual completo: MoM + YoY + alerta de efecto base2WITH mensual AS (3 SELECT4 DATE_TRUNC('month', fecha) AS mes,5 SUM(importe) AS ingresos6 FROM ventas7 GROUP BY DATE_TRUNC('month', fecha)8)9SELECT10 mes,11 ingresos,12 -- MoM13 LAG(ingresos, 1) OVER (ORDER BY mes) AS base_mom,14 ROUND(100.0 * (ingresos - LAG(ingresos, 1) OVER (ORDER BY mes))15 / NULLIF(LAG(ingresos, 1) OVER (ORDER BY mes), 0), 1) AS var_mom_pct,16 -- YoY17 LAG(ingresos, 12) OVER (ORDER BY mes) AS base_yoy,18 ROUND(100.0 * (ingresos - LAG(ingresos, 12) OVER (ORDER BY mes))19 / NULLIF(LAG(ingresos, 12) OVER (ORDER BY mes), 0), 1) AS var_yoy_pct,20 -- Alerta: si la base YoY es < 50% de la media global, hay efecto base21 CASE22 WHEN LAG(ingresos, 12) OVER (ORDER BY mes)23 < 0.5 * AVG(ingresos) OVER ()24 THEN 'BASE BAJA'25 WHEN LAG(ingresos, 12) OVER (ORDER BY mes)26 > 1.5 * AVG(ingresos) OVER ()27 THEN 'BASE ALTA'28 ELSE NULL29 END AS alerta30FROM mensual31ORDER BY mes;
MoM para tendencia reciente, YoY para contexto interanual, y la alerta para saber cuándo no fiarte del porcentaje.
### Resumen
- Toda métrica de negocio necesita un comparador temporal para tener significado. Los tres estándar son MoM, WoW y YoY.
- LAG con offset 1 da el periodo anterior; con offset 12 (en datos mensuales) da el mismo mes del año pasado.
- La variación porcentual es vulnerable al efecto base: un denominador anómalo distorsiona el resultado.
- Presenta SIEMPRE variación absoluta y porcentual juntas, y acompáñalas de dos comparadores (MoM + YoY).
- Antes de presentar un YoY, comprueba si el periodo base fue normal. Si no lo fue, anótalo.
- NULLIF protege contra división por cero — úsalo siempre en el denominador de variaciones porcentuales.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...