1 Introducción

En este documento se desarrollan los cuatro ejercicios solicitados en la Actividad 2: (1) pronóstico de ventas semanales con tres modelos vistos en clase, (2) el mismo análisis aplicado a la base de datos de ventas históricas de leche saborizada Hershey (Lechitas), (3) el caso Vintage Restaurant, y (4) el caso Carlson Department Store.

Para los ejercicios 1 y 2 se utilizan los tres modelos de pronóstico trabajados en clase:

  1. Modelo Naive (ingenuo): el pronóstico del periodo t es el valor observado en t-1.
  2. Promedio Móvil Simple (k = 3): el pronóstico de t es el promedio simple de los 3 periodos anteriores.
  3. Promedio Móvil Ponderado (k = 3): el pronóstico de t es un promedio ponderado de los 3 periodos anteriores, dando mayor peso a las observaciones más recientes. Se usan los pesos 0.5 (t-1), 0.3 (t-2) y 0.2 (t-3), que son los pesos convencionales para este modelo; si en tus notas de clase se usó otra combinación de pesos, solo hay que cambiar el vector pesos en el código de este documento y todo se recalcula automáticamente.

2 Ejercicio 1. Ventas Semanales

2.1 Datos

semana <- 1:12
ventas <- c(17, 21, 19, 23, 18, 16, 20, 11, 22, 20, 15, 22)
ej1 <- data.frame(Semana = semana, Ventas = ventas)
tabla(ej1, digits = 0, caption = "Ventas semanales (Ejercicio 1)")
Ventas semanales (Ejercicio 1)
Semana Ventas
1 17
2 21
3 19
4 23
5 18
6 16
7 20
8 11
9 22
10 20
11 15
12 22
ggplot(ej1, aes(Semana, Ventas)) +
  geom_line(color = "#2C7C91") +
  geom_point(color = "#2C7C91", size = 2) +
  scale_x_continuous(breaks = semana) +
  labs(title = "Serie de tiempo: Ventas Semanales", x = "Semana", y = "Ventas") +
  theme_minimal()

2.2 Modelo 1: Naive

naive_pron <- c(NA, head(ventas, -1))
ej1_naive <- data.frame(Semana = semana, Ventas = ventas, Pronostico = naive_pron)
tabla(ej1_naive, caption = "Modelo Naive - Ejercicio 1")
Modelo Naive - Ejercicio 1
Semana Ventas Pronostico
1 17 NA
2 21 17
3 19 21
4 23 19
5 18 23
6 16 18
7 20 16
8 11 20
9 22 11
10 20 22
11 15 20
12 22 15
met_naive_1 <- metricas(ventas, naive_pron)
tabla(met_naive_1, caption = "Métricas de exactitud - Naive (Ejercicio 1)")
Métricas de exactitud - Naive (Ejercicio 1)
ME MAE MSE RMSE MAPE(%)
0.45 5 32.82 5.73 28.56

2.3 Modelo 2: Promedio Móvil Simple (k = 3)

k <- 3
sma_pron <- rep(NA_real_, length(ventas))
for (t in (k + 1):length(ventas)) {
  sma_pron[t] <- mean(ventas[(t - k):(t - 1)])
}
ej1_sma <- data.frame(Semana = semana, Ventas = ventas, Pronostico = round(sma_pron, 2))
tabla(ej1_sma, caption = "Modelo Promedio Móvil Simple k=3 - Ejercicio 1")
Modelo Promedio Móvil Simple k=3 - Ejercicio 1
Semana Ventas Pronostico
1 17 NA
2 21 NA
3 19 NA
4 23 19.00
5 18 21.00
6 16 20.00
7 20 19.00
8 11 18.00
9 22 15.67
10 20 17.67
11 15 17.67
12 22 19.00
met_sma_1 <- metricas(ventas, sma_pron)
tabla(met_sma_1, caption = "Métricas de exactitud - Promedio Móvil Simple (Ejercicio 1)")
Métricas de exactitud - Promedio Móvil Simple (Ejercicio 1)
ME MAE MSE RMSE MAPE(%)
0 3.7 16.96 4.12 22.17

2.4 Modelo 3: Promedio Móvil Ponderado (k = 3)

pesos <- c(0.2, 0.3, 0.5)  # peso para t-3, t-2, t-1 (el más reciente pesa más)
wma_pron <- rep(NA_real_, length(ventas))
for (t in (k + 1):length(ventas)) {
  wma_pron[t] <- sum(ventas[(t - k):(t - 1)] * pesos)
}
ej1_wma <- data.frame(Semana = semana, Ventas = ventas, Pronostico = round(wma_pron, 2))
tabla(ej1_wma, caption = "Modelo Promedio Móvil Ponderado k=3 (pesos 0.5/0.3/0.2) - Ejercicio 1")
Modelo Promedio Móvil Ponderado k=3 (pesos 0.5/0.3/0.2) - Ejercicio 1
Semana Ventas Pronostico
1 17 NA
2 21 NA
3 19 NA
4 23 19.2
5 18 21.4
6 16 19.7
7 20 18.0
8 11 18.4
9 22 14.7
10 20 18.3
11 15 18.8
12 22 17.9
met_wma_1 <- metricas(ventas, wma_pron)
tabla(met_wma_1, caption = "Métricas de exactitud - Promedio Móvil Ponderado (Ejercicio 1)")
Métricas de exactitud - Promedio Móvil Ponderado (Ejercicio 1)
ME MAE MSE RMSE MAPE(%)
0.07 4.13 20.65 4.54 24.61

2.5 Tabla resumen de resultados (Ejercicio 1)

resumen_1 <- rbind(
  cbind(Modelo = "Naive", met_naive_1),
  cbind(Modelo = "Promedio Móvil Simple (k=3)", met_sma_1),
  cbind(Modelo = "Promedio Móvil Ponderado (k=3)", met_wma_1)
)
tabla(resumen_1, caption = "Comparación de modelos - Ejercicio 1 (Ventas Semanales)")
Comparación de modelos - Ejercicio 1 (Ventas Semanales)
Modelo ME MAE MSE RMSE MAPE(%)
Naive 0.45 5.00 32.82 5.73 28.56
Promedio Móvil Simple (k=3) 0.00 3.70 16.96 4.12 22.17
Promedio Móvil Ponderado (k=3) 0.07 4.13 20.65 4.54 24.61

2.6 Conclusión Ejercicio 1

De acuerdo con el MAPE (que permite comparar el error en términos porcentuales, independientemente de la escala de la serie), el modelo con menor error es Promedio Móvil Simple (k=3).

En una serie tan corta y con variaciones semana a semana relativamente grandes (de 11 a 23 unidades), el modelo Naive tiende a sobrerreaccionar a los cambios recientes, mientras que los promedios móviles (simple y ponderado) suavizan el ruido de corto plazo. El promedio móvil ponderado, al dar más peso a la observación más reciente, reacciona un poco más rápido que el promedio móvil simple ante cambios de nivel, sin perder toda la capacidad de suavizamiento. Dado que la serie no muestra una tendencia ni una estacionalidad clara (parece fluctuar alrededor de un nivel relativamente estable, entre 15 y 23), un modelo de promedios (simple o ponderado) es más adecuado que el Naive puro para pronosticar el corto plazo, y es preferible sobre modelos de tendencia o estacionalidad que no aplican a una serie de solo 12 observaciones sin patrón evidente.


3 Ejercicio 2. Hershey’s (Ventas Históricas Lechitas)

Se aplican las mismas instrucciones y los mismos tres modelos del Ejercicio 1 a la base de datos Ventas_Históricas_Lechitas.xlsx (ventas mensuales de leche saborizada Hershey México, en miles de dólares, de enero 2017 a diciembre 2019).

3.1 Datos

fechas <- seq(as.Date("2017-01-01"), as.Date("2019-12-01"), by = "month")

y2017 <- c(25520.51, 23740.11, 26253.58, 25868.43, 27072.87, 27150.5, 27067.1, 28145.25, 27546.29, 28400.37, 27441.98, 27852.47)
y2018 <- c(28463.69, 26996.11, 29768.2, 29292.51, 29950.68, 30099.17, 30851.26, 32271.76, 31940.74, 32995.93, 32197.12, 31984.82)
y2019 <- c(32496.44, 31287.28, 33376.02, 32949.77, 34004.11, 33757.89, 32927.3, 34324.12, 35151.28, 36133.07, 34799.91, 34846.17)

ventas2 <- c(y2017, y2018, y2019)
periodo <- 1:length(ventas2)
ej2 <- data.frame(Periodo = periodo, Fecha = fechas, Ventas = round(ventas2, 2))
tabla(head(ej2, 12), caption = "Ventas mensuales Lechitas (primeros 12 meses, ver serie completa en la gráfica)")
Ventas mensuales Lechitas (primeros 12 meses, ver serie completa en la gráfica)
Periodo Fecha Ventas
1 2017-01-01 25520.51
2 2017-02-01 23740.11
3 2017-03-01 26253.58
4 2017-04-01 25868.43
5 2017-05-01 27072.87
6 2017-06-01 27150.50
7 2017-07-01 27067.10
8 2017-08-01 28145.25
9 2017-09-01 27546.29
10 2017-10-01 28400.37
11 2017-11-01 27441.98
12 2017-12-01 27852.47
ggplot(ej2, aes(Fecha, Ventas)) +
  geom_line(color = "#B5443C") +
  geom_point(color = "#B5443C", size = 1.5) +
  labs(title = "Serie de tiempo: Ventas mensuales Lechitas (2017-2019)", x = "Mes", y = "Ventas (miles de $)") +
  theme_minimal()

La serie muestra una tendencia creciente clara a lo largo de los tres años, junto con una ligera estacionalidad anual (caídas relativas en febrero y repuntes hacia fin de año), características muy distintas a la serie del Ejercicio 1.

3.2 Modelo 1: Naive

naive_pron2 <- c(NA, head(ventas2, -1))
met_naive_2 <- metricas(ventas2, naive_pron2)
tabla(met_naive_2, caption = "Métricas de exactitud - Naive (Ejercicio 2)")
Métricas de exactitud - Naive (Ejercicio 2)
ME MAE MSE RMSE MAPE(%)
266.45 902.85 1237975 1112.64 3.01

3.3 Modelo 2: Promedio Móvil Simple (k = 3)

sma_pron2 <- rep(NA_real_, length(ventas2))
for (t in (k + 1):length(ventas2)) {
  sma_pron2[t] <- mean(ventas2[(t - k):(t - 1)])
}
met_sma_2 <- metricas(ventas2, sma_pron2)
tabla(met_sma_2, caption = "Métricas de exactitud - Promedio Móvil Simple (Ejercicio 2)")
Métricas de exactitud - Promedio Móvil Simple (Ejercicio 2)
ME MAE MSE RMSE MAPE(%)
591.01 870.47 1063496 1031.26 2.81

3.4 Modelo 3: Promedio Móvil Ponderado (k = 3)

wma_pron2 <- rep(NA_real_, length(ventas2))
for (t in (k + 1):length(ventas2)) {
  wma_pron2[t] <- sum(ventas2[(t - k):(t - 1)] * pesos)
}
met_wma_2 <- metricas(ventas2, wma_pron2)
tabla(met_wma_2, caption = "Métricas de exactitud - Promedio Móvil Ponderado (Ejercicio 2)")
Métricas de exactitud - Promedio Móvil Ponderado (Ejercicio 2)
ME MAE MSE RMSE MAPE(%)
492.27 818.64 940425.7 969.76 2.64

3.5 Comparación visual de pronósticos (últimos 12 meses)

comp2 <- data.frame(
  Fecha = fechas,
  Real = ventas2,
  Naive = naive_pron2,
  SMA = sma_pron2,
  WMA = wma_pron2
) %>% pivot_longer(cols = c(Real, Naive, SMA, WMA), names_to = "Serie", values_to = "Valor")

ggplot(filter(comp2, Fecha >= as.Date("2019-01-01")), aes(Fecha, Valor, color = Serie)) +
  geom_line(linewidth = 0.9) +
  labs(title = "Real vs. pronósticos - último año (2019)", x = "Mes", y = "Ventas (miles de $)") +
  theme_minimal()

3.6 Tabla resumen de resultados (Ejercicio 2)

resumen_2 <- rbind(
  cbind(Modelo = "Naive", met_naive_2),
  cbind(Modelo = "Promedio Móvil Simple (k=3)", met_sma_2),
  cbind(Modelo = "Promedio Móvil Ponderado (k=3)", met_wma_2)
)
tabla(resumen_2, caption = "Comparación de modelos - Ejercicio 2 (Lechitas)")
Comparación de modelos - Ejercicio 2 (Lechitas)
Modelo ME MAE MSE RMSE MAPE(%)
Naive 266.45 902.85 1237975.5 1112.64 3.01
Promedio Móvil Simple (k=3) 591.01 870.47 1063496.1 1031.26 2.81
Promedio Móvil Ponderado (k=3) 492.27 818.64 940425.7 969.76 2.64

3.7 Conclusión Ejercicio 2

El modelo con menor MAPE es Promedio Móvil Ponderado (k=3).

Como la serie de Lechitas tiene una tendencia sostenida al alza, cualquiera de los tres modelos (Naive, promedio móvil simple y ponderado) queda sistemáticamente por debajo del valor real, ya que ninguno incorpora explícitamente una componente de tendencia: todos “persiguen” a la serie desde atrás. El modelo Naive es el que reacciona más rápido a ese crecimiento (usa únicamente el dato inmediato anterior), por lo que en series con tendencia clara suele tener menor error que los promedios móviles, que al promediar 3 meses se retrasan más frente al crecimiento sostenido. Para esta serie, un modelo que incorpore tendencia (p. ej. suavización exponencial con tendencia u regresión lineal) sería más apropiado que cualquiera de los tres modelos aquí comparados, pero de los tres, el indicado por el MAPE es la mejor opción disponible.


4 Ejercicio 3. Vintage Restaurant

4.1 Planteamiento

El Vintage Restaurant, en la isla Captiva (Florida), cuenta con ventas mensuales de alimentos y bebidas (en miles de dólares) para sus primeros tres años de operación. Karen Payne, la propietaria, requiere pronosticar las ventas mensuales del cuarto año.

4.2 Datos

meses <- factor(month.name, levels = month.name)
Year1 <- c(242, 235, 232, 178, 184, 140, 145, 152, 110, 130, 152, 206)
Year2 <- c(263, 238, 247, 193, 193, 149, 157, 161, 122, 130, 167, 230)
Year3 <- c(282, 255, 265, 205, 210, 160, 166, 174, 126, 148, 173, 235)

ej3 <- data.frame(Mes = meses, `Año 1` = Year1, `Año 2` = Year2, `Año 3` = Year3, check.names = FALSE)
tabla(ej3, digits = 0, caption = "Ventas mensuales Vintage Restaurant (miles de $)")
Ventas mensuales Vintage Restaurant (miles de $)
Mes Año 1 Año 2 Año 3
January 242 263 282
February 235 238 255
March 232 247 265
April 178 193 205
May 184 193 210
June 140 149 160
July 145 157 166
August 152 161 174
September 110 122 126
October 130 130 148
November 152 167 173
December 206 230 235
ventas3 <- c(Year1, Year2, Year3)
t3 <- 1:36
fecha3 <- seq(as.Date("2021-01-01"), by = "month", length.out = 36)
serie3 <- ts(ventas3, start = c(2021, 1), frequency = 12)

4.3 1. Gráfica de la serie de tiempo

autoplot(serie3) +
  labs(title = "Vintage Restaurant: ventas mensuales (Año 1 a Año 3)",
       x = "Año", y = "Ventas (miles de $)") +
  theme_minimal()

Comentario. La serie muestra dos patrones principales: (1) una tendencia creciente de un año a otro (las ventas totales pasan de 2,106 a 2,250 y a 2,399 miles de dólares), y (2) una estacionalidad anual muy marcada: las ventas son altas en los meses de enero a marzo y en diciembre (temporada alta turística/vacacional), y caen fuertemente entre junio y octubre, con un mínimo en septiembre.

4.4 2. Índices estacionales

Se calculan mediante el método de descomposición (razón al promedio móvil centrado de 12 periodos).

desc3 <- decompose(serie3, type = "multiplicative")
indices <- desc3$figure
names(indices) <- month.name
tabla(data.frame(Mes = month.name, `Índice Estacional` = round(indices, 3), check.names = FALSE),
      caption = "Índices estacionales mensuales - Vintage Restaurant")
Índices estacionales mensuales - Vintage Restaurant
Mes Índice Estacional
January January 1.44
February February 1.30
March March 1.34
April April 1.04
May May 1.05
June June 0.80
July July 0.83
August August 0.85
September September 0.63
October October 0.70
November November 0.85
December December 1.16
ggplot(data.frame(Mes = factor(month.name, levels = month.name), Indice = indices),
       aes(Mes, Indice)) +
  geom_col(fill = "#2C7C91") +
  geom_hline(yintercept = 1, linetype = "dashed", color = "grey40") +
  labs(title = "Índices estacionales mensuales", y = "Índice estacional") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Comentario. Los meses con índice estacional mayor a 1 (temporada alta) son enero, febrero, marzo y diciembre; el mes de septiembre presenta el índice más bajo (temporada baja), seguido de octubre. Esto coincide con la intuición del negocio: Captiva Island es un destino vacacional de invierno, por lo que las ventas del restaurante son más altas en los meses de temporada de nieve en el norte de EE. UU. (turistas que escapan del frío) y en diciembre por las fiestas decembrinas, mientras que caen en los meses de verano/inicio de otoño, que además coincide con la temporada de huracanes en Florida (menor afluencia turística). Los índices estacionales sí tienen sentido intuitivo para este tipo de negocio.

4.5 3. Serie desestacionalizada

desestacional <- serie3 / rep(indices, 3)
df_deses <- data.frame(Fecha = fecha3, Real = as.numeric(serie3), Desestacionalizada = as.numeric(desestacional))

ggplot(df_deses, aes(Fecha)) +
  geom_line(aes(y = Real, color = "Serie original")) +
  geom_line(aes(y = Desestacionalizada, color = "Desestacionalizada"), linewidth = 1) +
  labs(title = "Serie original vs. desestacionalizada", y = "Ventas (miles de $)", color = "") +
  theme_minimal()

modelo_tendencia <- lm(Desestacionalizada ~ Fecha, data = df_deses %>% mutate(Fecha = t3))
tabla(broom_coef <- as.data.frame(summary(modelo_tendencia)$coefficients), digits = 4,
      caption = "Regresión lineal de tendencia sobre la serie desestacionalizada")
Regresión lineal de tendencia sobre la serie desestacionalizada
Estimate Std. Error t value Pr(>|t|)
(Intercept) 169.3494 1.0926 154.9951 0
Fecha 1.0213 0.0515 19.8323 0

Comentario. Una vez removido el efecto estacional, la serie desestacionalizada muestra una tendencia lineal creciente y sostenida a lo largo de los 36 meses: la pendiente estimada de la regresión (1.021 miles de $ por mes) es positiva y estadísticamente distinta de cero, confirmando que el crecimiento del restaurante no es solo estacional sino un crecimiento real del negocio en el tiempo.

4.6 4. Pronóstico por descomposición (enero-diciembre del Año 4)

b0 <- coef(modelo_tendencia)[1]
b1 <- coef(modelo_tendencia)[2]
t_year4 <- 37:48
tendencia_year4 <- b0 + b1 * t_year4
pron_decomp <- tendencia_year4 * indices

tabla(data.frame(Mes = month.name, `Tendencia` = round(tendencia_year4, 1),
                  `Índice Estacional` = round(indices, 3),
                  `Pronóstico (Descomposición)` = round(pron_decomp, 1), check.names = FALSE),
      caption = "Pronóstico Año 4 - Método de descomposición")
Pronóstico Año 4 - Método de descomposición
Mes Tendencia Índice Estacional Pronóstico (Descomposición)
January January 207.1 1.44 299.0
February February 208.2 1.30 270.5
March March 209.2 1.34 281.2
April April 210.2 1.04 218.9
May May 211.2 1.05 221.7
June June 212.2 0.80 169.9
July July 213.3 0.83 176.6
August August 214.3 0.85 182.8
September September 215.3 0.63 135.2
October October 216.3 0.70 151.5
November November 217.4 0.85 185.3
December December 218.4 1.16 253.2

4.7 5. Pronóstico por regresión con variables ficticias (dummy) estacionales

df3 <- data.frame(t = t3, Ventas = ventas3, Mes = factor(rep(month.name, 3), levels = month.name))
modelo_dummy <- lm(Ventas ~ t + Mes, data = df3)
tabla(as.data.frame(summary(modelo_dummy)$coefficients), digits = 3,
      caption = "Regresión con variables dummy estacionales (mes base: enero)")
Regresión con variables dummy estacionales (mes base: enero)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 249.108 2.746 90.727 0
t 1.017 0.076 13.467 0
MesFebruary -20.684 3.627 -5.703 0
MesMarch -16.368 3.629 -4.510 0
MesApril -73.385 3.633 -20.199 0
MesMay -70.736 3.639 -19.440 0
MesJune -117.753 3.646 -32.299 0
MesJuly -112.438 3.654 -30.768 0
MesAugust -107.122 3.664 -29.233 0
MesSeptember -151.139 3.676 -41.114 0
MesOctober -135.490 3.689 -36.725 0
MesNovember -108.507 3.704 -29.295 0
MesDecember -49.858 3.720 -13.402 0
nuevo <- data.frame(t = 37:48, Mes = factor(month.name, levels = month.name))
pron_dummy <- predict(modelo_dummy, newdata = nuevo)

tabla(data.frame(Mes = month.name, `Pronóstico (Regresión Dummy)` = round(pron_dummy, 1), check.names = FALSE),
      caption = "Pronóstico Año 4 - Regresión con variables ficticias")
Pronóstico Año 4 - Regresión con variables ficticias
Mes Pronóstico (Regresión Dummy)
January 286.8
February 267.1
March 272.4
April 216.4
May 220.1
June 174.1
July 180.4
August 186.8
September 143.8
October 160.4
November 188.4
December 248.1

4.8 6. Tabla resumen y gráfica comparativa

resumen_3 <- data.frame(
  Mes = month.name,
  `Descomposición` = round(pron_decomp, 1),
  `Regresión Dummy` = round(as.numeric(pron_dummy), 1),
  check.names = FALSE
)
tabla(resumen_3, caption = "Pronósticos Año 4 - Vintage Restaurant (ambos métodos)")
Pronósticos Año 4 - Vintage Restaurant (ambos métodos)
Mes Descomposición Regresión Dummy
January January 299.0 286.8
February February 270.5 267.1
March March 281.2 272.4
April April 218.9 216.4
May May 221.7 220.1
June June 169.9 174.1
July July 176.6 180.4
August August 182.8 186.8
September September 135.2 143.8
October October 151.5 160.4
November November 185.3 188.4
December December 253.2 248.1
df_comp3 <- data.frame(Mes = factor(month.name, levels = month.name),
                        Descomposicion = pron_decomp,
                        RegresionDummy = as.numeric(pron_dummy)) %>%
  pivot_longer(-Mes, names_to = "Metodo", values_to = "Pronostico")

ggplot(df_comp3, aes(Mes, Pronostico, color = Metodo, group = Metodo)) +
  geom_line(linewidth = 1) + geom_point() +
  labs(title = "Pronóstico Año 4 - Vintage Restaurant", y = "Ventas pronosticadas (miles de $)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

4.9 Error de pronóstico de enero (ventas reales = $295,000)

real_enero <- 295
pron_decomp_enero <- pron_decomp[1]
pron_dummy_enero <- as.numeric(pron_dummy[1])

error_decomp <- real_enero - pron_decomp_enero
error_dummy  <- real_enero - pron_dummy_enero

tabla(data.frame(
  `Método` = c("Descomposición", "Regresión Dummy"),
  `Pronóstico Enero Año 4` = round(c(pron_decomp_enero, pron_dummy_enero), 1),
  `Ventas Reales` = real_enero,
  `Error (Real - Pronóstico)` = round(c(error_decomp, error_dummy), 1),
  `Error %` = round(c(error_decomp, error_dummy) / real_enero * 100, 2),
  check.names = FALSE
), caption = "Error de pronóstico - enero Año 4")
Error de pronóstico - enero Año 4
Método Pronóstico Enero Año 4 Ventas Reales Error (Real - Pronóstico) Error %
January Descomposición 299.0 295 -4.0 -1.36
Regresión Dummy 286.8 295 8.2 2.80

4.10 Conclusiones Ejercicio 3

La serie de ventas de Vintage Restaurant combina una tendencia de crecimiento clara con una estacionalidad muy marcada (temporada alta en invierno/diciembre, temporada baja en septiembre). Ambos métodos de pronóstico (descomposición clásica y regresión con variables dummy) capturan razonablemente bien este patrón y arrojan pronósticos similares para el Año 4. El error de pronóstico de enero, comparado contra la venta real de $295,000, resulta relativamente moderado (unos cuantos puntos porcentuales), lo cual es esperable dado que es un solo mes y ambos modelos están sujetos a la variabilidad normal del negocio (clima, eventos puntuales, etc.).

Para reducir la incertidumbre del procedimiento de pronóstico y que Karen no se confunda con la diferencia entre el pronóstico y la venta real, se recomienda: (1) comunicar los pronósticos siempre acompañados de un rango o intervalo de confianza (no como una cifra única y exacta), (2) actualizar el modelo cada mes conforme se tenga la venta real (pronóstico “rodante”), de forma que el error de un mes no se arrastre a los siguientes, y (3) monitorear el error de pronóstico mes a mes (por ejemplo con el MAPE acumulado) para detectar a tiempo si el patrón estacional o la tendencia están cambiando y el modelo necesita reajustarse.


5 Ejercicio 4. Carlson Department Store

5.1 Planteamiento

Carlson Department Store sufrió daños por un huracán y permaneció cerrada de septiembre a diciembre. Se busca estimar: (1) las ventas que Carlson habría tenido de no haber ocurrido el huracán, (2) las ventas que el condado habría tenido de no haber ocurrido el huracán, y (3) la pérdida de ventas de Carlson en ese periodo, además de argumentar sobre el posible exceso de ventas relacionado con el huracán.

5.2 Datos

Ventas mensuales de Carlson y ventas totales de tiendas departamentales del condado, en millones de dólares, para los 48 meses previos al huracán (septiembre Año 0 - agosto Año 4) y los 4 meses de cierre (septiembre - diciembre Año 4).

meses_fiscal <- c("Sep","Oct","Nov","Dic","Ene","Feb","Mar","Abr","May","Jun","Jul","Ago")

carlson_1 <- c(1.71, 1.90, 2.74, 4.20, 1.45, 1.80, 2.03, 1.99, 2.32, 2.20, 2.13, 2.43)  # Año1: Sep-Ago
carlson_2 <- c(1.90, 2.13, 2.56, 4.16, 2.31, 1.89, 2.02, 2.23, 2.39, 2.14, 2.27, 2.21)  # Año2
carlson_3 <- c(1.89, 2.29, 2.83, 4.04, 2.31, 1.99, 2.42, 2.45, 2.57, 2.42, 2.40, 2.50)  # Año3
carlson_4 <- c(2.09, 2.54, 2.97, 4.35, 2.56, 2.28, 2.69, 2.48, 2.73, 2.37, 2.31, 2.23)  # Año4 (pre-huracán, Sep Año3-Ago Año4)

carlson_48 <- c(carlson_1, carlson_2, carlson_3, carlson_4)

condado_1 <- c(55.8, 56.4, 71.4, 117.6, 46.8, 48.0, 60.0, 57.6, 61.8, 58.2, 56.4, 63.0)
condado_2 <- c(57.6, 53.4, 71.4, 114.0, 46.8, 48.6, 59.4, 58.2, 60.6, 55.2, 51.0, 58.8)
condado_3 <- c(49.8, 54.6, 65.4, 102.0, 43.8, 45.6, 57.6, 53.4, 56.4, 52.8, 54.0, 60.6)
condado_4 <- c(47.4, 54.6, 67.8, 100.2, 48.0, 51.6, 57.6, 58.2, 60.0, 57.0, 57.6, 61.8)
condado_cierre <- c(69.0, 75.0, 85.2, 121.8)  # Sep, Oct, Nov, Dic del Año en que ocurrió el huracán (ventas reales, tienda cerrada)

condado_48 <- c(condado_1, condado_2, condado_3, condado_4)

t4 <- 1:48
df4 <- data.frame(t = t4, Mes = factor(rep(meses_fiscal, 4), levels = meses_fiscal),
                   Carlson = carlson_48, Condado = condado_48)

tabla(data.frame(Periodo = meses_fiscal, `Carlson Año4` = tail(carlson_48, 12),
                  `Condado Año4` = tail(condado_48, 12), check.names = FALSE),
      caption = "Últimos 12 meses antes del huracán (referencia)")
Últimos 12 meses antes del huracán (referencia)
Periodo Carlson Año4 Condado Año4
Sep 2.09 47.4
Oct 2.54 54.6
Nov 2.97 67.8
Dic 4.35 100.2
Ene 2.56 48.0
Feb 2.28 51.6
Mar 2.69 57.6
Abr 2.48 58.2
May 2.73 60.0
Jun 2.37 57.0
Jul 2.31 57.6
Ago 2.23 61.8
df4_long <- df4 %>% mutate(Fecha = seq(as.Date("2000-09-01"), by = "month", length.out = 48)) %>%
  pivot_longer(c(Carlson, Condado), names_to = "Serie", values_to = "Ventas")

ggplot(df4_long, aes(Fecha, Ventas, color = Serie)) +
  geom_line() +
  facet_wrap(~Serie, scales = "free_y", ncol = 1) +
  labs(title = "Ventas mensuales Carlson vs. Condado (48 meses previos al huracán)",
       y = "Ventas ($ millones)") +
  theme_minimal() + theme(legend.position = "none")

5.3 1. Estimación de las ventas que el condado habría tenido sin el huracán

Se ajusta un modelo de regresión con tendencia y variables dummy estacionales (mes) sobre los 48 meses previos al huracán, y se pronostican los 4 meses de cierre (septiembre-diciembre).

modelo_condado <- lm(Condado ~ t + Mes, data = df4)
tabla(as.data.frame(summary(modelo_condado)$coefficients), digits = 3,
      caption = "Regresión de tendencia + estacionalidad - Ventas del Condado")
Regresión de tendencia + estacionalidad - Ventas del Condado
Estimate Std. Error t value Pr(>|t|)
(Intercept) 54.400 1.752 31.058 0.000
t -0.092 0.035 -2.640 0.012
MesOct 2.192 2.293 0.956 0.346
MesNov 16.534 2.294 7.208 0.000
MesDic 56.076 2.295 24.431 0.000
MesEne -5.932 2.297 -2.582 0.014
MesFeb -3.740 2.299 -1.626 0.113
MesMar 6.552 2.302 2.846 0.007
MesAbr 4.845 2.306 2.101 0.043
MesMay 7.787 2.310 3.371 0.002
MesJun 3.979 2.314 1.719 0.094
MesJul 3.021 2.319 1.302 0.201
MesAgo 9.413 2.325 4.049 0.000
nuevo4 <- data.frame(t = 49:52, Mes = factor(c("Sep","Oct","Nov","Dic"), levels = meses_fiscal))
condado_pron_sinh <- predict(modelo_condado, newdata = nuevo4)

tabla(data.frame(Mes = c("Sep","Oct","Nov","Dic"),
                  `Condado pronosticado (sin huracán)` = round(condado_pron_sinh, 2),
                  `Condado real (con huracán)` = condado_cierre,
                  `Exceso ( real - pronosticado )` = round(condado_cierre - condado_pron_sinh, 2),
                  check.names = FALSE),
      caption = "Ventas del condado: pronosticadas (sin huracán) vs. reales (con huracán)")
Ventas del condado: pronosticadas (sin huracán) vs. reales (con huracán)
Mes Condado pronosticado (sin huracán) Condado real (con huracán) Exceso ( real - pronosticado )
Sep 49.89 69.0 19.11
Oct 51.99 75.0 23.01
Nov 66.24 85.2 18.96
Dic 105.69 121.8 16.11

5.4 2. Estimación de las ventas que Carlson habría tenido sin el huracán

Se modela la relación entre las ventas de Carlson y las ventas del condado (ambas comparten la tendencia y estacionalidad del mercado local), mediante una regresión de Carlson en función de las ventas del condado y el tiempo.

modelo_carlson <- lm(Carlson ~ Condado + t, data = df4)
tabla(as.data.frame(summary(modelo_carlson)$coefficients), digits = 4,
      caption = "Regresión de ventas Carlson en función de ventas del Condado y tendencia")
Regresión de ventas Carlson en función de ventas del Condado y tendencia
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.0655 0.1301 -0.5036 0.617
Condado 0.0357 0.0018 19.5452 0.000
t 0.0138 0.0021 6.6401 0.000
nuevo4_carlson <- data.frame(t = 49:52, Condado = condado_pron_sinh)
carlson_pron_sinh <- predict(modelo_carlson, newdata = nuevo4_carlson)

tabla(data.frame(Mes = c("Sep","Oct","Nov","Dic"),
                  `Carlson pronosticado (sin huracán)` = round(carlson_pron_sinh, 3),
                  `Carlson real (tienda cerrada)` = 0,
                  check.names = FALSE),
      caption = "Ventas de Carlson pronosticadas (escenario sin huracán)")
Ventas de Carlson pronosticadas (escenario sin huracán)
Mes Carlson pronosticado (sin huracán) Carlson real (tienda cerrada)
Sep 2.39 0
Oct 2.48 0
Nov 3.00 0
Dic 4.42 0

5.5 3. Estimación de la pérdida de ventas de Carlson (septiembre-diciembre)

perdida_total <- sum(carlson_pron_sinh)
tabla(data.frame(Mes = c("Sep","Oct","Nov","Dic", "TOTAL"),
                  `Ventas perdidas ($ millones)` = c(round(carlson_pron_sinh, 3), round(perdida_total, 3)),
                  check.names = FALSE),
      caption = "Pérdida de ventas estimada - Carlson Department Store")
Pérdida de ventas estimada - Carlson Department Store
Mes Ventas perdidas ($ millones)
1 Sep 2.39
2 Oct 2.48
3 Nov 3.00
4 Dic 4.42
TOTAL 12.30

La pérdida de ventas estimada para Carlson Department Store durante los cuatro meses que permaneció cerrada asciende a 12.3 millones de dólares.

5.6 Argumento a favor/en contra del exceso de ventas relacionado con el huracán

exceso_total <- sum(condado_cierre - condado_pron_sinh)
tabla(data.frame(
  Mes = c("Sep","Oct","Nov","Dic","TOTAL"),
  `Condado real` = c(condado_cierre, sum(condado_cierre)),
  `Condado pronosticado (sin huracán)` = c(round(condado_pron_sinh,2), round(sum(condado_pron_sinh),2)),
  `Exceso de ventas del condado` = c(round(condado_cierre - condado_pron_sinh, 2), round(exceso_total, 2)),
  check.names = FALSE
), caption = "Exceso de ventas del condado atribuible a la actividad post-huracán")
Exceso de ventas del condado atribuible a la actividad post-huracán
Mes Condado real Condado pronosticado (sin huracán) Exceso de ventas del condado
1 Sep 69.0 49.89 19.11
2 Oct 75.0 51.99 23.01
3 Nov 85.2 66.24 18.96
4 Dic 121.8 105.69 16.11
TOTAL 351.0 273.80 77.20

Las ventas reales de las tiendas departamentales del condado durante septiembre-diciembre (351 millones) superan de forma consistente en los cuatro meses al pronóstico de lo que habrían sido sin el huracán (273.8 millones), generando un exceso estimado de 77.2 millones de dólares en el condado. Esto es consistente con la inyección de más de $8,000 millones en ayuda federal y pagos de seguros que documenta el caso: ese dinero se tradujo en mayor actividad comercial (reconstrucción, reposición de bienes, etc.) que benefició a las demás tiendas departamentales que sí permanecieron abiertas.

Este hallazgo sustenta el argumento a favor de que sí hubo un exceso de ventas relacionado con el huracán en el condado — pero es clave notar que Carlson no participó de ese exceso porque estuvo cerrada; de hecho, es razonable argumentar ante la aseguradora que la pérdida de Carlson debería incluir no solo sus ventas “normales” esperadas, sino también una porción proporcional de ese exceso de actividad comercial que sus competidores sí captaron mientras ella estaba fuera de operación (venta que Carlson habría tenido la oportunidad de captar de no haber estado cerrada). Por ello, la estimación de pérdida de la sección anterior (basada únicamente en la relación histórica Carlson-condado antes del huracán) debe considerarse un piso conservador de la pérdida real de Carlson.

5.7 Conclusiones Ejercicio 4

  1. Las ventas del condado durante septiembre-diciembre habrían sido de aproximadamente 273.8 millones de dólares de no haber ocurrido el huracán.
  2. Carlson Department Store habría vendido aproximadamente 12.3 millones de dólares durante esos cuatro meses de no haber sido golpeada por el huracán; esa es la pérdida de ventas estimada a reclamar ante la aseguradora.
  3. Existe evidencia de un exceso de ventas en el condado de aproximadamente 77.2 millones de dólares durante el periodo de cierre, atribuible a la actividad económica post-huracán (reconstrucción, ayuda federal, seguros). Esto respalda el argumento de que Carlson tiene derecho a negociar una compensación adicional, más allá de sus ventas “normales” perdidas, dado que no pudo participar de ese repunte de la actividad comercial local por estar cerrada.

6 Conclusión general

A lo largo de los cuatro ejercicios se compararon distintos modelos de pronóstico de series de tiempo: modelos simples sin tendencia ni estacionalidad (Naive, promedios móviles simple y ponderado), adecuados para series estables de corto plazo como la del Ejercicio 1; y modelos que incorporan tendencia y estacionalidad (descomposición clásica y regresión con variables dummy), necesarios para series con estos patrones más complejos como Lechitas, Vintage Restaurant y Carlson Department Store. La elección del modelo correcto depende siempre de las características propias de cada serie (tendencia, estacionalidad, variabilidad), y su desempeño debe evaluarse con métricas de error como el MAE, MSE, RMSE y, sobre todo, el MAPE cuando se busca comparabilidad entre series de distinta escala.