energy_series <- vic_elec %>%
filter_index(~ "2014-12-31") %>%
dplyr::select(Time, Demand, Temperature) %>%
drop_na()Activity34
Extending ETS Models with Predictors
Data Prep
- Use historical energy demand + temperature
- Ensure alignment in timestamps (
drop_na())
Step 1 - Baseline ETS Model
# 1. ETS for Demand (non-seasonal)
demand_ets <- energy_series %>% model(ETS(Demand))
demand_ets_fc <- demand_ets %>%
forecast(h = "1 week") %>%
as_tibble() %>%
rename(mean = .mean) %>%
mutate(Model = "ETS")\[\text{Demand}_t = \text{Level}_{t-1} + \text{Error}_t\]
The first model uses ETS (Error-Trend-Seasonal) for demand alone. This is limited because:
- Ignores temperature (known predictor)
- Assumes patterns are purely endogenous
Step 2 - ARIMAX with Temperature
# 2. ARIMAX with temperature forecasts
# First forecast temperature
temp_ets <- energy_series %>% model(ETS(Temperature))
temp_fc <- temp_ets %>%
forecast(h = "1 week") %>%
as_tibble() %>%
dplyr::select(Time, Temperature = .mean)
demand_dr <- energy_series %>% model(ARIMA(Demand ~ Temperature))
demand_dr_fc <- demand_dr %>%
forecast(new_data = temp_fc %>% as_tsibble(index = Time)) %>%
as_tibble() %>%
rename(mean = .mean) %>%
mutate(Model = "ARIMAX")\[\text{Demand}_t = \phi_1 \text{Demand}_{t-1} + \beta \text{Temperature}_t + \epsilon_t\]
Improvement: Adds temperature as exogenous predictor.
But requires temperature forecasts - introduces error propagation if temperature predictions are poor.
Part 2: Why VAR Models Shine for Short-Term
Vector Autoregression (VAR)
\[\begin{cases} \text{Demand}_t = \alpha_1 + \sum_{i=1}^p \phi_{1i}\text{Demand}_{t-i} + \sum_{i=1}^p \psi_{1i}\text{Temp}_{t-i} \\ \text{Temp}_t = \alpha_2 + \sum_{i=1}^p \phi_{2i}\text{Demand}_{t-i} + \sum_{i=1}^p \psi_{2i}\text{Temp}_{t-i} \end{cases}\]
Key Advantages
- Handles bidirectional relationships (temperature ↔︎ demand)
- Captures lagged cross-effects
- Better for short-term forecasts where system inertia matters
3. Critical Implementation Details
- Differencing: Makes series stationary for VAR (
difference()) - Lag choice: 48 lags = 24hr periodicity (half-hourly data)
- Forecast alignment:
\[\text{VarForecast}_t = \text{LastObs} + \sum \text{DifferencedForecasts}_t\]
(Reverse the differencing through cumulative sums)
4. Comparison
- ETS: Good baseline but misses covariates
- ARIMAX: Better with good temp forecasts
- VAR: Best for short-term co-movements, no external forecast needed
# 3. VAR with tsDyn implementation
var_data <- energy_series %>% as_tibble() %>%
select(Demand, Temperature) %>%
mutate(across(everything(), difference)) %>%
tidyr::drop_na()
var_model <- tsDyn::lineVar(var_data, lag = 48)
var_fc <- predict(var_model, n.ahead = 336)
# Align VAR forecasts with original scale
last_obs <- tail(energy_series, 1)
var_fc_demand <- tibble(
Time = demand_ets_fc$Time,
mean = last_obs$Demand + cumsum(var_fc[,1]),
Model = "VAR"
)
# Combine forecasts
combined_fc <- bind_rows(
demand_ets_fc %>% select(Time, Model, mean),
demand_dr_fc %>% select(Time, Model, mean),
var_fc_demand
)# Plot with accurate comparisons
ggplot() +
geom_line(data = energy_series %>% tail(2016), # Last 2 weeks
aes(x = Time, y = Demand), color = "gray40") +
geom_line(data = combined_fc,
aes(x = Time, y = mean, color = Model), linewidth = 0.8) +
labs(title = "Electricity Demand Forecasts: Three Methodologies",
subtitle = "Using actual temperature forecasts for ARIMAX\nVAR with 48-lag (24h) differenced model",
y = "Demand (MW)") +
theme_minimal() +
scale_color_viridis_d(option = "H")