En los negocios, muchas variables no se observan una sola vez: se registran día tras día, semana tras semana, mes tras mes o año tras año. Ventas, demanda, inventarios, precios, tasas de cambio, número de clientes, exportaciones, costos logísticos y tiempos de entrega son ejemplos naturales.
Cuando el orden temporal importa, ya no basta con resumir los datos con una media o una desviación estándar. La pregunta cambia:
¿Cómo ha evolucionado la variable en el tiempo y qué patrones pueden explicar su comportamiento?
Esta sesión introduce el análisis de series de tiempo con un enfoque aplicado a los negocios. No pretende desarrollar todavía modelos ARIMA ni técnicas avanzadas de pronóstico. El objetivo es aprender a leer, describir, separar y modelar los principales patrones temporales.
Idea guía de la sesión
Una serie de tiempo no se estudia solo para describir el pasado. En negocios se analiza para entender patrones, anticipar comportamientos y apoyar decisiones sobre inventario, ventas, compras, precios y capacidad.
Al finalizar, se espera que el estudiante pueda:
Cowpertwait y Metcalfe (2009) señalan que muchas variables de la ciencia, la ingeniería y el comercio son medidas secuencialmente en el tiempo. Una idea central de su definición es:
“When a variable is measured sequentially in time … the resulting data form a time series.”
En términos prácticos:
Una serie de tiempo es un conjunto de observaciones de una variable registradas en orden temporal.
La podemos representar mediante
\[ \{Y_t\}=\{Y_1,Y_2,\ldots,Y_n\}, \]
donde:
Ejemplos empresariales
Mini ejemplo aplicado
Si una empresa registra las ventas totales de cada mes durante 5 años, entonces tiene una serie de tiempo mensual. Si además registra el número de pedidos cada día, también dispone de una serie diaria. Ambas son series de tiempo, pero responden preguntas distintas.
El tiempo entre dos observaciones consecutivas se denomina intervalo de muestreo.
Una serie puede ser:
La frecuencia no es un detalle menor. Determina qué patrones pueden observarse.
Por ejemplo, una serie anual de ventas puede mostrar crecimiento de largo plazo, pero difícilmente permitirá estudiar el comportamiento particular de diciembre.
Pregunta para discutir
Una empresa registra únicamente sus ventas anuales. ¿Podría determinar con esos datos si diciembre es sistemáticamente mejor que enero?
En datos de corte transversal, cambiar el orden de las filas normalmente no cambia el análisis. En una serie de tiempo sí.
Si tenemos:
\[ 120,\;130,\;140,\;150, \]
observamos una evolución diferente de:
\[ 150,\;120,\;140,\;130. \]
Los mismos cuatro valores producen una historia temporal distinta.
Además, Cowpertwait y Metcalfe resaltan otra característica fundamental: observaciones cercanas en el tiempo suelen estar relacionadas entre sí. En otras palabras, lo que ocurre hoy puede estar relacionado con lo ocurrido ayer.
Esta dependencia temporal es una de las razones por las cuales el análisis de series de tiempo requiere métodos particulares.
Un principio sencillo y poderoso es:
\[ \boxed{\text{Primero observar; después modelar}} \]
Una gráfica temporal puede revelar:
Cowpertwait y Metcalfe (2009) enfatizan que el gráfico temporal permite reconocer patrones, pero también puede poner en evidencia observaciones atípicas o valores erróneos.
Trabajaremos con una empresa ficticia llamada Nova Market, que registra mensualmente la demanda y las ventas de una categoría de productos.
El caso ha sido diseñado para contener varias situaciones típicas de negocios:
La ventaja de trabajar con datos simulados es que conocemos la demanda real no observada por la empresa, lo cual permitirá explicar el censuramiento.
set.seed(410)
fechas <- seq(
from = as.Date("2021-01-01"),
to = as.Date("2025-12-01"),
by = "month"
)
n <- length(fechas)
t <- 1:n
mes_num <- month(fechas)
anio <- year(fechas)
nombres_mes <- c(
"Enero", "Febrero", "Marzo", "Abril",
"Mayo", "Junio", "Julio", "Agosto",
"Septiembre", "Octubre", "Noviembre", "Diciembre"
)
# Patrón estacional de la demanda
factor_estacional <- c(
0.82, 0.86, 0.92, 0.96,
1.00, 1.04, 1.00, 0.98,
1.03, 1.08, 1.18, 1.34
)
# Tendencia de largo plazo
tendencia <- 420 + 5.5 * t
# Promociones comerciales
promocion <- rbinom(n, size = 1, prob = 0.20)
# Demanda latente: lo que los clientes realmente quisieron comprar
demanda_latente <- round(
tendencia * factor_estacional[mes_num] +
55 * promocion +
rnorm(n, mean = 0, sd = 25)
)
demanda_latente <- pmax(demanda_latente, 1)
# En condiciones normales hay inventario suficiente
inventario <- round(
demanda_latente + runif(n, min = 35, max = 110)
)
# Se fuerzan algunos meses con quiebre de inventario,
# especialmente en periodos de demanda alta.
meses_quiebre <- c(24, 36, 47, 48, 58, 59, 60)
inventario[meses_quiebre] <- round(
demanda_latente[meses_quiebre] *
runif(length(meses_quiebre), min = 0.72, max = 0.90)
)
# Las ventas observadas no pueden superar el inventario disponible
ventas <- pmin(demanda_latente, inventario)
datos <- tibble(
fecha = fechas,
t = t,
anio = anio,
mes_num = mes_num,
mes = factor(
nombres_mes[mes_num],
levels = nombres_mes
),
promocion = factor(
promocion,
levels = c(0, 1),
labels = c("No", "Sí")
),
demanda_latente = demanda_latente,
inventario = inventario,
ventas = ventas,
quiebre_stock = ventas < demanda_latente
)
head(datos, 12)En una empresa real, normalmente no conoceríamos la demanda latente en los meses con agotamiento de inventario. Aquí la conservamos únicamente porque los datos son simulados y queremos estudiar el problema de censuramiento.
ggplot(datos, aes(x = fecha, y = ventas)) +
geom_line(linewidth = 1, color = "#1f77b4") +
geom_point(size = 2.1, color = "#d62728") +
labs(
title = "Nova Market: ventas mensuales",
subtitle = "Serie mensual 2021–2025",
x = "Fecha",
y = "Unidades vendidas"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
plot.subtitle = element_text(color = "#5d6d7e"),
axis.title = element_text(face = "bold")
)Antes de calcular cualquier modelo, observe la gráfica y responda mentalmente:
Una serie temporal puede contener varios tipos de comportamiento simultáneamente.
Para esta introducción distinguiremos:
\[ \boxed{ \text{Tendencia}+ \text{Estacionalidad}+ \text{Ciclo}+ \text{Variación irregular} } \]
No todas las series presentan todos los componentes.
La tendencia describe la dirección general o evolución de largo plazo de una serie.
Puede ser:
Por ejemplo, si una empresa aumenta gradualmente su número de clientes durante cinco años, la serie puede presentar una tendencia creciente.
Interpretación empresarial
Una tendencia creciente en ventas podría relacionarse con expansión del mercado, apertura de nuevos canales, aumento de clientes, inflación, crecimiento poblacional o una combinación de factores.
Pero una tendencia no demuestra por sí sola causalidad.
Los textos base distinguen entre tendencias determinísticas y tendencias estocásticas.
Cowpertwait y Metcalfe (2009) explican que una tendencia puede modelarse de manera determinística cuando existe una evolución relativamente regular que puede representarse mediante una función del tiempo. En cambio, ciertas series económicas y financieras pueden mostrar cambios de dirección difíciles de explicar mediante una función fija.
Cryer y Chan (2008) también separan explícitamente el estudio de tendencias determinísticas y procesos no estacionarios de naturaleza estocástica.
En este curso introductorio trabajaremos principalmente con modelos determinísticos de tendencia, por ejemplo:
\[ Y_t=\beta_0+\beta_1t+\varepsilon_t. \]
Cowpertwait y Metcalfe (2009) describen la variación estacional como un patrón que se repite dentro de un periodo fijo.
No necesariamente tiene que tratarse de estaciones climáticas.
Puede existir estacionalidad:
Ejemplos
Una diferencia importante:
Estacionalidad: se repite con una periodicidad conocida y aproximadamente fija.
Ciclo: presenta oscilaciones de mayor duración, pero sin una periodicidad necesariamente fija.
Cowpertwait y Metcalfe mencionan como ejemplo los ciclos de negocios, que no tienen por qué repetirse exactamente cada cierto número de meses.
Por tanto:
\[ \boxed{\text{repetición fija} \Rightarrow \text{estacionalidad}} \]
mientras que
\[ \boxed{\text{oscilación sin periodo fijo} \Rightarrow \text{componente cíclico}} \]
Incluso después de explicar tendencia y estacionalidad, siempre queda una parte de la serie que no sigue un patrón perfectamente predecible.
Puede deberse a:
Esta parte suele denominarse componente irregular, error o residuo, dependiendo del enfoque utilizado.
Cowpertwait y Metcalfe (2009) presentan una descomposición aditiva básica:
\[ X_t=m_t+s_t+z_t, \]
donde:
Esta representación es muy útil porque obliga a pensar en una observación como la combinación de distintas fuentes de variación.
Usamos un modelo aditivo cuando el efecto estacional tiene aproximadamente el mismo tamaño absoluto a lo largo del tiempo:
\[ X_t=T_t+S_t+E_t. \]
Ejemplo
Si cada diciembre una empresa vende aproximadamente 100 unidades adicionales, independientemente del nivel general de ventas, un esquema aditivo puede ser razonable.
Cowpertwait y Metcalfe proponen considerar un esquema multiplicativo cuando el efecto estacional aumenta junto con el nivel de la serie:
\[ X_t=T_tS_t+E_t. \]
En una interpretación empresarial sencilla, el efecto estacional se comporta más como un porcentaje que como una cantidad fija.
Ejemplo
Si diciembre suele vender alrededor de 30 % más que un mes normal, el efecto aumenta cuando crece el nivel general de ventas.
Una regla visual inicial:
Esto debe complementarse con el análisis de los residuos y el contexto del negocio.
Las series reales pueden tener mucho ruido. Para observar mejor su dirección de largo plazo se pueden usar técnicas de suavizamiento.
Una media móvil simple de orden \(k\) puede escribirse como:
\[ MA_t= \frac{ Y_t+Y_{t-1}+\cdots+Y_{t-k+1} }{k}. \]
La idea es sencilla:
reemplazar parte de la variación de corto plazo por un promedio local para visualizar mejor el comportamiento subyacente.
Cowpertwait y Metcalfe (2009) presentan las medias móviles como una forma de estimar tendencia sin imponer inicialmente una forma funcional específica.
datos <- datos %>%
mutate(
media_movil_3 = as.numeric(
stats::filter(
ventas,
filter = rep(1/3, 3),
sides = 2
)
)
)
ggplot(datos, aes(x = fecha)) +
geom_line(aes(y = ventas), linewidth = 0.8, color = "#7f8c8d") +
geom_line(
aes(y = media_movil_3),
linewidth = 1.2,
color = "#e67e22"
) +
labs(
title = "Ventas y media móvil centrada de 3 meses",
x = "Fecha",
y = "Unidades"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)La media móvil suaviza los cambios mensuales, pero también elimina información de corto plazo.
Para discutir
¿Una serie más suave es necesariamente una serie mejor?
No. Suavizar ayuda a observar patrones, pero puede ocultar cambios importantes para la operación.
En una serie mensual con estacionalidad anual, una forma clásica de estimar la tendencia es utilizar una media móvil centrada de 12 meses.
Como 12 es un número par, el centrado exacto se obtiene aplicando una media \(2\times12\), con pesos:
\[ \frac{1}{24}, \frac{1}{12}, \ldots, \frac{1}{12}, \frac{1}{24}. \]
pesos_2x12 <- c(0.5, rep(1, 11), 0.5) / 12
datos <- datos %>%
mutate(
tendencia_ma_2x12 = as.numeric(
stats::filter(
ventas,
filter = pesos_2x12,
sides = 2
)
)
)
ggplot(datos, aes(x = fecha)) +
geom_line(aes(y = ventas), linewidth = 0.7, color = "#95a5a6") +
geom_line(
aes(y = tendencia_ma_2x12),
linewidth = 1.3,
color = "#16a085",
na.rm = TRUE
) +
labs(
title = "Ventas y tendencia suavizada con media móvil 2×12",
x = "Fecha",
y = "Unidades"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)Convertimos las ventas mensuales en un objeto ts con
frecuencia 12.
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## 2021 376 348 441 417 405 456 492 446 512 495 561 610
## 2022 345 485 494 500 557 610 583 516 572 583 587 669
## 2023 473 546 511 568 603 592 622 595 617 707 705 672
## 2024 524 645 630 573 590 734 645 658 769 727 658 667
## 2025 577 616 630 674 703 779 721 761 768 655 799 822
La salida separa visualmente:
La descomposición no “descubre la verdad”. Es una forma de representar la serie bajo determinados supuestos. Los componentes obtenidos deben interpretarse en el contexto del negocio.
En el modelo aditivo:
\[ Y_t^{(ajustada)} = Y_t-\widehat{S}_t. \]
datos <- datos %>%
mutate(
componente_estacional = as.numeric(descomp_aditiva$seasonal),
ventas_ajustadas = ventas - componente_estacional
)
ggplot(datos, aes(x = fecha)) +
geom_line(aes(y = ventas), linewidth = 0.8, color = "#2980b9") +
geom_line(aes(y = ventas_ajustadas), linewidth = 1.1, color = "#c0392b") +
labs(
title = "Ventas observadas y ventas ajustadas por estacionalidad",
x = "Fecha",
y = "Unidades"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)La serie ajustada ayuda a responder preguntas como:
¿El negocio realmente creció o solamente estaba entrando en su temporada alta?
Una forma exploratoria sencilla consiste en comparar la distribución de ventas por mes.
ggplot(datos, aes(x = mes, y = ventas, fill = mes)) +
geom_boxplot(alpha = 0.85, color = "#34495e") +
scale_fill_hue() +
labs(
title = "Distribución de ventas según el mes",
x = "Mes",
y = "Unidades vendidas"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.text.x = element_text(
angle = 45,
hjust = 1
),
legend.position = "none"
)Si diciembre aparece sistemáticamente por encima de enero, tenemos evidencia visual de una diferencia estacional.
Pero cuidado:
\[ \boxed{ \text{un diciembre alto una sola vez} \neq \text{estacionalidad} } \]
Para hablar de estacionalidad necesitamos observar un patrón que se repite en varios ciclos.
También podemos usar una descomposición multiplicativa.
descomp_mult <- decompose(
ventas_ts,
type = "multiplicative"
)
indices_estacionales <- tibble(
mes = factor(nombres_mes, levels = nombres_mes),
indice = as.numeric(descomp_mult$figure)
)
kable(
indices_estacionales,
digits = 3,
col.names = c("Mes", "Índice estacional")
)| Mes | Índice estacional |
|---|---|
| Enero | 0.793 |
| Febrero | 0.946 |
| Marzo | 0.925 |
| Abril | 0.939 |
| Mayo | 0.991 |
| Junio | 1.085 |
| Julio | 1.034 |
| Agosto | 0.964 |
| Septiembre | 1.067 |
| Octubre | 1.074 |
| Noviembre | 1.072 |
| Diciembre | 1.110 |
Una interpretación aproximada es:
Por ejemplo:
\[ S_{\text{diciembre}}=1.20 \]
se interpretaría como:
diciembre tiende a ubicarse aproximadamente 20 % por encima del nivel de la serie, una vez considerado el esquema multiplicativo.
Además del suavizamiento, podemos representar la tendencia mediante una función matemática.
Trabajaremos con tres modelos básicos:
La finalidad no es “encontrar una ecuación bonita”, sino producir una representación interpretable del cambio de largo plazo.
El modelo lineal es:
\[ Y_t=\beta_0+\beta_1t+\varepsilon_t. \]
donde:
##
## Call:
## lm(formula = ventas ~ t, data = datos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -152.140 -27.101 -4.598 37.096 118.353
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 425.731 15.678 27.16 <2e-16 ***
## t 5.493 0.447 12.29 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 59.96 on 58 degrees of freedom
## Multiple R-squared: 0.7225, Adjusted R-squared: 0.7177
## F-statistic: 151 on 1 and 58 DF, p-value: < 2.2e-16
La pendiente se obtiene con:
## t
## 5.49297
Si, por ejemplo, la pendiente fuera \(5.2\), interpretaríamos:
según el modelo lineal, las ventas aumentan en promedio aproximadamente 5.2 unidades por mes.
datos <- datos %>%
mutate(
ajuste_lineal = predict(
modelo_lineal,
newdata = datos
)
)
ggplot(datos, aes(x = fecha, y = ventas)) +
geom_line(linewidth = 0.7, color = "#7f8c8d") +
geom_point(size = 1.8, color = "#7f8c8d") +
geom_line(
aes(y = ajuste_lineal),
linewidth = 1.2,
color = "#8e44ad"
) +
labs(
title = "Ajuste de una tendencia lineal",
x = "Fecha",
y = "Unidades vendidas"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)Si la velocidad de crecimiento cambia a través del tiempo, una línea recta puede resultar demasiado rígida.
Podemos considerar:
\[ Y_t= \beta_0+\beta_1t+\beta_2t^2+\varepsilon_t. \]
##
## Call:
## lm(formula = ventas ~ t + I(t^2), data = datos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -151.954 -27.724 -2.969 40.699 119.596
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 407.22659 24.01185 16.959 < 2e-16 ***
## t 7.28373 1.81630 4.010 0.000178 ***
## I(t^2) -0.02936 0.02886 -1.017 0.313356
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 59.94 on 57 degrees of freedom
## Multiple R-squared: 0.7275, Adjusted R-squared: 0.7179
## F-statistic: 76.07 on 2 and 57 DF, p-value: < 2.2e-16
datos <- datos %>%
mutate(
ajuste_cuadratico = predict(
modelo_cuadratico,
newdata = datos
)
)
ggplot(datos, aes(x = fecha, y = ventas)) +
geom_line(linewidth = 0.7, color = "#95a5a6") +
geom_point(size = 1.8, color = "#95a5a6") +
geom_line(
aes(y = ajuste_cuadratico),
linewidth = 1.2,
color = "#27ae60"
) +
labs(
title = "Ajuste de una tendencia cuadrática",
x = "Fecha",
y = "Unidades vendidas"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)Un polinomio de mayor grado puede ajustarse muy bien a los datos históricos y aun así producir extrapolaciones absurdas.
Cowpertwait y Metcalfe advierten que los polinomios de alto orden no son una buena elección para extrapolar tendencias a largo plazo.
Cuando el crecimiento parece proporcional al nivel de la serie, podemos utilizar un modelo exponencial:
\[ Y_t=ae^{bt}. \]
Aplicando logaritmo natural:
\[ \log(Y_t)=\log(a)+bt. \]
Así podemos estimarlo mediante regresión lineal.
##
## Call:
## lm(formula = log(ventas) ~ t, data = datos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.35393 -0.05190 -0.00161 0.07307 0.22564
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.0718874 0.0290009 209.37 <2e-16 ***
## t 0.0096608 0.0008269 11.68 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1109 on 58 degrees of freedom
## Multiple R-squared: 0.7018, Adjusted R-squared: 0.6967
## F-statistic: 136.5 on 1 and 58 DF, p-value: < 2.2e-16
Reconstruimos el ajuste en la escala original:
datos <- datos %>%
mutate(
ajuste_exponencial = exp(
predict(
modelo_exponencial,
newdata = datos
)
)
)
ggplot(datos, aes(x = fecha, y = ventas)) +
geom_line(linewidth = 0.7, color = "#bdc3c7") +
geom_point(size = 1.8, color = "#bdc3c7") +
geom_line(
aes(y = ajuste_exponencial),
linewidth = 1.2,
color = "#e74c3c"
) +
labs(
title = "Ajuste de una tendencia exponencial",
x = "Fecha",
y = "Unidades vendidas"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
axis.title = element_text(face = "bold")
)No conviene seleccionar un modelo solamente porque tenga el mayor \(R^2\).
Podemos comparar, entre otros aspectos:
Definimos el error:
\[ e_t=Y_t-\widehat{Y}_t. \]
Dos medidas de ajuste muy utilizadas son:
\[ MAE= \frac{1}{n} \sum_{t=1}^{n} |e_t| \]
y
\[ RMSE= \sqrt{ \frac{1}{n} \sum_{t=1}^{n} e_t^2 }. \]
metricas <- bind_rows(
tibble(
Modelo = "Lineal",
MAE = mean(abs(datos$ventas - datos$ajuste_lineal)),
RMSE = sqrt(mean((datos$ventas - datos$ajuste_lineal)^2))
),
tibble(
Modelo = "Cuadrático",
MAE = mean(abs(datos$ventas - datos$ajuste_cuadratico)),
RMSE = sqrt(mean((datos$ventas - datos$ajuste_cuadratico)^2))
),
tibble(
Modelo = "Exponencial",
MAE = mean(abs(datos$ventas - datos$ajuste_exponencial)),
RMSE = sqrt(mean((datos$ventas - datos$ajuste_exponencial)^2))
)
)
kable(
metricas,
digits = 2
)| Modelo | MAE | RMSE |
|---|---|---|
| Lineal | 46.99 | 58.95 |
| Cuadrático | 46.73 | 58.43 |
| Exponencial | 47.78 | 60.43 |
Estas métricas se calcularon sobre los mismos datos utilizados para ajustar los modelos. Por tanto, sirven para comparar ajuste histórico, no para demostrar cuál pronosticará mejor el futuro.
En una regresión convencional solemos asumir que los errores son independientes.
En series de tiempo, esa condición puede fallar porque los residuos también están ordenados temporalmente.
Cowpertwait y Metcalfe (2009) advierten que, cuando los residuos presentan autocorrelación positiva, los errores estándar obtenidos mediante una regresión ordinaria pueden ser demasiado pequeños y los valores \(p\) demasiado optimistas.
Por eso, en esta sesión utilizaremos los modelos principalmente para:
Una inferencia formal sobre la tendencia requiere revisar con mayor cuidado la estructura temporal de los residuos.
La palabra sesgo puede utilizarse en varios sentidos. En esta sesión distinguiremos dos:
No debe confundirse con la asimetría (skewness) de una distribución.
Definimos:
\[ e_t=Y_t-\widehat{Y}_t. \]
El error medio es:
\[ ME= \frac{1}{n} \sum_{t=1}^{n} e_t. \]
Su interpretación depende de cómo definamos el error. Con nuestra convención:
\[ e_t= \text{observado}-\text{pronosticado}. \]
Entonces:
Utilizaremos los primeros 48 meses para estimar una tendencia y los últimos 12 meses para evaluarla.
entrenamiento <- datos %>%
filter(t <= 48)
prueba <- datos %>%
filter(t > 48)
modelo_entrenamiento <- lm(
ventas ~ t,
data = entrenamiento
)
prueba <- prueba %>%
mutate(
pronostico = predict(
modelo_entrenamiento,
newdata = prueba
),
error = ventas - pronostico
)
kable(
prueba %>%
select(
fecha,
ventas,
pronostico,
error
),
digits = 2
)| fecha | ventas | pronostico | error |
|---|---|---|---|
| 2025-01-01 | 577 | 709.93 | -132.93 |
| 2025-02-01 | 616 | 715.87 | -99.87 |
| 2025-03-01 | 630 | 721.81 | -91.81 |
| 2025-04-01 | 674 | 727.75 | -53.75 |
| 2025-05-01 | 703 | 733.69 | -30.69 |
| 2025-06-01 | 779 | 739.63 | 39.37 |
| 2025-07-01 | 721 | 745.57 | -24.57 |
| 2025-08-01 | 761 | 751.51 | 9.49 |
| 2025-09-01 | 768 | 757.45 | 10.55 |
| 2025-10-01 | 655 | 763.39 | -108.39 |
| 2025-11-01 | 799 | 769.33 | 29.67 |
| 2025-12-01 | 822 | 775.27 | 46.73 |
ME <- mean(prueba$error)
MAE <- mean(abs(prueba$error))
RMSE <- sqrt(mean(prueba$error^2))
tibble(
ME = ME,
MAE = MAE,
RMSE = RMSE
) %>%
kable(digits = 2)| ME | MAE | RMSE |
|---|---|---|
| -33.85 | 56.48 | 68.96 |
La magnitud y el signo del ME deben interpretarse junto
con la gráfica.
ggplot(prueba, aes(x = fecha)) +
geom_line(aes(y = ventas), linewidth = 0.9, color = "#2980b9") +
geom_point(aes(y = ventas), size = 2.2, color = "#2980b9") +
geom_line(
aes(y = pronostico),
linewidth = 1.1,
linetype = 2,
color = "#e67e22"
) +
labs(
title = "Evaluación fuera de muestra",
subtitle = "Ventas observadas frente a tendencia pronosticada",
x = "Fecha",
y = "Unidades"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
plot.subtitle = element_text(color = "#5d6d7e"),
axis.title = element_text(face = "bold")
)Un modelo puede estar perfectamente calculado y aun así producir conclusiones incorrectas si los datos observados no representan adecuadamente el fenómeno.
Ejemplos empresariales:
Idea clave
\[ \boxed{ \text{un buen modelo no corrige automáticamente un mal proceso de medición} } \]
Los dos textos base utilizados en esta sesión desarrollan ampliamente tendencias, estacionalidad, descomposición, suavizamiento y modelación temporal. El censuramiento no constituye uno de sus temas introductorios centrales; se incorpora aquí porque forma parte del microcurrículo y es especialmente relevante en problemas de negocios.
El censuramiento aparece cuando no conocemos el valor exacto de una observación, pero sí conocemos información parcial sobre ella.
Por ejemplo, si una tienda tenía 500 unidades disponibles y vendió las 500, no sabemos si la demanda era:
\[ 500,\;550,\;700,\;1000,\ldots \]
Solo sabemos que:
\[ D_t\geq500. \]
La demanda está censurada por la disponibilidad de inventario.
En un escenario sencillo:
\[ V_t=\min(D_t,I_t), \]
donde:
Si:
\[ D_t<I_t, \]
entonces:
\[ V_t=D_t. \]
Pero si:
\[ D_t\ge I_t, \]
entonces:
\[ V_t=I_t \]
y únicamente sabemos que la demanda fue al menos igual al inventario vendido.
Por eso:
\[ \boxed{ \text{ventas observadas} \neq \text{demanda real} } \]
cuando existen quiebres de inventario.
Identifiquemos los meses en los cuales las ventas quedaron limitadas por el inventario.
datos %>%
filter(quiebre_stock) %>%
select(
fecha,
demanda_latente,
inventario,
ventas
) %>%
kable()| fecha | demanda_latente | inventario | ventas |
|---|---|---|---|
| 2022-12-01 | 797 | 669 | 669 |
| 2023-12-01 | 828 | 672 | 672 |
| 2024-11-01 | 804 | 658 | 658 |
| 2024-12-01 | 900 | 667 | 667 |
| 2025-10-01 | 837 | 655 | 655 |
| 2025-11-01 | 903 | 799 | 799 |
| 2025-12-01 | 1024 | 822 | 822 |
En la práctica no observaríamos demanda_latente; la
incluimos porque la simulación nos permite conocerla.
ggplot(datos, aes(x = fecha)) +
geom_line(
aes(y = demanda_latente),
linewidth = 1,
linetype = 2,
color = "#8e44ad"
) +
geom_line(
aes(y = ventas),
linewidth = 0.95,
color = "#27ae60"
) +
geom_point(
data = datos %>% filter(quiebre_stock),
aes(y = ventas),
size = 3,
color = "#e74c3c"
) +
labs(
title = "Demanda latente y ventas observadas",
subtitle = "Los puntos corresponden a meses con quiebre de inventario",
x = "Fecha",
y = "Unidades"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1f3c88"),
plot.subtitle = element_text(color = "#5d6d7e"),
axis.title = element_text(face = "bold")
)Supongamos que la empresa ajusta una tendencia únicamente con las ventas observadas.
Comparemos la tendencia estimada para:
modelo_ventas <- lm(
ventas ~ t,
data = datos
)
modelo_demanda <- lm(
demanda_latente ~ t,
data = datos
)
comparacion_pendientes <- tibble(
Serie = c(
"Ventas observadas",
"Demanda latente"
),
Pendiente = c(
coef(modelo_ventas)["t"],
coef(modelo_demanda)["t"]
)
)
kable(
comparacion_pendientes,
digits = 3
)| Serie | Pendiente |
|---|---|
| Ventas observadas | 5.493 |
| Demanda latente | 6.629 |
Si la pendiente de las ventas es menor que la pendiente de la demanda, el negocio podría concluir que está creciendo más lentamente de lo que realmente crece su mercado.
Este es un problema serio para decisiones de:
No son exactamente lo mismo.
| Situación | ¿Qué sabemos? | Ejemplo |
|---|---|---|
| Dato faltante | No observamos el valor | El sistema no registró las ventas del día |
| Censuramiento | Conocemos un límite o información parcial | Se vendió todo el inventario: demanda \(\geq\) stock |
| Truncamiento | Ciertos casos ni siquiera aparecen en la base | Solo se almacenan pedidos superiores a cierto monto |
Esta distinción afecta la forma en que debe analizarse la información.
Imagine que los agotamientos de inventario ocurren principalmente en diciembre.
Entonces justamente los meses de mayor demanda quedan limitados por el stock disponible.
Como resultado, al analizar ventas, podríamos:
Se produce un círculo vicioso:
\[ \text{inventario bajo} \rightarrow \text{ventas censuradas} \rightarrow \text{demanda subestimada} \rightarrow \text{inventario bajo}. \]
Ese es un excelente ejemplo de por qué el análisis estadístico necesita comprender cómo se generan los datos.
No existe una regla universal basada en un único número.
Una decisión razonable debe combinar:
\[ \boxed{ \text{gráfico} + \text{error} + \text{residuos} + \text{sentido del negocio} } \]
Preguntas útiles:
Suponga que una tendencia cuadrática presenta un \(R^2\) muy alto.
Eso no significa que:
El tiempo funciona aquí como una variable que resume una evolución, no necesariamente como una causa.
Cryer y Chan (2008) muestran que una serie puede cambiar debido a una intervención que modifica su nivel o su tendencia.
En negocios podrían ser:
Una tendencia estimada sobre todo el periodo puede ocultar esos cambios.
Si una empresa duplicó su número de tiendas en julio de 2024, ¿tendría sentido interpretar toda la serie como si nada hubiera cambiado?
Ante una nueva serie, una secuencia inicial razonable es:
Pregunte:
Revise:
Nunca empiece el análisis temporal únicamente con una tabla.
Busque:
Use medias móviles para facilitar la observación de la tendencia.
Pregunte si el comportamiento parece aditivo o multiplicativo.
Compare:
No seleccione modelos únicamente por \(R^2\).
Pregunte qué parte del fenómeno realmente observa la base de datos.
El análisis debe terminar en una conclusión de negocio, no en una salida de R.
Una mala conclusión sería:
“El modelo cuadrático tiene un RMSE de 49.6.”
Una conclusión útil sería:
“Las ventas muestran una trayectoria creciente y un patrón estacional marcado al final del año. Sin embargo, varios meses de alta demanda presentan agotamiento de inventario, por lo que las ventas observadas probablemente subestiman la demanda real. En consecuencia, la tendencia y los índices estacionales calculados con ventas deben interpretarse con cautela antes de utilizarse para decisiones de abastecimiento.”
La diferencia está en pasar de:
\[ \text{resultado estadístico} \]
a:
\[ \text{interpretación} \rightarrow \text{decisión}. \]
Con base en los resultados obtenidos para Nova Market, responda:
Al terminar la sesión, el estudiante debería poder explicar con sus propias palabras:
Serie de tiempo: observaciones de una variable ordenadas temporalmente.
Frecuencia: periodicidad con la cual se registran las observaciones.
Tendencia: evolución general de largo plazo.
Estacionalidad: patrón que se repite en intervalos regulares.
Ciclo: movimiento recurrente sin periodicidad fija.
Componente irregular: variación no explicada por los patrones sistemáticos considerados.
Suavizamiento: reducción de fluctuaciones de corto plazo para facilitar la observación de patrones.
Descomposición: separación conceptual de la serie en componentes.
Ajuste estacional: eliminación o control del efecto estacional para estudiar otros movimientos.
Modelo de tendencia: función utilizada para representar la evolución sistemática de una serie.
Sesgo: desviación sistemática producida por el modelo o por el proceso de observación.
Censuramiento: situación en la que no observamos el valor exacto, pero conocemos un límite o información parcial.
Una serie de tiempo no es simplemente una columna de números con fechas.
Contiene información sobre:
\[ \text{historia} + \text{patrones} + \text{cambios} + \text{incertidumbre}. \]
En negocios, una lectura adecuada de esos elementos permite mejorar decisiones de:
Pero hay una advertencia fundamental:
\[ \boxed{ \text{el modelo solo puede aprender de lo que los datos permiten observar} } \]
Si la demanda está censurada, si el sistema cambió, si existen periodos faltantes o si el proceso de medición está sesgado, el análisis debe reconocerlo explícitamente.
Cowpertwait, P. S. P., & Metcalfe, A. V. (2009). Introductory Time Series with R. Springer. DOI: 10.1007/978-0-387-88698-5.
Cryer, J. D., & Chan, K.-S. (2008). Time Series Analysis: With Applications in R (2nd ed.). Springer.
La estructura conceptual de esta guía sobre:
se apoya principalmente en Cowpertwait y Metcalfe (2009) y se complementa con el tratamiento de tendencias, pronóstico e intervenciones de Cryer y Chan (2008).
Los apartados sobre sesgo de los datos y censuramiento de demanda por inventario son una extensión didáctica para responder a los contenidos del curso y llevar los conceptos al contexto empresarial.