不规则时间戳

!pip install -Uqq nixtla
from nixtla.utils import in_colab
IN_COLAB = in_colab()
if not IN_COLAB:
    from nixtla.utils import colab_badge
    from fastcore.test import test_eq, test_fail, test_warns
    from dotenv import load_dotenv 

在处理时间序列数据时,时间戳的频率是一个关键因素,可能会显著影响预测结果。像每日、每周或每月这样的规则频率处理起来很简单。然而,不规则频率,如排除周末的工作日,对时间序列预测方法来说可能具有挑战性。

我们的预测方法能够处理这种不规则时间序列数据,只要您指定序列的频率。例如,在工作日的情况下,频率应该传递为 ‘B’。如果没有这样做,方法可能无法自动检测频率,特别是在时间戳不规则的情况下。

if not IN_COLAB:
    load_dotenv()    
    colab_badge('docs/tutorials/12_irregular_timestamps')

1. 导入包

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

import pandas as pd
from nixtla import NixtlaClient
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. 加载数据

第一步是获取你的时间序列数据。数据必须包含时间戳和相关值。例如,你可能在处理股票价格,你的数据可能如下所示。在此示例中,我们使用OpenBB

# 测试自定义频率
if not IN_COLAB:
    df_fed_test = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/openbb/fed.csv')
    pd.testing.assert_frame_equal(
        nixtla_client.forecast(df_fed_test, h=12, target_col='FF', level=[90]),
        nixtla_client.forecast(df_fed_test, h=12, target_col='FF', freq='W', level=[90])
    )
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Inferred freq: W-WED
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Restricting input...
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Inferred freq: W-WED
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Restricting input...
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
pltr_df = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/openbb/pltr.csv')
pltr_df['date'] = pd.to_datetime(pltr_df['date'])
pltr_df.head()
date Open High Low Close Adj Close Volume Dividends Stock Splits
0 2020-09-30 10.00 11.41 9.11 9.50 9.50 338584400 0.0 0.0
1 2020-10-01 9.69 10.10 9.23 9.46 9.46 124297600 0.0 0.0
2 2020-10-02 9.06 9.28 8.94 9.20 9.20 55018300 0.0 0.0
3 2020-10-05 9.43 9.49 8.92 9.03 9.03 36316900 0.0 0.0
4 2020-10-06 9.04 10.18 8.90 9.90 9.90 90864000 0.0 0.0

让我们来看看这个数据集具有不规则的时间戳。pandas的DatetimeIndex中的dayofweek属性返回一周中的天,星期一=0,…,星期天=6。因此,检查dayofweek > 4实际上是在检查日期是否落在星期六(5)或星期天(6),这通常是非工作日(周末)。

(pltr_df['date'].dt.dayofweek > 4).sum()
0

我们可以看到时间戳不规则。让我们检查一下 Close 序列。

nixtla_client.plot(pltr_df, time_col='date', target_col='Close')

3. 使用不规则时间戳进行预测

要预测这些数据,您可以使用我们的 forecast 方法。重要的是,请记得使用 freq 参数指定数据的频率。在这种情况下,它将是 ‘B’,代表工作日。我们还需要定义 time_col 来选择序列的索引(默认是 ds),以及 target_col 来预测我们的目标变量,在本例中我们将预测 Close

# fails to infer "B" frequency
if not IN_COLAB:
    test_fail(
        lambda: nixtla_client.forecast(
            df=pltr_df, h=14,
            time_col='date', target_col='Close',
        ),
        contains='frequency'
    )
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
fcst_pltr_df = nixtla_client.forecast(
    df=pltr_df, h=14, freq='B',
    time_col='date', target_col='Close',
)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...

📘 Azure AI 中的可用模型

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

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

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

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

fcst_pltr_df.head()
date TimeGPT
0 2023-09-25 14.688427
1 2023-09-26 14.742798
2 2023-09-27 14.781240
3 2023-09-28 14.824156
4 2023-09-29 14.795214

请记住,对于工作日,频率为’B’。对于其他频率,您可以参考pandas偏移别名文档

通过指定频率,您可以帮助预测方法更好地理解数据中的模式,从而获得更准确和可靠的预测。

让我们绘制由 TimeGPT 生成的预测结果。

nixtla_client.plot(
    pltr_df, 
    fcst_pltr_df, 
    time_col='date',
    target_col='Close',
    max_insample_length=90, 
)

您还可以使用 level 参数将不确定性量化添加到您的预测中:

fcst_pltr_levels_df = nixtla_client.forecast(
    df=pltr_df, h=42, freq='B',
    time_col='date', target_col='Close',
    add_history=True,
    level=[80, 90],
)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
INFO:nixtla.nixtla_client:Calling Historical Forecast Endpoint...
nixtla_client.plot(
    pltr_df, 
    fcst_pltr_levels_df, 
    time_col='date',
    target_col='Close',
    level=[80, 90],
)

如果你想预测另一个变量,只需更改 target_col 参数。现在让我们预测 Volume

fcst_pltr_df = nixtla_client.forecast(
    df=pltr_df, h=14, freq='B',
    time_col='date', target_col='Volume',
)
nixtla_client.plot(
    pltr_df, 
    fcst_pltr_df, 
    time_col='date',
    max_insample_length=90,
    target_col='Volume',
)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...

但是如果我们想一次性预测所有时间序列呢?我们可以通过重塑我们的数据框来实现。当前,数据框是宽格式(每个序列是一个列),但是我们需要将其转换为长格式(彼此叠加)。我们可以通过以下方式来实现:

pltr_long_df = pd.melt(
    pltr_df, 
    id_vars=['date'],
    var_name='series_id'
)
pltr_long_df.head()
date series_id value
0 2020-09-30 Open 10.00
1 2020-10-01 Open 9.69
2 2020-10-02 Open 9.06
3 2020-10-05 Open 9.43
4 2020-10-06 Open 9.04

然后我们只需简单调用 forecast 方法,并指定 id_col 参数。

fcst_pltr_long_df = nixtla_client.forecast(
    df=pltr_long_df, h=14, freq='B',
    id_col='series_id', time_col='date', target_col='value',
)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
fcst_pltr_long_df.head()
series_id date TimeGPT
0 Adj Close 2023-09-25 14.688427
1 Adj Close 2023-09-26 14.742798
2 Adj Close 2023-09-27 14.781240
3 Adj Close 2023-09-28 14.824156
4 Adj Close 2023-09-29 14.795214

然后我们可以预测 Open 系列:

nixtla_client.plot(
    pltr_long_df, 
    fcst_pltr_long_df, 
    id_col='series_id',
    time_col='date',
    target_col='value',
    unique_ids=['Open'],
    max_insample_length=90,
)

添加额外信息

在时间序列预测中,我们所预测的变量通常不仅受到其过去值的影响,还受到其他因素或变量的影响。这些外部变量被称为外生变量,它们可以提供重要的额外背景信息,从而显著提高我们预测的准确性。本教程的一个重点因素是公司的收入。收入数据可以提供公司财务健康和增长潜力的关键指标,这两者都可能严重影响其股价。这些数据我们可以从 OpenBB 获得。

revenue_pltr = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/openbb/revenue-pltr.csv')
value = revenue_pltr['totalRevenue'].iloc[0]
if not isinstance(value, float) and 'M' in value:
    def convert_to_float(val):
        if 'M' in val:
            return float(val.replace(' M', '')) * 1e6
        elif 'K' in val:
            return float(val.replace(' K', '')) * 1e3
        else:
            return float(val)
    revenue_pltr['totalRevenue'] = revenue_pltr['totalRevenue'].apply(convert_to_float)
revenue_pltr.tail()
fiscalDateEnding totalRevenue
5 2022-06-30 473010000.0
6 2022-09-30 477880000.0
7 2022-12-31 508624000.0
8 2023-03-31 525186000.0
9 2023-06-30 533317000.0

在我们的数据集中,首先我们观察到的信息是,数据仅可用到2023年第一季度末。我们的数据以季度频率表示,我们的目标是利用这些信息来预测该日期之后14天的每日股票价格。

然而,为了准确地计算包含收入作为外生变量的这种预测,我们需要了解未来收入的值。这一点至关重要,因为未来的收入值可能会显著影响股票价格。

由于我们旨在预测14个每日股票价格,因此我们只需预测即将到来的季度的收入。这种方法使我们能够创建一个连贯的预测流程,其中一个预测的输出(收入)用作另一个预测(股票价格)的输入,从而利用所有可用的信息,以实现尽可能准确的预测。

fcst_pltr_revenue = nixtla_client.forecast(revenue_pltr, h=1, time_col='fiscalDateEnding', target_col='totalRevenue')
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Inferred freq: Q-DEC
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
fcst_pltr_revenue.head()
fiscalDateEnding TimeGPT
0 2023-09-30 547264448

接着我们上次的讨论,预测流程中的下一个关键步骤是调整我们的数据频率,以匹配股票价格的频率,股票价格是以工作日为单位表示的。为此,我们需要重新抽样历史和未来预测的收入数据。

我们可以使用以下代码来实现这一点。

revenue_pltr['fiscalDateEnding'] = pd.to_datetime(revenue_pltr['fiscalDateEnding'])
revenue_pltr = revenue_pltr.set_index('fiscalDateEnding').resample('B').ffill().reset_index()

重要提示:在这个过程中,我们将同一季度内所有天数分配相同的收入值是至关重要的。这种简化是必要的,因为季度收入数据与每日股价数据之间存在粒度差异。然而,在实际应用中,必须谨慎对待这一假设。季度收入数字对每日股价的影响可能会因多种因素而在季度内大幅波动,包括市场预期变化、其他财务新闻和事件。在本教程中,我们使用这一假设来说明如何将外生变量纳入我们的预测模型,但在现实场景中,可能需要更细致的处理,具体取决于可用数据和特定的应用案例。

然后我们可以创建完整的历史数据集。

pltr_revenue_df = pltr_df.merge(revenue_pltr.rename(columns={'fiscalDateEnding': 'date'}))
pltr_revenue_df.head()
date Open High Low Close Adj Close Volume Dividends Stock Splits totalRevenue
0 2021-03-31 22.500000 23.850000 22.379999 23.290001 23.290001 61458500 0.0 0.0 341234000.0
1 2021-04-01 23.950001 23.950001 22.730000 23.070000 23.070000 51788800 0.0 0.0 341234000.0
2 2021-04-05 23.780001 24.450001 23.340000 23.440001 23.440001 65374300 0.0 0.0 341234000.0
3 2021-04-06 23.549999 23.610001 22.830000 23.270000 23.270000 41933500 0.0 0.0 341234000.0
4 2021-04-07 23.000000 23.549999 22.809999 22.900000 22.900000 32766200 0.0 0.0 341234000.0

要计算未来收入的数据框:

horizon = 14
import numpy as np
future_df = pd.DataFrame({
    'date': pd.date_range(pltr_revenue_df['date'].iloc[-1], periods=horizon + 1, freq='B')[-horizon:],
    'totalRevenue': np.repeat(fcst_pltr_revenue.iloc[0]['TimeGPT'], horizon)
})
future_df.head()
date totalRevenue
0 2023-07-03 547264448
1 2023-07-04 547264448
2 2023-07-05 547264448
3 2023-07-06 547264448
4 2023-07-07 547264448

然后我们可以通过 forecast 方法中的 X_df 参数传递未来的收入。由于收入是在历史数据框中,这些信息将被用于模型中。

fcst_pltr_df = nixtla_client.forecast(
    pltr_revenue_df, h=horizon, 
    freq='B',
    time_col='date', 
    target_col='Close',
    X_df=future_df,
)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
WARNING:nixtla.nixtla_client:The specified horizon "h" exceeds the model horizon. This may lead to less accurate forecasts. Please consider using a smaller horizon.
INFO:nixtla.nixtla_client:Using the following exogenous variables: totalRevenue
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
nixtla_client.plot(
    pltr_revenue_df, 
    fcst_pltr_df, 
    id_col='series_id',
    time_col='date',
    target_col='Close',
    max_insample_length=90,
)

我们还可以看到收入的重要性:

nixtla_client.weights_x.plot.barh(x='features', y='weights')

从特征重要性图中,我们可以得出结论,收入是模型预测中的一个重要因素,这意味着收入的变化将影响预测结果。

Give us a ⭐ on Github