分类变量

!pip install -Uqq nixtla datasetsforecast
from nixtla.utils import in_colab
IN_COLAB = in_colab()
if not IN_COLAB:
    from nixtla.utils import colab_badge
    from dotenv import load_dotenv

分类变量是可能影响预测的外部因素。这些变量取有限个固定值,并对观察值进行分组。

例如,如果你正在为一个零售商预测每日产品需求,你可以利用一个事件变量,它可以告诉你某天发生了什么类型的事件,例如’无’、‘体育’或’文化’。

要在TimeGPT中结合分类变量,你需要将时间序列数据中的每个点与相应的外部数据配对。

if not IN_COLAB:
    load_dotenv()    
    colab_badge('docs/tutorials/03_categorical_variables')

1. 导入包

首先,我们安装并导入所需的包,并初始化 Nixtla 客户端。

import pandas as pd
import os

from nixtla import NixtlaClient
from datasetsforecast.m5 import M5
nixtla_client = NixtlaClient(
    # defaults to os.environ.get("NIXTLA_API_KEY")
    api_key = 'my_api_key_provided_by_nixtla'   
)

👍 使用 Azure AI 终端

要使用 Azure AI 终端,请记得同时设置 base_url 参数:

nixtla_client = NixtlaClient(base_url="你的 Azure AI 终端", api_key="你的 api_key")

if not IN_COLAB:
    nixtla_client = NixtlaClient()

2. 加载M5数据

让我们来看一个关于预测M5数据集产品销售的示例。M5数据集包含美国10家零售店的每日产品需求(销售)数据。

首先,我们使用datasetsforecast加载数据。这将返回:

  • Y_df,包含每个独特产品(unique_id列)在每个时间戳(ds列)的销售数据(y列)。
  • X_df,包含每个独特产品(unique_id列)在每个时间戳(ds列)的额外相关信息。
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_id ds y
0 FOODS_1_001_CA_1 2011-01-29 3.0
1 FOODS_1_001_CA_1 2011-01-30 0.0
2 FOODS_1_001_CA_1 2011-01-31 0.0
3 FOODS_1_001_CA_1 2011-02-01 1.0
4 FOODS_1_001_CA_1 2011-02-02 4.0
5 FOODS_1_001_CA_1 2011-02-03 2.0
6 FOODS_1_001_CA_1 2011-02-04 0.0
7 FOODS_1_001_CA_1 2011-02-05 2.0
8 FOODS_1_001_CA_1 2011-02-06 0.0
9 FOODS_1_001_CA_1 2011-02-07 0.0

在这个例子中,我们将只保留来自列 event_type_1 的额外相关信息。该列是一个_类别变量_,指示某一日期是否发生可能影响产品销售的重要事件。

X_df = X_df[['unique_id', 'ds', 'event_type_1']]

X_df.head(10)
unique_id ds event_type_1
0 FOODS_1_001_CA_1 2011-01-29 nan
1 FOODS_1_001_CA_1 2011-01-30 nan
2 FOODS_1_001_CA_1 2011-01-31 nan
3 FOODS_1_001_CA_1 2011-02-01 nan
4 FOODS_1_001_CA_1 2011-02-02 nan
5 FOODS_1_001_CA_1 2011-02-03 nan
6 FOODS_1_001_CA_1 2011-02-04 nan
7 FOODS_1_001_CA_1 2011-02-05 nan
8 FOODS_1_001_CA_1 2011-02-06 Sporting
9 FOODS_1_001_CA_1 2011-02-07 nan

正如您所见,2011年2月6日将举行一场体育赛事。

3. 使用分类变量预测产品需求

我们将仅预测单一产品的需求。我们选择了一种高销量的食品产品,标识为 FOODS_3_090_CA_3

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

我们合并了两个数据框以创建将在 TimeGPT 中使用的数据集。

df = Y_df_product.merge(X_df_product)

df.head(10)
unique_id ds y event_type_1
0 FOODS_3_090_CA_3 2011-01-29 108.0 nan
1 FOODS_3_090_CA_3 2011-01-30 132.0 nan
2 FOODS_3_090_CA_3 2011-01-31 102.0 nan
3 FOODS_3_090_CA_3 2011-02-01 120.0 nan
4 FOODS_3_090_CA_3 2011-02-02 106.0 nan
5 FOODS_3_090_CA_3 2011-02-03 123.0 nan
6 FOODS_3_090_CA_3 2011-02-04 279.0 nan
7 FOODS_3_090_CA_3 2011-02-05 175.0 nan
8 FOODS_3_090_CA_3 2011-02-06 186.0 Sporting
9 FOODS_3_090_CA_3 2011-02-07 120.0 nan
if not IN_COLAB:
    df = pd.read_parquet("../../assets/M5_categorical_variables_example.parquet")

为了在 TimeGPT 中使用 分类变量,有必要对这些变量进行数值编码。本教程中我们将使用 独热编码

我们可以通过使用 pandas 内置的 get_dummies 功能对 event_type_1 列进行独热编码。对 event_type_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_id ds y Cultural National Religious Sporting nan
1959 FOODS_3_090_CA_3 2016-06-10 140.0 0 0 0 0 1
1960 FOODS_3_090_CA_3 2016-06-11 151.0 0 0 0 0 1
1961 FOODS_3_090_CA_3 2016-06-12 87.0 0 0 0 0 1
1962 FOODS_3_090_CA_3 2016-06-13 67.0 0 0 0 0 1
1963 FOODS_3_090_CA_3 2016-06-14 50.0 0 0 0 0 1
1964 FOODS_3_090_CA_3 2016-06-15 58.0 0 0 0 0 1
1965 FOODS_3_090_CA_3 2016-06-16 116.0 0 0 0 0 1
1966 FOODS_3_090_CA_3 2016-06-17 124.0 0 0 0 0 1
1967 FOODS_3_090_CA_3 2016-06-18 167.0 0 0 0 0 1
1968 FOODS_3_090_CA_3 2016-06-19 118.0 0 0 0 1 0

正如您所见,我们现在添加了5列,每列都有一个二元指示符(10),指示在特定日期是否有文化国家宗教体育事件或没有事件(nan)。例如,在2016年6月19日,有一个体育事件。

让我们转到我们的预测任务。我们将预测2016年2月的前7天。这包括2016年2月7日——举办超级碗50的日期。这类大型国家事件通常会影响零售产品的销售。

为了在TimeGPT中使用编码的分类变量,我们必须将它们作为未来值添加。因此,我们创建一个未来值数据框,包含unique_id、时间戳ds和编码的分类变量。

当然,我们会去掉目标列,因为这通常是不可用的——这就是我们要预测的数量!

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

future_ex_vars_df.head(10)
unique_id ds Cultural National Religious Sporting nan
1829 FOODS_3_090_CA_3 2016-02-01 0 0 0 0 1
1830 FOODS_3_090_CA_3 2016-02-02 0 0 0 0 1
1831 FOODS_3_090_CA_3 2016-02-03 0 0 0 0 1
1832 FOODS_3_090_CA_3 2016-02-04 0 0 0 0 1
1833 FOODS_3_090_CA_3 2016-02-05 0 0 0 0 1
1834 FOODS_3_090_CA_3 2016-02-06 0 0 0 0 1
1835 FOODS_3_090_CA_3 2016-02-07 0 0 0 1 0

接下来,我们将输入数据框限制为除7天预测日之外的所有数据:

df_train = df.query("ds < '2016-02-01'")

df_train.tail(10)
unique_id ds y Cultural National Religious Sporting nan
1819 FOODS_3_090_CA_3 2016-01-22 94.0 0 0 0 0 1
1820 FOODS_3_090_CA_3 2016-01-23 144.0 0 0 0 0 1
1821 FOODS_3_090_CA_3 2016-01-24 146.0 0 0 0 0 1
1822 FOODS_3_090_CA_3 2016-01-25 87.0 0 0 0 0 1
1823 FOODS_3_090_CA_3 2016-01-26 73.0 0 0 0 0 1
1824 FOODS_3_090_CA_3 2016-01-27 62.0 0 0 0 0 1
1825 FOODS_3_090_CA_3 2016-01-28 64.0 0 0 0 0 1
1826 FOODS_3_090_CA_3 2016-01-29 102.0 0 0 0 0 1
1827 FOODS_3_090_CA_3 2016-01-30 113.0 0 0 0 0 1
1828 FOODS_3_090_CA_3 2016-01-31 98.0 0 0 0 0 1

让我们调用 forecast 方法,首先不使用分类变量。

timegpt_fcst_without_cat_vars_df = nixtla_client.forecast(df=df_train, h=7, level=[80, 90])
timegpt_fcst_without_cat_vars_df.head()
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Inferred freq: D
INFO:nixtla.nixtla_client:Restricting input...
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
unique_id ds TimeGPT TimeGPT-lo-90 TimeGPT-lo-80 TimeGPT-hi-80 TimeGPT-hi-90
0 FOODS_3_090_CA_3 2016-02-01 73.304092 53.449049 54.795078 91.813107 93.159136
1 FOODS_3_090_CA_3 2016-02-02 66.335518 47.510669 50.274136 82.396899 85.160367
2 FOODS_3_090_CA_3 2016-02-03 65.881630 36.218617 41.388896 90.374364 95.544643
3 FOODS_3_090_CA_3 2016-02-04 72.371864 -26.683115 25.097362 119.646367 171.426844
4 FOODS_3_090_CA_3 2016-02-05 95.141045 -2.084882 34.027078 156.255011 192.366971

📘 Azure AI中的可用模型

如果您正在使用Azure AI端点,请确保设置 model="azureai"

nixtla_client.forecast(..., model="azureai")

对于公共API,我们支持两种模型:timegpt-1timegpt-1-long-horizon

默认使用 timegpt-1。有关如何以及何时使用 timegpt-1-long-horizon 的信息,请参见 本教程

我们绘制了预测及预测期之前的最后28天的数据:

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

TimeGPT已经提供了一个合理的预测,但似乎对2016年2月6日的峰值预测稍显不足——那是超级碗的前一天。

现在让我们再次调用 forecast 方法,这次 带上 分类变量。

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()
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Inferred freq: D
INFO:nixtla.nixtla_client:Using the following exogenous variables: Cultural, National, Religious, Sporting, nan
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
unique_id ds TimeGPT TimeGPT-lo-90 TimeGPT-lo-80 TimeGPT-hi-80 TimeGPT-hi-90
0 FOODS_3_090_CA_3 2016-02-01 70.661271 -0.204378 14.593348 126.729194 141.526919
1 FOODS_3_090_CA_3 2016-02-02 65.566941 -20.394326 11.654239 119.479643 151.528208
2 FOODS_3_090_CA_3 2016-02-03 68.510010 -33.713710 6.732952 130.287069 170.733731
3 FOODS_3_090_CA_3 2016-02-04 75.417710 -40.974649 4.751767 146.083653 191.810069
4 FOODS_3_090_CA_3 2016-02-05 97.340302 -57.385361 18.253812 176.426792 252.065965

📘 Azure AI 中可用的模型

如果您正在使用 Azure AI 端点,请确保设置 model="azureai":

nixtla_client.forecast(..., model="azureai")

对于公共 API,我们支持两个模型:timegpt-1timegpt-1-long-horizon

默认情况下使用 timegpt-1。请参阅 本教程 了解何时以及如何使用 timegpt-1-long-horizon

我们绘制了预测和预测期之前的最后28天的数据:

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

我们可以直观地验证预测值更接近实际观察值,这正是我们在预测中包含分类变量的结果。

让我们通过计算我们创建的预测的平均绝对误差来验证这个结论。

from utilsforecast.losses import mae
# 创建目标数据框
df_target = df[['unique_id', 'ds', 'y']].query("ds >= '2016-02-01' & ds <= '2016-02-07'")

# 重命名预测列
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'})

# 将预测结果与目标数据框合并
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']])

# 计算错误
mean_absolute_errors = mae(df_target, ['TimeGPT-without-cat-vars', 'TimeGPT-with-cat-vars'])
mean_absolute_errors
unique_id TimeGPT-without-cat-vars TimeGPT-with-cat-vars
0 FOODS_3_090_CA_3 24.285649 20.028514

实际上,我们发现使用TimeGPT时,包含分类变量的错误率比不包含分类变量时低约20%,这表明我们在包含分类变量时表现更好。

Give us a ⭐ on Github