RPubs Link: http://rpubs.com/sydneyjanssen/1452448
Gasoline prices in the U.S. have been historically prone to sharp increases, including a 48% spike within the first half of 2022 (He, 2023). This pattern is also visible in the more recent data examined in this study. This paper forecasts U.S. regular gasoline prices from January 2005 to August 2026 using WTI crude oil as an external predictor, and evaluates whether machine learning methods, including XGBoost and NNETAR, outperform a traditional dynamic regression approach.
Gasoline prices affect individual budgeting and logistics planning for businesses that depend on fuel costs, such as delivery services, ride-share companies, retailers estimating shipping costs, and more. Even a 1-2 month forecast can help individuals or companies budget more accurately. Research has shown that gasoline prices respond asymmetrically to crude oil price movements, rising sharply when oil prices increase, but falling more slowly when oil prices decrease (Wen et al., 2025). Additional research attributes this asymmetric pricing pattern to the extra time and effort required for consumers to compare prices across gas stations, rather than to coordinated pricing among retailers (Cha & Lee, 2023).
The analysis found that the dynamic regression model outperformed both machine learning approaches on every accuracy metric tested, achieving the lowest RMSE (0.203), MAE (0.156), and MAPE (4.28%), compared to XGBoost’s RMSE of 0.270 and NNETAR’s RMSE of 0.595.
Forecasting gasoline prices has become a more popular area of research, and studies have applied both classical time series methods and machine learning approaches to understand the relationship with crude oil. Research has used XGBoost to test whether gas prices and market uncertainty indices could be used to forecast crude oil prices, and have compared the results against Support Vector Machines and ARIMAX (Tissaoui et al., 2023). In this study, XGBoost performed the best and the feature importance analysis found that gas prices and uncertainty indices could both be used to predict future WTI crude oil prices. XGBoost has also been used to forecast silver prices (Gono, Napitupulu, & Firdaniza, 2023) and short term electric load demand (Zhao, Xiang, Huang, Wang, & Fang, 2022), and performed better than random forest and other machine learning models in these studies.
Additional studies have analyzed the relationship between crude oil and retail gasoline prices, and have found that the relationship is often asymmetric, where retail prices tend to increase quickly when crude oil costs increase but fall more slowly when crude oil costs decrease. Wen et al. (2025) found that including this asymmetry into a forecast model improves the accuracy of the forecast. Additionally, Cha & Lee (2023) found that this happens because consumers shop less when prices are volatile, so gas stations raise prices quickly but lower them slowly.
Traditional time series methods, including regression models with ARIMA errors and Fourier terms, are a common way to include external predictors in a forecast while accounting for autocorrelation and seasonality in the data (Hyndman & Athanasopoulos, 2021). He (2023) compared exponential smoothing and ARIMA against a time series regression model that included crude oil price and seasonality when forecasting gasoline prices, and found the regression model performed best, even when prices changed direction during the test period. This approach should handle this dataset well since it includes both an external economic driver and a seasonal component.
Researchers have also used neural network approaches to forecast commodity and crude oil prices. Ramyar & Kianfar (2019) found that an MLP neural network predicted crude oil prices more accurately than a VAR model, while Bouteska et al. (2023) built a neural network designed to capture nonlinear price patterns and found it outperformed standard neural network models when forecasting crude oil and natural gas prices. Other research has combined neural networks with traditional models instead of testing them against each other. Dar et al. (2022) combined ARIMA with a neural network to forecast price swings in Brent crude oil, an oil benchmark similar to WTI, and found the combined model worked better than either method alone.
This analysis uses U.S. Regular Gasoline Price (GASREGW) and WTI Crude Oil (DCOILWTICO) data from the Federal Reserve Economic Data (FRED) database, aggregated to monthly frequency from January 2005 through August 2026. A missing value check after joining the two series found none in either column, so no imputation was needed.
# Load the data
gas_path <- "~/Desktop/GASREGW.csv"
wti_path <- "~/Desktop/DCOILWTICO.csv"
gas <- read_csv(gas_path) %>%
rename(Date = observation_date, GasPrice = GASREGW) %>%
mutate(Month = yearmonth(Date)) %>%
group_by(Month) %>%
summarise(GasPrice = mean(GasPrice, na.rm = TRUE))
wti <- read_csv(wti_path) %>%
rename(Date = observation_date, WTI = DCOILWTICO) %>%
mutate(Month = yearmonth(Date)) %>%
group_by(Month) %>%
summarise(WTI = mean(WTI, na.rm = TRUE))
fuel <- gas %>%
left_join(wti, by = "Month") %>%
as_tsibble(index = Month) %>%
filter(Month >= yearmonth("2005 Jan"))
sum(is.na(fuel$GasPrice))
## [1] 0
sum(is.na(fuel$WTI))
## [1] 0
To determine the appropriate lag structure between WTI crude oil and gasoline prices, WTI was tested at lags 0 through 3 against gas price using Pearson correlation. Lag 0 produced the strongest correlation (r = 0.874), compared to lag 1 (r = 0.862), lag 2 (r = 0.797), and lag 3 (r = 0.711), so current-period WTI was used as the external regressor throughout the analysis.
# Check the WTI lag relationship before building any models
fuel %>% autoplot(GasPrice) +
labs(title = "U.S. Regular Gasoline Price From January 2005 to August 2026",
x = "Year", y = "U.S. Dollars per Gallon") +
scale_x_yearmonth(date_breaks = "2 years", date_labels = "%Y")
for (l in 0:3) {
cat("lag", l, ":", cor(fuel$GasPrice, lag(fuel$WTI, l), use = "complete.obs"), "\n")
}
## lag 0 : 0.8741041
## lag 1 : 0.8623086
## lag 2 : 0.7967721
## lag 3 : 0.7106834
# Lock in WTI lag
fuel <- fuel %>%
mutate(WTI_lag = lag(WTI, 0))
The dataset was split into an 80% training set and a 20% test set, rather than a random split.
# Train/test split, 80/20
n <- nrow(fuel)
train <- fuel %>% slice(1:floor(n * 0.8))
test <- fuel %>% slice((floor(n * 0.8) + 1):n)
The first model is a dynamic regression with ARIMA errors. Gasoline price is the response variable, WTI_lag is the external predictor, and Fourier terms capture seasonality. It’s written as ARIMA(GasPrice ~ WTI_lag + fourier(K) + PDQ(0,0,0)).
Fourier order was chosen by comparing AICc across K = 1, 2, and 3. K = 2 had the lowest AICc (-310.51), compared to K = 1 (-306.94) and K = 3 (-307.44). The selected model was a regression with ARIMA(0,1,3) errors, with a WTI_lag coefficient of 0.0197 (standard error 0.0013), showing a positive and statistically meaningful relationship between crude oil and gasoline prices.
# Compare Fourier order (K) by AICc
fit_k_compare <- train %>%
model(
K1 = ARIMA(GasPrice ~ WTI_lag + fourier(K = 1) + PDQ(0,0,0)),
K2 = ARIMA(GasPrice ~ WTI_lag + fourier(K = 2) + PDQ(0,0,0)),
K3 = ARIMA(GasPrice ~ WTI_lag + fourier(K = 3) + PDQ(0,0,0))
)
glance(fit_k_compare) %>% select(.model, AICc)
## # A tibble: 3 × 2
## .model AICc
## <chr> <dbl>
## 1 K1 -307.
## 2 K2 -311.
## 3 K3 -307.
# Fit dynamic regression with WTI and Fourier (K = 2)
fit_dynreg <- train %>%
model(DynReg = ARIMA(GasPrice ~ WTI_lag + fourier(K = 2) + PDQ(0,0,0)))
report(fit_dynreg)
## Series: GasPrice
## Model: LM w/ ARIMA(0,1,3) errors
##
## Coefficients:
## ma1 ma2 ma3 WTI_lag fourier(K = 2)C1_12
## -0.0036 -0.3058 -0.1683 0.0197 -0.1277
## s.e. 0.0761 0.0698 0.0684 0.0013 0.0202
## fourier(K = 2)S1_12 fourier(K = 2)C2_12 fourier(K = 2)S2_12
## 0.0333 -0.0409 0.0064
## s.e. 0.0197 0.0144 0.0144
##
## sigma^2 estimated as 0.01238: log likelihood=164.71
## AIC=-311.43 AICc=-310.51 BIC=-281.43
# Forecast the test period with the dynamic regression model
dynreg_fc <- fit_dynreg %>%
forecast(new_data = test)
dynreg_fc %>%
autoplot(fuel %>% filter(Month >= yearmonth("2015 Jan")),
level = NULL) +
labs(
title = paste0("U.S. Gasoline Price: Dynamic Regression Forecast vs Actuals ",
"From 2015 to 2026"),
x = "Year",
y = "U.S. Dollars per Gallon"
) +
scale_x_yearmonth(date_breaks = "2 years", date_labels = "%Y")
The second model is a gradient boosted tree model built using XGBoost. XGBoost isn’t part of the fable ecosystem, so features were engineered manually. These include a 1-month lag (lag1), a 12-month lag (lag12), a 3-month rolling average (roll3), the lagged WTI variable (WTI_lag), and sine and cosine Fourier terms (fourier_sin1, fourier_cos1) to represent seasonality.
The model was trained with a maximum tree depth of 4, a learning rate (eta) of 0.05, and 200 boosting rounds. Training and test data were split chronologically rather than randomly to avoid leaking future information into the training set.
# Build lag, rolling average, and seasonal features for XGBoost
library(slider)
fuel_feat <- fuel %>%
as_tibble() %>%
arrange(Month) %>%
mutate(
lag1 = lag(GasPrice, 1),
lag12 = lag(GasPrice, 12),
roll3 = slide_dbl(GasPrice, mean, .before = 2, .complete = TRUE),
month_num = month(Month),
fourier_sin1 = sin(2 * pi * month_num / 12),
fourier_cos1 = cos(2 * pi * month_num / 12)
) %>%
drop_na()
train_feat <- fuel_feat %>% filter(Month %in% train$Month)
test_feat <- fuel_feat %>% filter(Month %in% test$Month)
nrow(train_feat)
## [1] 196
nrow(test_feat)
## [1] 52
# Fit XGBoost on the engineered features
feature_cols <- c("lag1", "lag12", "roll3", "WTI_lag", "fourier_sin1", "fourier_cos1")
dtrain <- xgb.DMatrix(data = as.matrix(train_feat[, feature_cols]),
label = train_feat$GasPrice)
dtest <- xgb.DMatrix(data = as.matrix(test_feat[, feature_cols]),
label = test_feat$GasPrice)
params <- list(objective = "reg:squarederror", max_depth = 4, eta = 0.05)
xgb_fit <- xgb.train(
params = params,
data = dtrain,
nrounds = 200,
verbose = 0
)
xgb_pred <- predict(xgb_fit, dtest)
The third model is a feed-forward neural network, specifically a single hidden-layer multilayer perceptron (MLP). It’s implemented through the NNETAR function within the fable framework, using the lagged WTI variable as a predictor of gasoline price.
# Fit NNETAR (MLP) model
set.seed(123)
fit_nnetar <- train %>%
model(NNET = NNETAR(GasPrice ~ WTI_lag))
set.seed(123)
nnet_fc <- fit_nnetar %>%
forecast(new_data = test)
nnet_fc
## # A fable: 52 x 6 [1M]
## # Key: .model [1]
## .model Month GasPrice .mean WTI WTI_lag
## <chr> <mth> <dist> <dbl> <dbl> <dbl>
## 1 NNET 2022 May sample[5000] 3.85 110. 110.
## 2 NNET 2022 Jun sample[5000] 3.75 115. 115.
## 3 NNET 2022 Jul sample[5000] 3.66 102. 102.
## 4 NNET 2022 Aug sample[5000] 3.56 93.7 93.7
## 5 NNET 2022 Sep sample[5000] 3.38 84.3 84.3
## 6 NNET 2022 Oct sample[5000] 3.26 87.6 87.6
## 7 NNET 2022 Nov sample[5000] 3.15 84.4 84.4
## 8 NNET 2022 Dec sample[5000] 3.00 76.4 76.4
## 9 NNET 2023 Jan sample[5000] 2.89 78.1 78.1
## 10 NNET 2023 Feb sample[5000] 2.81 76.8 76.8
## # ℹ 42 more rows
XGBoost’s output is a plain numeric vector, not a fable object, so its forecasts couldn’t be combined directly with the other models. Instead, the dynamic regression, XGBoost, and NNETAR forecasts were pulled into a common tibble by month. The ensemble forecast is the simple average of the three.
# Build the comparison tibble and ensemble average
comparison <- test %>%
as_tibble() %>%
select(Month, GasPrice) %>%
mutate(DynReg = dynreg_fc$.mean,
XGB = xgb_pred,
NNET = nnet_fc$.mean,
Ensemble = (DynReg + XGB + NNET) / 3)
comparison
## # A tibble: 52 × 6
## Month GasPrice DynReg XGB NNET Ensemble
## <mth> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2022 May 4.44 4.20 4.01 3.85 4.02
## 2 2022 Jun 4.93 4.28 4.02 3.75 4.02
## 3 2022 Jul 4.56 4.01 3.98 3.66 3.89
## 4 2022 Aug 3.98 3.85 3.81 3.56 3.74
## 5 2022 Sep 3.70 3.65 3.58 3.38 3.54
## 6 2022 Oct 3.82 3.66 3.61 3.26 3.51
## 7 2022 Nov 3.68 3.51 3.50 3.15 3.39
## 8 2022 Dec 3.21 3.28 3.18 3.00 3.16
## 9 2023 Jan 3.34 3.30 3.34 2.89 3.18
## 10 2023 Feb 3.39 3.33 3.31 2.81 3.15
## # ℹ 42 more rows
# Compute accuracy metrics for all four models
acc <- function(actual, predicted) {
rmse <- sqrt(mean((actual - predicted)^2))
mae <- mean(abs(actual - predicted))
mape <- mean(abs((actual - predicted) / actual)) * 100
tibble(RMSE = rmse, MAE = mae, MAPE = mape)
}
accuracy_table <- bind_rows(
DynReg = acc(comparison$GasPrice, comparison$DynReg),
XGB = acc(comparison$GasPrice, comparison$XGB),
NNET = acc(comparison$GasPrice, comparison$NNET),
Ensemble = acc(comparison$GasPrice, comparison$Ensemble),
.id = "Model"
)
knitr::kable(accuracy_table, digits = 3,
caption = "Model Accuracy Comparison Across All Four Models")
| Model | RMSE | MAE | MAPE |
|---|---|---|---|
| DynReg | 0.203 | 0.156 | 4.275 |
| XGB | 0.270 | 0.199 | 5.321 |
| NNET | 0.595 | 0.544 | 15.208 |
| Ensemble | 0.316 | 0.252 | 6.767 |
The four models were evaluated on the test set using RMSE, MAE, and MAPE. The dynamic regression model performed best across all three metrics, with an RMSE of 0.203, MAE of 0.156, and MAPE of 4.28%. XGBoost was second best with an RMSE 0.270, MAE of 0.199, and MAPE 5.32%. The ensemble average placed third, with an RMSE 0.316, MAE of 0.252, and MAPE of 6.77%. NNETAR performed the worst, with an RMSE 0.595, MAE of 0.544, and MAPE of 15.2%.
The chart below compares all four models’ forecasts against actual gasoline prices from 2022 to 2026. The dynamic regression model tracks the actual prices the closest throughout the test period, including the sharp increase in early 2026. XGBoost and the ensemble follow a similar pattern, but less closely. NNETAR consistently forecasts gas prices too low, missing the 2026 spike entirely. This lines up with its higher error metrics above.
# Plot all four model forecasts against actual prices
comparison_long <- comparison %>%
pivot_longer(cols = c(GasPrice, DynReg, XGB, NNET, Ensemble),
names_to = "Series", values_to = "Price")
ggplot(comparison_long, aes(x = Month, y = Price, color = Series)) +
geom_line(linewidth = 1) +
scale_color_viridis_d() +
labs(title = "U.S. Gasoline Price: Model Forecasts vs Actuals From 2022 to 2026",
x = "Year",
y = "U.S. Dollars per Gallon",
color = "Series") +
scale_x_yearmonth(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(labels = scales::comma)
The chart below shows XGBoost’s feature importance. The 3-month rolling average (roll3) was the most influential feature, followed by the lagged WTI variable (WTI_lag). The 1-month and 12-month lags (lag1, lag12) mattered less, and the Fourier seasonal terms had almost no impact. This suggests that XGBoost relied mainly on recent price trends rather than on the relationship between WTI crude oil and gasoline prices.
# Residual diagnostics for the dynamic regression model
fit_dynreg %>% gg_tsresiduals()
# Plot XGBoost feature importance
importance <- xgb.importance(feature_names = feature_cols, model = xgb_fit)
ggplot(importance, aes(x = reorder(Feature, Gain), y = Gain)) +
geom_col(fill = "#440154") +
coord_flip() +
labs(title = "XGBoost Feature Importance for Gasoline Price Forecasting",
x = "Feature",
y = "Relative Importance (Gain)")
The dynamic regression model likely performed better than the machine learning approaches because it was built around a known relationship, where gas prices move with WTI crude oil, whereas XGBoost and NNETAR had to learn that relationship from the start. The ARIMA error terms also captured leftover autocorrelation, so when the model over or underestimated gas prices in one month, that error often carried into the next month or two rather than behaving like random noise. The Fourier terms accounted for seasonality and captured the annual pattern of gas prices increasing in the summer and decreasing in the winter. XGBoost and NNETAR had to identify any useful pattern in a relatively small monthly dataset without that built-in structure, which likely limited their ability to generalize to the test period.
The analysis has a few limitations that are worth noting. First, forecasting the test period relied on WTI’s historical values, instead of a real forecast of future oil prices. If this model were to be used in practice, it would need a separate WTI forecast, and its accuracy would depend on how good that forecast was. Second, the residual diagnostics also showed a small amount of leftover autocorrelation at lag 6 and lag 14 to 15, so the dynamic regression model did not completely capture every pattern in the data. Finally, XGBoost and NNETAR were also trained on a relatively small dataset of about 196 monthly observations, which may have limited how well those models could learn more complex patterns.
The dynamic regression model had the lowest error across every metric tested, so it would be the strongest choice for forecasting gasoline prices in this context. However, XGBoost and NNETAR also rely on WTI as a predictor, so in a real setting all three models would still need a reliable forecast of oil prices instead of just historical values. If anyone were to use this model to budget, it’s important to consider that real-world performance would likely be lower, since oil prices would also need to be forecast rather than included as known values like they were in this analysis.
This paper forecasted U.S. regular gasoline prices from January 2005 to August 2026 by comparing four approaches, which were a dynamic regression model with an external WTI regressor and Fourier seasonal terms, XGBoost, NNETAR, and an ensemble average. The dynamic regression model performed the best overall, producing the most accurate forecasts across every metric tested. This suggests that for this dataset, including a known economic relationship helped the dynamic regression model perform better than the other models that had to learn the patterns without this.
Future work could pair this model with an actual WTI forecast rather than historical values, to more realistically match real-world conditions. Extending the training window or adding other predictors, such as the CPI and GDP variables used in He (2023), could also help XGBoost and NNETAR learn more complex patterns with more data to work with.
Bouteska, A., Hajek, P., Fisher, B., & Abedin, M. Z. (2023). Nonlinearity in forecasting energy commodity prices: Evidence from a focused time-delayed neural network. Research in International Business and Finance, 64.
Cha, K., & Lee, C. Y. (2023). Rockets and feathers in the gasoline market: Evidence from South Korea. Sustainability, 15(4), 3815.
Dar, L. S., Aamir, M., Khan, Z., Bilal, M., Boonsatit, N., & Jirawattanapanit, A. (2022). Forecasting crude oil prices volatility by reconstructing EEMD components using ARIMA and FFNN models. Frontiers in Energy Research, 10, Article 991602.
Gono, D. N., Napitupulu, H., & Firdaniza. (2023). Silver price forecasting using extreme gradient boosting (XGBoost) method. Mathematics, 11(18), 3813.
He, X. J. (2023). Forecasting gasoline price with time series models. Communications of the IIMA, 21(1).
Hyndman, R. J., & Athanasopoulos, G. (2021). Forecasting: Principles and practice (3rd ed.). OTexts. https://otexts.com/fpp3/
Ramyar, S., & Kianfar, F. (2019). Forecasting crude oil prices: A comparison between artificial neural networks and vector autoregressive models. Computational Economics, 53(2), 743-761.
Tissaoui, K., Zaghdoudi, T., Hakimi, A., & Nsaibi, M. (2023). Do gas price and uncertainty indices forecast crude oil prices? Fresh evidence through XGBoost modeling. Computational Economics, 62(2), 663-687.
Wen, D., He, M., Wang, Y., & Zhang, Y. (2025). Forecasting gasoline prices using oil prices: New evidence based on the rocket and feather hypothesis. Energy, 335.
Zhao, Q., Xiang, W., Huang, B., Wang, J., & Fang, J. (2022). Optimised extreme gradient boosting model for short term electric load demand forecasting of regional grid system. Scientific Reports, 12, 19282.