Decoding Energy Patterns: Time Series Analysis of US Energy Consumption

R
Modeling
Quarto
Author

Zoe Zhou

Published

March 13, 2025

Renewable Energy Examples by GMS

Overview

In today’s data-driven world, understanding energy consumption patterns isn’t just for utility companies - it’s valuable for everyone interested in sustainability, energy planning, or data science techniques. Join me as I explore residential energy consumption trends using powerful time series analysis methods in R.

Data Summary

we will use data on US residential energy consumption from January 1973 - September 2023 (from the US Energy Information Administration).

  • Dataset: U.S. Residential Energy Consumption (Jan 1973 - Sep 2023)
  • Units: Trillion BTU
  • Source: US Energy Information Administration (https://www.eia.gov/totalenergy/data/monthly/index.php)

Objectives

  1. Examine patterns and trends in energy usage over this 50-year period
  2. Forecast consumption patterns five years into the future

Set up

Code
library(tidyverse)
library(here)
library(lubridate)
library(tsibble) 
library(feasts)
library(fable)
library(ggplot2)

energy_df <- read_csv(here("posts", "2025-03-13-energy", "data", "energy.csv"))

energy_ts <- energy_df %>% 
  mutate(date = yearmonth(yrmonth)) %>% 
  as_tsibble(index = date, 
             key = sector)

Decomposing the Time Series

To formally separate the components of our time series, I’ll use STL decomposition (Seasonal and Trend decomposition using LOESS):

STL is a versatile and robust method for decomposing time series. STL is an acronym for “Seasonal and Trend decomposition using LOESS”, while LOESS is a method for estimating nonlinear relationships.” LOESS (“Locally estimated scatterplot smoothing”) uses a weighted moving average across all points in the dataset, weighted by distance from the point being averaged.

Notice that it allows seasonality to vary over time (a major difference from classical decomposition, and important here since we do see changes in seasonality).

Code
# Find STL decomposition
dcmp <- energy_ts %>%
  filter(sector == 'residential') %>%
  model(feasts::STL(energy_total ~ season(period = '1 year') + trend(window = 25)))


# Visualize the decomposed components
components(dcmp) %>% 
  autoplot() +
  theme_minimal()

This decomposition clearly identifies:

  • The seasonal component with its bi-modal pattern

  • A trend component showing the overall increase and plateau

  • A remainder component that captures the noise in the data

Autocorrelation Analysis

To further understand the temporal dependencies in our data, we can use autocorrelation plots (ACF). These plots help us identify the correlation of a time series with its own past values.

Code
energy_ts %>% 
  filter(sector == 'residential') %>%
  ACF(energy_total) %>% 
  autoplot()

Looking at the autocorrelation function (ACF) confirmed the strong seasonal pattern, with the highest correlation at 12-month intervals

Forecasting

After understanding the historical patterns, I created a forecasting model using Exponential Smoothing (ETS). I specified a model with multiplicative seasonality and additive trend based on the change in variance over time and also within the secondary summer peak.

Code
energy_fit <- energy_ts %>%
  filter(sector == 'residential') %>%
  group_by_key(sector) %>%
  model(ets = ETS(energy_total ~ season(method = "M") + trend(method = "A")))

energy_forecast <- energy_fit %>% 
  forecast(h = "5 years")

# plot it added to the original data:
energy_forecast %>% 
  autoplot(energy_ts)

Model Validation

To validate the model’s performance, I compared the predicted values against the actual data:

Code
energy_predicted <- broom::augment(energy_fit)

# Create a more presentable comparison of actual vs. predicted energy consumption
ggplot(data = energy_predicted) +
  geom_line(aes(x = date, y = energy_total, color = "Actual Consumption"), linewidth = 0.8) +
  geom_line(aes(x = date, y = .fitted, color = "Model Prediction"), linewidth = 0.8) +
  labs(
    title = "U.S. Residential Energy Consumption: Actual vs. Predicted",
    subtitle = "Comparing observed data with ETS model predictions (1973-2023)",
    x = "Year",
    y = "Energy Consumption (Trillion BTU)",
    color = "Data Type"
  ) +
  scale_color_manual(values = c("Actual Consumption" = "#2E86C1", "Model Prediction" = "darkorange")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 10, color = "gray30"),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    panel.grid.minor = element_line(color = "gray90"),
    panel.grid.major = element_line(color = "gray80"),
    axis.title = element_text(face = "bold"),
    axis.text = element_text(size = 9)
  )

The model appears to capture both the seasonal patterns and overall trend effectively, with residuals that are approximately normally distributed.

Residual Analysis

The residuals appear roughly centered around zero, which is what we want to see. The distribution is approximately symmetric, suggesting our model isn’t systematically over- or under-predicting. There are some slight deviations from perfect normality, which could indicate specific periods where the model struggled to capture all aspects of the data. But overall, This residual analysis gives us confidence that our ETS model is performing reasonably well, though there’s always room for refinement. The residuals don’t show strong evidence of systematic errors, suggesting that the model has captured the main patterns in the residential energy consumption data.

Code
ggplot(data = energy_predicted, aes(x = .resid)) +
  geom_histogram(fill = "#4682B4", color = "white", bins = 30) +
  labs(
    title = "Distribution of Model Residuals",
    subtitle = "Assessing model performance through residual analysis",
    x = "Residual Value (Actual - Predicted)",
    y = "Frequency"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold"),
    plot.subtitle = element_text(color = "gray30")
  ) +
  geom_vline(xintercept = 0, linetype = "dashed", color = "red")

Conclusion

This analysis reveals several important insights about residential energy consumption in the United States:

  • The strong seasonal pattern has evolved to include both winter heating and summer cooling demands

  • Long-term growth appears to have plateaued in recent decades, possibly reflecting improved energy efficiency

  • The forecast suggests continuation of these patterns, with predictable seasonal fluctuations

Time series analysis provides powerful tools for understanding patterns in data that change over time. For energy consumption, these insights could help utilities plan capacity, policymakers design energy efficiency programs, and consumers understand their usage patterns.