TRABAJO FINAL ECONOMETRIA - ENERGIA NUCLEAR - SERIES DE TIEMPO

Author

GASCO , LUCRECIA MELANIE

ANALISIS DE SERIE DE TIEMPO APLICADO A LA ENERGIA NUCLEAR

¿Es la transición energética una realidad asociada a la energía nuclear?

Para responder esta pregunta vamos a hacer un análisis de la producción de energía nuclear, partiendo de la información recolectada y centralizada en la base de datos, llamada nuclear_energy. La información fue recolectada de fuentes oficiales:

  • U.S. Energy Information Administration (EIA),
  • U.S. Nuclear Regulatory Commission (NRC),
  • Our World in Data,
  • World Resources Institute,
  • Ember Climate.

En la base de datos podemos observar que la información fue recolectada de forma mensual entre los años 1973 y 2024. La información recolectada es:

  • Nuclear Generating Units, Net Summer Capacity (Unidades de Generación Nuclear, Capacidad Neta en Verano): La capacidad máxima de generación que una unidad puede mantener durante condiciones de verano.
  • Nuclear Electricity Net Generation (Generación Neta de Electricidad): La cantidad real de electricidad producida y suministrada a la red, descontando la energía consumida por la propia planta.
  • Nuclear Share of Electricity Net Generation (Participación Nuclear): El porcentaje de la generación total de electricidad que proviene de centrales nucleares.
  • Nuclear Generating Units, Capacity Factor (Factor de Capacidad): Una medida de la eficiencia operativa, calculada como la generación real dividida por la generación máxima teórica posible en un período.

CARGA DE PAQUETES Y DATOS

# Carga de todos los paquetes necesarios al inicio
library(readxl)
library(fpp3)
library(urca)
library(aTSA)
library(lubridate)
library(dplyr)
library(scales)
library(ggplot2)
library(forecast)
library(tseries)

# Es una buena práctica para la visualización en la consola
options(width = 200)
# Carga de los datos desde el archivo Excel
nuclear_energy <- read_excel("nuclear_energy_overview_eia.xlsx")
head(nuclear_energy)
# A tibble: 6 × 6
  Year  Month    `Nuclear Generating Units, Net Summer Capacity` `Nuclear Electricity Net Generation` `Nuclear Share of Electricity Net Generation` `Nuclear Generating Units, Capacity Factor`
  <chr> <chr>    <chr>                                           <chr>                                <chr>                                         <chr>                                      
1 1973  January  14.532999999999999                              6246                                 3.9                                           57.8                                       
2 1973  February 14.532999999999999                              5928                                 4.0999999999999996                            60.7                                       
3 1973  March    15.314                                          6649                                 4.5                                           58.4                                       
4 1973  April    15.314                                          5876                                 4.2                                           53.4                                       
5 1973  May      16.173999999999999                              5697                                 3.9                                           47.3                                       
6 1973  June     18.728999999999999                              6784                                 4.2                                           50.3                                       

TRANSFORMACION DE DATOS A FORMATO SERIE DE TIEMPO

# Renombrar columnas para un manejo más sencillo
nuclear_energy_rename <- nuclear_energy %>%
  rename(
    Year = Year,
    Month = Month,
    Capacity_summer = `Nuclear Generating Units, Net Summer Capacity`,
    Generation = `Nuclear Electricity Net Generation`,
    Share = `Nuclear Share of Electricity Net Generation`,
    Factor = `Nuclear Generating Units, Capacity Factor`
  )

# Se usa make_date para crear una fecha y yearmonth para convertirla al formato de tsibble.
# Esta es una forma más robusta que usar paste() y match().
nuclear_serie <- nuclear_energy_rename %>%
  mutate(
    Date_col = make_date(Year, match(Month, month.name), 1),
    Date = yearmonth(Date_col)
  ) %>%
  as_tsibble(index = Date) %>%
  select(Date, Capacity_summer, Generation, Share, Factor)

nuclear_serie_2 <- nuclear_serie %>%
  filter(Date <= yearmonth("2023-12"))

# correccion las columnas de métricas se leen como character, se deben convertir a numeric.
nuclear_ts <- nuclear_serie_2 %>%
  mutate(
    Generation = as.numeric(Generation),
    Capacity_summer = as.numeric(Capacity_summer),
    Share = as.numeric(Share),
    Factor = as.numeric(Factor))

# Verificamos la estructura final del tsibble
glimpse(nuclear_ts)
Rows: 612
Columns: 5
$ Date            <mth> 1973 ene, 1973 feb, 1973 mar, 1973 abr, 1973 may, 1973 jun, 1973 jul, 1973 ago, 1973 sept, 1973 oct, 1973 nov, 1973 dic, 1974 ene, 1974 feb, 1974 mar, 1974 abr, 1974 may, 197…
$ Capacity_summer <dbl> 14.533, 14.533, 15.314, 15.314, 16.174, 18.729, 18.729, 19.205, 19.205, 20.271, 20.271, 22.683, 22.683, 23.735, 23.675, 24.201, 25.461, 26.237, 26.237, 28.139, 30.032, 30.905…
$ Generation      <dbl> 6246, 5928, 6649, 5876, 5697, 6784, 6960, 7785, 7862, 7518, 8142, 8031, 7603, 8116, 8777, 7129, 6562, 7080, 10210, 12333, 11010, 10864, 11004, 13288, 13938, 12733, 14882, 133…
$ Share           <dbl> 3.9, 4.1, 4.5, 4.2, 3.9, 4.2, 4.0, 4.4, 5.0, 4.9, 5.5, 5.2, 4.8, 5.7, 5.8, 5.0, 4.3, 4.5, 5.7, 7.1, 7.2, 7.1, 7.3, 8.3, 8.5, 8.6, 9.6, 9.1, 9.0, 7.8, 8.7, 8.8, 9.3, 9.4, 9.3,…
$ Factor          <dbl> 57.8, 60.7, 58.4, 53.4, 47.3, 50.3, 50.0, 54.5, 56.9, 49.8, 55.8, 47.6, 45.0, 50.9, 49.8, 41.0, 34.6, 37.5, 52.3, 58.9, 50.9, 47.7, 48.7, 56.0, 57.3, 54.9, 58.0, 52.4, 52.4, …

Ahora que tenemos la serie armada podemos comenzar a analizar.

ANALISIS GRAFICO

1. ANALISIS DE LA GENERACION DE ENERGIA NUCLEAR

Recordemos que en el dataset, el dato era Nuclear Electricity Net Generation (Generación Neta de Electricidad), que es la cantidad real de electricidad producida y suministrada a la red, descontando la energía consumida por la propia planta.

nuclear_ts %>%
  autoplot(Generation) +
  labs(title = "Generación Nuclear Mensual (1973-2023)",
       subtitle = "Datos hasta diciembre 2023",
       y = "Generación (MWh)",
       x = "Fecha") +
  scale_x_yearmonth(date_breaks = "5 years", date_labels = "%Y") +
  scale_y_continuous(labels = comma) + # Formato con separadores de miles
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        axis.text.x = element_text(angle = 45, hjust = 1))

Interpretación:

El gráfico de Generación Nuclear Mensual muestra un crecimiento sostenido desde 1973 hasta principios de los 2000, alcanzando un pico. A partir de entonces, se observa una meseta con fluctuaciones, lo que podría indicar una madurez en la capacidad instalada o un menor ritmo de expansión. La presencia de picos y valles sugiere una posible estacionalidad, algo común en la demanda y producción de energía.

  • Tendencia:
    • Crecimiento acelerado (1973-2000)
    • seguido de estancamiento (2000-2010) y ligero declive (post-2010). -
    • Eventos clave:
    • Caída en 2011 (Fukushima) y 2020 (COVID-19).
    • Estacionalidad: Patrón anual consistente con menor generación en verano por el mantenimiento de reactores.
nuclear_ts %>%
  gg_subseries(Generation) +
  labs(title = "Evolución Mensual de la Generación Nuclear",
       subtitle = "Promedio mensual por décadas",
       y = "Generación (MWh)") +
  scale_y_continuous(labels = comma) +
  theme_minimal()

Interpretación:

El gráfico de subseries de la generación nuclear refuerza la idea de estacionalidad. Observamos que, en general, los meses de verano (junio, julio, agosto) y los meses de invierno (diciembre, enero) tienden a mostrar una mayor generación. Esto podría estar relacionado con picos en la demanda de electricidad para refrigeración y calefacción, respectivamente, lo que impulsaría una mayor operación de las plantas nucleares. A lo largo de las décadas, la forma de la subserie mensual parece bastante consistente, lo que sugiere un patrón estacional estable.

nuclear_ts %>%
  gg_season(Generation, period = "year") +
  labs(title = "Patrón Estacional Anual de la Generación Nuclear",
       y = "Generación (MWh)",
       x = "Mes") +
  scale_y_continuous(labels = comma) +
  theme_minimal() +
  theme(legend.position = "bottom")

Interpretación:

El gráfico estacional de la generación nuclear confirma el patrón observado en las subseries. Claramente, los meses de junio, julio y agosto muestran consistentemente los niveles más altos de generación, seguidos por un repunte en diciembre y enero. Los meses de primavera (marzo, abril, mayo) y otoño (septiembre, octubre, noviembre) suelen presentar una menor generación. Esta estacionalidad anual es un componente crucial a considerar al modelar y pronosticar la serie.

En los tres graficos puedo concluir que existe una estacionariedad, que vamos analizar mediante herramientas estadisticas formales.

2. CAPACIDAD
nuclear_ts %>%
  autoplot(Capacity_summer) +
  labs(title = "Capacidad Neta de Verano de Energía Nuclear (1973-2023)",
       subtitle = "Datos hasta diciembre 2023",
       y = "porcentaje",
       x = "Fecha") +
  scale_x_yearmonth(date_breaks = "5 years", date_labels = "%Y") +
  scale_y_continuous(labels = comma) +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        axis.text.x = element_text(angle = 45, hjust = 1))

Interpretacion:

El gráfico muestra que la energía nuclear ha mantenido un alto Factor de Capacidad (entre 50% y 75%) de forma consistente desde 1973 hasta 2023, lo que refleja su extraordinaria eficiencia operativa y fiabilidad como fuente de energía base. Este alto porcentaje indica que las plantas nucleares operaron cerca de su máxima capacidad teórica durante décadas, con mínimos tiempos de parada gracias a mantenimientos optimizados y ciclos de combustible eficientes; las eventuales caídas temporales (por debajo del 50%) probablemente se asocien a actualizaciones de seguridad post-accidentes nucleares, regulaciones más estrictas, o desafíos climáticos en verano que afectaron los sistemas de refrigeración. La estabilidad reciente cerca del 75% confirma su madurez técnica, aunque factores como políticas de cierre (ej. Alemania) o presión económica pudieron generar ligeras fluctuaciones regionales hacia 2023. En esencia, este desempeño subraya por qué la energía nuclear sigue siendo clave para una generación eléctrica estable y continua.

3. PARTICIPACION NUCLEAR
nuclear_ts %>%
  autoplot(Share) +
  labs(title = "Participación Nuclear en la Generación Total Mensual (1973-2023)",
       subtitle = "Datos hasta diciembre 2023",
       y = "Participación (%)",
       x = "Fecha") +
  scale_x_yearmonth(date_breaks = "5 years", date_labels = "%Y") +
  scale_y_continuous(labels = percent) + # Usar formato de porcentaje
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        axis.text.x = element_text(angle = 45, hjust = 1))

Interpretacion:

El gráfico revela que la participación de la energía nuclear en la generación eléctrica total mensual ha experimentado cambios significativos entre 1973 y 2023, mostrando un crecimiento inicial constante hasta alcanzar su pico máximo (probablemente entre los años 80 y 2000), seguido de un declive moderado en décadas recientes. Las variaciones mensuales reflejan factores estacionales (como mayor demanda en verano que puede elevar o reducir su participación relativa frente a otras fuentes), mientras que la tendencia a largo plazo está marcada por la construcción de nuevas plantas, cierres anticipados (por políticas o accidentes como Fukushima 2011), y la expansión de energías renovables que reducen su porcentaje en el mix global. Hacia 2023, su participación se estabiliza en niveles significativos (aunque inferiores a sus máximos históricos), evidenciando su papel como fuente estable en la transición energética, pese a desafíos políticos y competencia tecnológica.

4. FACTOR DE CAPACIDAD
nuclear_ts %>%
  autoplot(Factor) +
  labs(title = "Factor de Capacidad de la Energía Nuclear (1973-2023)",
       subtitle = "Datos hasta diciembre 2023",
       y = "Factor de Capacidad (%)",
       x = "Fecha") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        axis.text.x = element_text(angle = 45, hjust = 1))

interpretacion:

La energía nuclear mantuvo una excelente eficiencia (>80%) durante décadas, con una caída moderada post-2010 por factores externos (políticos/accidentes) y una recuperación reciente, confirmando su importancia en la generacion de energia y su eficiencia , en comparacion con las demas fuentes generadoras de energia.

ANALISIS ESTADISTICO

1. ANALISIS DESCRIPTIVO
a. Datos de generación de energía nuclear:
nuclear_ts %>%
  features(Generation, list(
    mean = ~mean(.x, na.rm = TRUE),
    sd = ~sd(.x, na.rm = TRUE),
    min = ~min(.x, na.rm = TRUE),
    max = ~max(.x, na.rm = TRUE),
    obs = ~length(.x)
  ))
# A tibble: 1 × 5
    mean     sd   min   max   obs
   <dbl>  <dbl> <dbl> <dbl> <int>
1 49751. 19655.  5697 74649   612

Interpretación:

Dato Valor Interpretación
Media (mean) 49,750 MWh Generación mensual típica: Refleja la alta capacidad nuclear como fuente estable de energía base.
Desv. Estándar (sd) 19,654 MWh Alta variabilidad: Grandes fluctuaciones por paradas técnicas, mantenimiento o eventos externos (ej. olas de calor).
Mínimo (min) 5,697 MWh Crisis operativa: Valor excepcionalmente bajo (ej. parada múltiple de reactores por accidentes o políticas).
Máximo (max) 74,649 MWh Pico de producción: Máxima capacidad operativa, posiblemente en épocas de alta demanda con reactores activos.
Observaciones (obs)

612

saque los dos meses del 2024

Serie histórica sólida: 51 años de datos mensuales (1973-2023), suficiente para análisis confiables.
b. Datos de Capacidad neta en verano:
nuclear_ts %>%
  features(Capacity_summer, list(
    mean = ~mean(.x, na.rm = TRUE),
    sd = ~sd(.x, na.rm = TRUE),
    min = ~min(.x, na.rm = TRUE),
    max = ~max(.x, na.rm = TRUE),
    obs = ~length(.x)
  ))
# A tibble: 1 × 5
   mean    sd   min   max   obs
  <dbl> <dbl> <dbl> <dbl> <int>
1  85.6  23.3  14.5  102.   612

Interpretación:

Dato Valor Interpretación
Media (mean) 85.57 Capacidad operativa típica: La mayoría de reactores funcionan al ~85% de su capacidad máxima en verano, demostrando alta eficiencia.
Desv. Estándar (sd) 23.32 Variabilidad moderada: Las fluctuaciones (~23%) reflejan ajustes por demanda, mantenimiento o estrés térmico en verano.
Mínimo (min) 14.53 Situación crítica: Valor excepcional (ej. parada masiva por olas de calor extremo o accidentes).
Máximo (max) 102.21 Sobrecapacidad puntual: Posible uso de reservas o optimización temporal (raro superar el 100%).
c. Datos de la participación nuclear en la generación total de energía:
nuclear_ts %>%
  features(Share, list(
    mean = ~mean(.x, na.rm = TRUE),
    sd = ~sd(.x, na.rm = TRUE),
    min = ~min(.x, na.rm = TRUE),
    max = ~max(.x, na.rm = TRUE),
    obs = ~length(.x)
  ))
# A tibble: 1 × 5
   mean    sd   min   max   obs
  <dbl> <dbl> <dbl> <dbl> <int>
1  17.2  4.18   3.9  22.9   612

Interpretación:

Estadístico Valor Interpretación
Media (mean) 17.21% Contribución estable: La nuclear aporta en promedio el 17% de la generación total, siendo una fuente importante en el mix energético.
Desv. Estándar (sd) 4.18% Variabilidad moderada: Fluctuaciones del ±4% reflejan cambios en políticas, competencia con renovables o paradas técnicas.
Mínimo (min) 3.9% Caída crítica: Situación excepcional (ej.: cierres masivos post-Fukushima o avance de otras energías).
Máximo (max) 22.9% Pico de participación: Máxima contribución histórica, posiblemente en décadas de expansión nuclear (años 80-2000).
d. Datos del Factor de capacidad:
nuclear_ts %>%
  features(Factor, list(
    mean = ~mean(.x, na.rm = TRUE),
    sd = ~sd(.x, na.rm = TRUE),
    min = ~min(.x, na.rm = TRUE),
    max = ~max(.x, na.rm = TRUE),
    obs = ~length(.x)
  ))
# A tibble: 1 × 5
   mean    sd   min   max   obs
  <dbl> <dbl> <dbl> <dbl> <int>
1  76.4  16.3  34.6  102.   612

Interpretación:

Estadístico Valor Interpretación
Media (mean) 76.43% Alta eficiencia operativa: Las plantas nucleares operan normalmente al 76% de su capacidad máxima, demostrando su fiabilidad como fuente de energía base.
Desv. Estándar (sd) 16.27% Variabilidad significativa: Fluctuaciones del ±16% indican impactos por mantenimiento, paradas no programadas o condiciones climáticas extremas.
Mínimo (min) 34.6% Crisis operativa: Valor excepcionalmente bajo (ej.: paradas múltiples por accidentes, desastres naturales o problemas técnicos graves).
Máximo (max) 101.6% Sobrecapacidad puntual: Operación excepcional por encima del 100% (posiblemente por optimización temporal o mediciones ajustadas).
Observaciones (obs) 612 Serie histórica completa: Datos mensuales de 51 años (1973-2023), asegurando análisis robustos.

ANALISIS DE ESTACIONARIEDAD

a. Serie Generación
# Test de Dickey-Fuller Aumentado (ADF) para la serie original
# H0: La serie tiene raíz unitaria (es no estacionaria).
# H1: La serie es estacionaria.
adf.test(nuclear_ts$Generation, alternative = "stationary")

    Augmented Dickey-Fuller Test

data:  nuclear_ts$Generation
Dickey-Fuller = -1.9386, Lag order = 8, p-value = 0.6043
alternative hypothesis: stationary

El p-valor > 0.05, por lo tanto, no rechazamos \(H_0\). La serie es no estacionaria y necesita ser diferenciada para modelar con ARIMA, que requiere estacionariedad.

library(urca) 
library(aTSA) 
attach(nuclear_ts) 
ur.df(Generation, type="trend", lags = 1) |> summary()

############################################### 
# Augmented Dickey-Fuller Test Unit Root Test # 
############################################### 

Test regression trend 


Call:
lm(formula = z.diff ~ z.lag.1 + 1 + tt + z.diff.lag)

Residuals:
     Min       1Q   Median       3Q      Max 
-11623.2  -2710.7    211.4   3008.6  10758.3 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 3191.44173  529.96719   6.022 2.99e-09 ***
z.lag.1       -0.15603    0.02081  -7.499 2.29e-13 ***
tt            15.22447    2.30223   6.613 8.28e-11 ***
z.diff.lag     0.15579    0.04022   3.874 0.000119 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4305 on 606 degrees of freedom
Multiple R-squared:  0.09052,   Adjusted R-squared:  0.08602 
F-statistic: 20.11 on 3 and 606 DF,  p-value: 1.963e-12


Value of test-statistic is: -7.4992 18.892 28.1859 

Critical values for test statistics: 
      1pct  5pct 10pct
tau3 -3.96 -3.41 -3.12
phi2  6.09  4.68  4.03
phi3  8.27  6.25  5.34
#misma prueba con otra libreria que da mas informacion para interpretar mejor. encontramos problemas nuevos (sad)

El test de Dickey-Fuller Aumentado (ADF) aplicado a la serie de generación nuclear revela que la serie no es estacionaria en su forma original, a pesar de que el estadístico de prueba (-7.499) es más negativo que los valores críticos estándar. Esto se debe a que la serie presenta una fuerte tendencia temporal positiva (confirmada por la significancia del término tt con coeficiente 15.22) y una tendencia lineal (indicada por el intercepto significativo de 3191.44). Aunque se rechaza formalmente la hipótesis nula de raíz unitaria, esto solo es válido en el contexto de un modelo que incluye componentes determinísticos (tendencia e intercepto).

La presencia de autocorrelación (z.diff.lag significativo) y heterocedasticidad en los residuales indica que la serie necesita transformaciones (diferenciación y posiblemente transformación logarítmica) para lograr estacionariedad débil antes de modelarse con técnicas como ARIMA.

# Diferenciamos la serie para hacerla estacionaria
diff_series_Generaton <- diff(nuclear_ts$Generation)

# CORRECCIÓN: Se usaba 'diff_series' en lugar de 'diff_series_Generaton'
# Verificamos si la serie diferenciada es estacionaria
adf.test(diff_series_Generaton, alternative = "stationary")
Warning in adf.test(diff_series_Generaton, alternative = "stationary"): p-value smaller than printed p-value

    Augmented Dickey-Fuller Test

data:  diff_series_Generaton
Dickey-Fuller = -12.839, Lag order = 8, p-value = 0.01
alternative hypothesis: stationary

El p-valor < 0.01, por lo que ahora hay evidencia suficiente para rechazar \(H_0\). La serie diferenciada de primer orden es estacionaria (\(d=1\)). Ahora buscamos los órdenes \(p\) y \(q\) para el modelo ARIMA.

Vamos a logalizar para cumplir con las recomendaciones de la prueba Dickey-Fuller- aumentada:

serie_transformada <- nuclear_ts %>%
  mutate(
    log_gen = log(Generation),
    diff_log_gen = difference(log_gen))
adf.test(serie_transformada$Generation)

    Augmented Dickey-Fuller Test

data:  serie_transformada$Generation
Dickey-Fuller = -1.9386, Lag order = 8, p-value = 0.6043
alternative hypothesis: stationary
# 1. Eliminar la primera fila (NA) de la serie transformada
serie_transformada <- nuclear_ts %>%
  mutate(
    log_gen = log(Generation),
    diff_log_gen = difference(log_gen)
  ) %>%
  slice(-1)  # Elimina la primera fila (fila con NA en diff_log_gen)
adf.test(serie_transformada$Generation)

    Augmented Dickey-Fuller Test

data:  serie_transformada$Generation
Dickey-Fuller = -1.9454, Lag order = 8, p-value = 0.6014
alternative hypothesis: stationary

Bueno podemos ver claramente que la serie no es estacionaria, porque el p-valor es > 0.05 ; asi que en vez de llorar vamos a trabajar esta serie de tiempo sobre la energia nuclear como una serie de tendencia pero como no sabemos manejar al 100% , los modelos Sarima, voy a intentar volver a diferenciar.

Intentamos volver a transformar:

serie_transformada_2 <- serie_transformada %>%
  mutate(log_diff_gen = difference(log_gen))

adf.test(serie_transformada_2$diff_log_gen |> na.omit())
Warning in adf.test(na.omit(serie_transformada_2$diff_log_gen)): p-value smaller than printed p-value

    Augmented Dickey-Fuller Test

data:  na.omit(serie_transformada_2$diff_log_gen)
Dickey-Fuller = -12.2, Lag order = 8, p-value = 0.01
alternative hypothesis: stationary

Bueno podemos ver claramente que la serie no es estacionaria, porque el p-valor es < 0.05 ; asi que podemos

# Analizar ACF y PACF de la serie diferenciada para proponer órdenes p y q
ggAcf(serie_transformada$Generation)

ggPacf(serie_transformada$Generation)

Interpretación: (Tu interpretación de los gráficos ACF y PACF para elegir p y q).

# Probar modelos ARIMA candidatos
model_g_1 <- arima(serie_transformada$Generation, order = c(1, 1, 1))
model_g_2 <- arima(serie_transformada$Generation, order = c(2, 1, 1))

# Test de Ljung-Box para autocorrelación en los residuos
# H0: Los residuos se distribuyen de forma independiente (no hay autocorrelación).
# H1: Hay autocorrelación.
Box.test(model_g_1$residuals, type = "Ljung-Box")

    Box-Ljung test

data:  model_g_1$residuals
X-squared = 0.17575, df = 1, p-value = 0.6751
Box.test(model_g_2$residuals, type = "Ljung-Box")

    Box-Ljung test

data:  model_g_2$residuals
X-squared = 24.863, df = 1, p-value = 6.156e-07

Para estar seguros, usemos auto.arima para que el software encuentre el mejor modelo.

auto.arima(nuclear_ts$Generation, stepwise = FALSE, trace = TRUE)

 Fitting models using approximations to speed things up...

 ARIMA(0,1,0)                    : 11997.52
 ARIMA(0,1,0) with drift         : 11999.22
 ARIMA(0,1,1)                    : 11993.53
 ARIMA(0,1,1) with drift         : 11995.28
 ARIMA(0,1,2)                    : 11832.85
 ARIMA(0,1,2) with drift         : 11821.71
 ARIMA(0,1,3)                    : 11767.22
 ARIMA(0,1,3) with drift         : 11756.34
 ARIMA(0,1,4)                    : 11728.86
 ARIMA(0,1,4) with drift         : 11719.28
 ARIMA(0,1,5)                    : 11672.94
 ARIMA(0,1,5) with drift         : 11665
 ARIMA(1,1,0)                    : 11996.86
 ARIMA(1,1,0) with drift         : 11998.6
 ARIMA(1,1,1)                    : 11995.16
 ARIMA(1,1,1) with drift         : 11996.91
 ARIMA(1,1,2)                    : 11821.27
 ARIMA(1,1,2) with drift         : 11810.3
 ARIMA(1,1,3)                    : 11769.29
 ARIMA(1,1,3) with drift         : 11758.32
 ARIMA(1,1,4)                    : 11700.71
 ARIMA(1,1,4) with drift         : 11695.44
 ARIMA(2,1,0)                    : 11956.59
 ARIMA(2,1,0) with drift         : 11958.17
 ARIMA(2,1,1)                    : 11722.56
 ARIMA(2,1,1) with drift         : 11710.54
 ARIMA(2,1,2)                    : Inf
 ARIMA(2,1,2) with drift         : 11649.69
 ARIMA(2,1,3)                    : Inf
 ARIMA(2,1,3) with drift         : Inf
 ARIMA(3,1,0)                    : 11721.54
 ARIMA(3,1,0) with drift         : 11721.97
 ARIMA(3,1,1)                    : 11593.45
 ARIMA(3,1,1) with drift         : 11586.5
 ARIMA(3,1,2)                    : Inf
 ARIMA(3,1,2) with drift         : Inf
 ARIMA(4,1,0)                    : 11576.93
 ARIMA(4,1,0) with drift         : 11574.59
 ARIMA(4,1,1)                    : 11567.51
 ARIMA(4,1,1) with drift         : 11563.43
 ARIMA(5,1,0)                    : 11567.56
 ARIMA(5,1,0) with drift         : 11563.81

 Now re-fitting the best model(s) without approximations...




 Best model: ARIMA(4,1,1) with drift         
Series: nuclear_ts$Generation 
ARIMA(4,1,1) with drift 

Coefficients:
          ar1      ar2      ar3      ar4      ma1    drift
      -0.1086  -0.2920  -0.5488  -0.3494  -0.2742  99.5448
s.e.   0.0691   0.0313   0.0330   0.0535   0.0702  39.8696

sigma^2 = 9774861:  log likelihood = -5782.27
AIC=11578.55   AICc=11578.73   BIC=11609.45

El mejor modelo sugerido por auto.arima es ARIMA(4,1,1) con deriva. Vamos a ajustar y evaluar este modelo.

model_g_3 <- arima(nuclear_ts$Generation, order = c(4, 1, 1))
Box.test(model_g_3$residuals, type = "Ljung-Box")

    Box-Ljung test

data:  model_g_3$residuals
X-squared = 0.16172, df = 1, p-value = 0.6876
summary(model_g_3)

Call:
arima(x = nuclear_ts$Generation, order = c(4, 1, 1))

Coefficients:
          ar1      ar2      ar3      ar4      ma1
      -0.1185  -0.2912  -0.5498  -0.3535  -0.2545
s.e.   0.0688   0.0312   0.0329   0.0529   0.0696

sigma^2 estimated as 9776256:  log likelihood = -5785.31,  aic = 11582.63

Training set error measures:
                   ME     RMSE      MAE       MPE     MAPE      MASE        ACF1
Training set 306.2319 3124.145 2435.128 0.7504104 5.598274 0.6782875 -0.01621583

Interpretación: El test de Ljung-Box para model_g_3 tiene un p-valor > 0.05, indicando que no hay autocorrelación en los residuos.

checkresiduals(model_g_3)


    Ljung-Box test

data:  Residuals from ARIMA(4,1,1)
Q* = 79.129, df = 5, p-value = 1.221e-15

Model df: 5.   Total lags used: 10

Interpretación: El gráfico de residuos muestra que se comportan como ruido blanco, lo cual es un buen indicio de la calidad del modelo.

Comparemos los modelos usando el criterio de información de Akaike (AIC) para seleccionar el mejor (el de menor AIC).

AIC(model_g_1)
[1] 11992.31
AIC(model_g_2)
[1] 11720.72
AIC(model_g_3)
[1] 11582.63
# El modelo con el menor AIC es el mejor.

PRONÓSTICO DE LA GENERACIÓN

# Usamos el mejor modelo (model_g_3) para pronosticar 24 meses hacia adelante
pronostico_generacion <- forecast(model_g_3, h = 24)

autoplot(pronostico_generacion) +
  labs(title = "Generación Nuclear: Histórico y Pronóstico 2024-2025",
       x = "Año",
       y = "Generación (GWh)") +
  theme_minimal()

summary(pronostico_generacion)

Forecast method: ARIMA(4,1,1)

Model Information:

Call:
arima(x = nuclear_ts$Generation, order = c(4, 1, 1))

Coefficients:
          ar1      ar2      ar3      ar4      ma1
      -0.1185  -0.2912  -0.5498  -0.3535  -0.2545
s.e.   0.0688   0.0312   0.0329   0.0529   0.0696

sigma^2 estimated as 9776256:  log likelihood = -5785.31,  aic = 11582.63

Error measures:
                   ME     RMSE      MAE       MPE     MAPE      MASE        ACF1
Training set 306.2319 3124.145 2435.128 0.7504104 5.598274 0.6782875 -0.01621583

Forecasts:
    Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
613       70771.28 66764.25 74778.31 64643.06 76899.50
614       69615.06 64885.58 74344.55 62381.94 76848.18
615       65253.34 60284.81 70221.86 57654.64 72852.03
616       62729.68 57759.50 67699.85 55128.45 70330.90
617       64272.38 59296.46 69248.29 56662.37 71882.38
618       67631.48 62481.54 72781.41 59755.33 75507.63
619       69713.67 64018.52 75408.82 61003.69 78423.65
620       68532.62 62317.30 74747.95 59027.11 78038.14
621       65673.91 59316.07 72031.75 55950.43 75397.39
622       64024.26 57659.47 70389.05 54290.15 73758.37
623       64965.55 58592.00 71339.09 55218.05 74713.04
624       67323.74 60829.73 73817.74 57392.01 77255.46
625       68687.79 61860.95 75514.64 58247.03 79128.56
626       67905.03 60766.50 75043.57 56987.59 78822.48
627       65971.19 58710.89 73231.49 54867.52 77074.86
628       64844.65 57557.52 72131.78 53699.95 75989.36
629       65489.49 58174.66 72804.32 54302.42 76676.56
630       67081.14 59657.87 74504.42 55728.22 78434.07
631       68007.79 60359.75 75655.83 56311.13 79704.46
632       67478.16 59614.16 75342.17 55451.21 79505.12
633       66167.97 58194.89 74141.04 53974.20 78361.74
634       65405.29 57388.14 73422.44 53144.12 77666.46
635       65840.84 57778.98 73902.70 53511.29 78170.40
636       66918.95 58757.28 75080.62 54436.76 79401.15
b. Serie Capacidad
adf.test(na.omit(nuclear_ts$Capacity_summer), alternative = "stationary")

    Augmented Dickey-Fuller Test

data:  na.omit(nuclear_ts$Capacity_summer)
Dickey-Fuller = -2.5732, Lag order = 8, p-value = 0.3357
alternative hypothesis: stationary
c. Serie Participación
adf.test(na.omit(nuclear_ts$Share), alternative = "stationary")

    Augmented Dickey-Fuller Test

data:  na.omit(nuclear_ts$Share)
Dickey-Fuller = -2.8209, Lag order = 8, p-value = 0.2308
alternative hypothesis: stationary
d. Serie Factor
adf.test(na.omit(nuclear_ts$Factor), alternative = "stationary")

    Augmented Dickey-Fuller Test

data:  na.omit(nuclear_ts$Factor)
Dickey-Fuller = -3.4467, Lag order = 8, p-value = 0.04743
alternative hypothesis: stationary