Explaining Forecasting Models with SHAP#

Why did the model predict a spike tomorrow morning? Which lagged values mattered most? How do covariates shape the forecast?

Darts’ ShapExplainer answers these questions using SHAP (SHapley Additive exPlanations) [1], a game-theoretic framework that attributes each input feature’s contribution to a prediction. It works with any of Darts’ scikit-learn-like [2] and PyTorch [3] models through a single, unified API.

What you will learn:

We use the ElectricityConsumptionZurichDataset throughout, keeping the data small for fast computation.

  • [1] Lundberg & Lee, A Unified Approach to Interpreting Model Predictions, NeurIPS 2017.

  • [2] Models such as SKLearnModel, LinearRegressionModel, LightGBMModel, …

  • [3] Regular neural network models such as TiDEModel and foundation models such as Chronos2Model, …

1. Setup and Data Preparation#

[1]:
%load_ext autoreload
%autoreload 2
%matplotlib inline
[2]:
import logging
import warnings

import numpy as np
import plotly
import shap

from darts import concatenate, set_option
from darts.datasets import ElectricityConsumptionZurichDataset
from darts.explainability import ShapExplainer
from darts.metrics import mae, mic, miw
from darts.models import DLinearModel, LinearRegressionModel
from darts.utils.likelihood_models import QuantileRegression
from darts.utils.timeseries_generation import datetime_attribute_timeseries

warnings.filterwarnings("ignore")
logging.disable(logging.CRITICAL)
set_option("plotting.use_darts_style", True)

plotly.offline.init_notebook_mode()
shap.initjs()

We load hourly electricity consumption for Zurich households & SMEs, using the last four weeks (three for training, one for testing). Also, let’s use calendar features “hour” and “dayofweek” as future covariates.

[3]:
data = ElectricityConsumptionZurichDataset().load().astype("float32")
data = data.resample("h", method="sum")[-28 * 24 - 1 : -1]

series = data["Value_NE5"].with_columns_renamed("Value_NE5", "consumption")
future_covariates = (
    datetime_attribute_timeseries(
        time_index=series,
        attribute="hour",
        add_length=24,
    )
    .add_datetime_attribute("dayofweek")
    .astype("float32")
)

train, test = series[: -24 * 7], series[-24 * 7 :]
[4]:
fig = train.plotly(label="train")
test.plotly(label="test", fig=fig)
fig.update_layout(yaxis_title="Consumption (MWh)", autosize=True)