What Are Categorical Variables?

Categorical variables are external factors that take on a limited range of discrete values, grouping observations by categories. For example, “Sporting” or “Cultural” events in a dataset describing product demand.

By capturing unique external conditions, categorical variables enhance the predictive power of your model and can reduce forecasting error. They are easy to incorporate by merging each time series data point with its corresponding categorical data.

This tutorial demonstrates how to incorporate categorical (discrete) variables into TimeGPT forecasts.

How to Use Categorical Variables in TimeGPT

Step 1: Import Packages and Initialize the Nixtla Client

Make sure you have the necessary libraries installed: pandas, nixtla, and datasetsforecast.

import pandas as pd
import os

from nixtla import NixtlaClient
from datasetsforecast.m5 import M5

# Initialize the Nixtla Client
nixtla_client = NixtlaClient(
    api_key='my_api_key_provided_by_nixtla'
)

Step 2: Load M5 Data

We use the M5 dataset — a collection of daily product sales demands across 10 US stores — to showcase how categorical variables can improve forecasts.

Start by loading the M5 dataset and converting the date columns to datetime objects.

Y_df, X_df, _ = M5.load(directory=os.getcwd())

Y_df['ds'] = pd.to_datetime(Y_df['ds'])
X_df['ds'] = pd.to_datetime(X_df['ds'])

Y_df.head(10)
unique_iddsy
FOODS_1_001_CA_12011-01-293.0
FOODS_1_001_CA_12011-01-300.0
FOODS_1_001_CA_12011-01-310.0
FOODS_1_001_CA_12011-02-011.0
FOODS_1_001_CA_12011-02-024.0
FOODS_1_001_CA_12011-02-032.0
FOODS_1_001_CA_12011-02-040.0
FOODS_1_001_CA_12011-02-052.0
FOODS_1_001_CA_12011-02-060.0
FOODS_1_001_CA_12011-02-070.0

Extract the categorical columns from the X_df dataframe.

X_df = X_df[['unique_id', 'ds', 'event_type_1']]
X_df.head(10)
unique_iddsevent_type_1
FOODS_1_001_CA_12011-01-29nan
FOODS_1_001_CA_12011-01-30nan
FOODS_1_001_CA_12011-01-31nan
FOODS_1_001_CA_12011-02-01nan
FOODS_1_001_CA_12011-02-02nan
FOODS_1_001_CA_12011-02-03nan
FOODS_1_001_CA_12011-02-04nan
FOODS_1_001_CA_12011-02-05nan
FOODS_1_001_CA_12011-02-06Sporting
FOODS_1_001_CA_12011-02-07nan

Notice that there is a Sporting event on February 6, 2011, listed under event_type_1.

Step 3: Prepare Data for Forecasting

We’ll select a specific product to demonstrate how to incorporate categorical features into TimeGPT forecasts.

Select a High-Selling Product and Merge Data

Start by selecting a high-selling product and merging the data.

product = 'FOODS_3_090_CA_3'

Y_df_product = Y_df.query('unique_id == @product')
X_df_product = X_df.query('unique_id == @product')

df = Y_df_product.merge(X_df_product)
df.head(10)
unique_iddsyevent_type_1
FOODS_3_090_CA_32011-01-29108.0nan
FOODS_3_090_CA_32011-01-30132.0nan
FOODS_3_090_CA_32011-01-31102.0nan
FOODS_3_090_CA_32011-02-01120.0nan
FOODS_3_090_CA_32011-02-02106.0nan
FOODS_3_090_CA_32011-02-03123.0nan
FOODS_3_090_CA_32011-02-04279.0nan
FOODS_3_090_CA_32011-02-05175.0nan
FOODS_3_090_CA_32011-02-06186.0Sporting
FOODS_3_090_CA_32011-02-07120.0nan

One-Hot Encode Categorical Events

Encode categorical variables using one-hot encoding. One-hot encoding transforms each category into a separate column containing binary indicators (0 or 1).

event_type_1_ohe = pd.get_dummies(df['event_type_1'], dtype=int)

df = pd.concat([df, event_type_1_ohe], axis=1)
df = df.drop(columns=['event_type_1'])

df.tail(10)
unique_iddsyCulturalNationalReligiousSportingnan
FOODS_3_090_CA_32016-06-10140.000001
FOODS_3_090_CA_32016-06-11151.000001
FOODS_3_090_CA_32016-06-1287.000001
FOODS_3_090_CA_32016-06-1367.000001
FOODS_3_090_CA_32016-06-1450.000001
FOODS_3_090_CA_32016-06-1558.000001
FOODS_3_090_CA_32016-06-16116.000001
FOODS_3_090_CA_32016-06-17124.000001
FOODS_3_090_CA_32016-06-18167.000001
FOODS_3_090_CA_32016-06-19118.000010

Prepare Future External Variables

Select future external variables for Feb 1-7, 2016.

future_ex_vars_df = df.drop(columns=['y']).query("ds >= '2016-02-01' & ds <= '2016-02-07'")

Separate training data before Feb 1, 2016.

df_train = df.query("ds < '2016-02-01'")
df_train.tail(10)
unique_iddsyCulturalNationalReligiousSportingnan
FOODS_3_090_CA_32016-01-2294.000001
FOODS_3_090_CA_32016-01-23144.000001
FOODS_3_090_CA_32016-01-24146.000001
FOODS_3_090_CA_32016-01-2587.000001
FOODS_3_090_CA_32016-01-2673.000001
FOODS_3_090_CA_32016-01-2762.000001
FOODS_3_090_CA_32016-01-2864.000001
FOODS_3_090_CA_32016-01-29102.000001
FOODS_3_090_CA_32016-01-30113.000001
FOODS_3_090_CA_32016-01-3198.000001

Step 4: Forecast Product Demand

To evaluate the impact of categorical variables, we’ll forecast product demand with and without them.

Forecast Without Categorical Variables

timegpt_fcst_without_cat_vars_df = nixtla_client.forecast(
    df=df_train,
    h=7,
    level=[80, 90]
)

timegpt_fcst_without_cat_vars_df.head()
unique_iddsTimeGPTTimeGPT-lo-90TimeGPT-lo-80TimeGPT-hi-80TimeGPT-hi-90
FOODS_3_090_CA_32016-02-0173.30409253.44904954.79507891.81310793.159136
FOODS_3_090_CA_32016-02-0266.33551847.51066950.27413682.39689985.160367
FOODS_3_090_CA_32016-02-0365.88163036.21861741.38889690.37436495.544643
FOODS_3_090_CA_32016-02-0472.371864-26.68311525.097362119.646367171.426844
FOODS_3_090_CA_32016-02-0595.141045-2.08488234.027078156.255011192.366971

Visualize the forecast without categorical variables.

nixtla_client.plot(
    df[['unique_id', 'ds', 'y']].query("ds <= '2016-02-07'"),
    timegpt_fcst_without_cat_vars_df,
    max_insample_length=28,
)

Forecast with categorical variables

TimeGPT already provides a reasonable forecast, but it seems to somewhat underforecast the peak on the 6th of February 2016 - the day before the Super Bowl.

Forecast With Categorical Variables

timegpt_fcst_with_cat_vars_df = nixtla_client.forecast(
    df=df_train,
    X_df=future_ex_vars_df,
    h=7,
    level=[80, 90]
)

timegpt_fcst_with_cat_vars_df.head()
unique_iddsTimeGPTTimeGPT-lo-90TimeGPT-lo-80TimeGPT-hi-80TimeGPT-hi-90
FOODS_3_090_CA_32016-02-0170.661271-0.20437814.593348126.729194141.526919
FOODS_3_090_CA_32016-02-0265.566941-20.39432611.654239119.479643151.528208
FOODS_3_090_CA_32016-02-0368.510010-33.7137106.732952130.287069170.733731
FOODS_3_090_CA_32016-02-0475.417710-40.9746494.751767146.083653191.810069
FOODS_3_090_CA_32016-02-0597.340302-57.38536118.253812176.426792252.065965

Visualize the forecast with categorical variables.

# Visualize the forecast with categorical variables
nixtla_client.plot(
    df[['unique_id', 'ds', 'y']].query("ds <= '2016-02-07'"),
    timegpt_fcst_with_cat_vars_df,
    max_insample_length=28,
)

Forecast with categorical variables

5. Evaluate Forecast Accuracy

Finally, we calculate the Mean Absolute Error (MAE) for the forecasts with and without categorical variables.

# Create target dataframe
df_target = df[['unique_id', 'ds', 'y']].query("ds >= '2016-02-01' & ds <= '2016-02-07'")

# Rename forecast columns
timegpt_fcst_without_cat_vars_df = timegpt_fcst_without_cat_vars_df.rename(columns={'TimeGPT': 'TimeGPT-without-cat-vars'})
timegpt_fcst_with_cat_vars_df = timegpt_fcst_with_cat_vars_df.rename(columns={'TimeGPT': 'TimeGPT-with-cat-vars'})

# Merge forecasts with target dataframe
df_target = df_target.merge(timegpt_fcst_without_cat_vars_df[['unique_id', 'ds', 'TimeGPT-without-cat-vars']])
df_target = df_target.merge(timegpt_fcst_with_cat_vars_df[['unique_id', 'ds', 'TimeGPT-with-cat-vars']])

# Compute errors
mean_absolute_errors = mae(df_target, ['TimeGPT-without-cat-vars', 'TimeGPT-with-cat-vars'])
unique_idTimeGPT-without-cat-varsTimeGPT-with-cat-vars
FOODS_3_090_CA_324.28564920.028514

Including categorical variables noticeably improves forecast accuracy, reducing MAE by about 20%.

Conclusion

Categorical variables are powerful additions to TimeGPT forecasts, helping capture valuable external factors. By properly encoding these variables and merging them with your time series, you can significantly enhance predictive performance.

Continue exploring more advanced techniques or different datasets to further improve your TimeGPT forecasting models.