%load_ext autoreload
%autoreload 2
目标变换
无缝转换目标值
由于mlforecast使用单一全局模型,因此对目标进行一些转换可能会很有帮助,以确保所有序列具有相似的分布。这些转换也有助于消除趋势,以便于那些无法直接处理趋势的模型。
数据准备
在这个示例中,我们将使用M4数据集中的一个单一系列。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datasetsforecast.m4 import M4
from sklearn.base import BaseEstimator
from mlforecast import MLForecast
from mlforecast.target_transforms import Differences, LocalStandardScaler
= 'data'
data_path await M4.async_download(data_path, group='Hourly')
*_ = M4.load(data_path, 'Hourly')
df, 'ds'] = df['ds'].astype('int32')
df[= df[df['unique_id'].eq('H196')] serie
本地转换
每个系列应用的转换
差异
我们将查看我们的系列,以寻找可能的差异,这些差异将有助于我们的模型。
def plot(series, fname):
= len(series)
n_series = plt.subplots(ncols=n_series, figsize=(7 * n_series, 6), squeeze=False)
fig, ax for (title, serie), axi in zip(series.items(), ax.flat):
'ds')['y'].plot(title=title, ax=axi)
serie.set_index(f'../../figs/{fname}', bbox_inches='tight')
fig.savefig( plt.close()
'original': serie}, 'target_transforms__eda.png') plot({
我们可以看到我们的数据具有趋势性以及明显的季节性。我们可以先尝试去除趋势。
= MLForecast(
fcst =[],
models=1,
freq=[Differences([1])],
target_transforms
)= fcst.preprocess(serie)
without_trend 'original': serie, 'without trend': without_trend}, 'target_transforms__diff1.png') plot({
趋势已经消失,我们现在可以尝试进行24差分(减去前一天同一小时的值)。
= MLForecast(
fcst =[],
models=1,
freq=[Differences([1, 24])],
target_transforms
)= fcst.preprocess(serie)
without_trend_and_seasonality 'original': serie, 'without trend and seasonality': without_trend_and_seasonality}, 'target_transforms__diff2.png') plot({
本地标准化器
我们看到我们的序列现在是随机噪声。假设我们还想对其进行标准化,即使其均值为0,方差为1。我们可以在这些差异之后添加 LocalStandardScaler 转换。
= MLForecast(
fcst =[],
models=1,
freq=[Differences([1, 24]), LocalStandardScaler()],
target_transforms
)= fcst.preprocess(serie)
standardized 'original': serie, 'standardized': standardized}, 'target_transforms__standardized.png')
plot({'y'].agg(['mean', 'var']).round(2) standardized[
mean -0.0
var 1.0
Name: y, dtype: float64
现在我们已经捕获了序列的组件(趋势 + 季节性),我们可以尝试使用一个始终预测为0的模型进行预测,这基本上将投影出趋势和季节性。
class Zeros(BaseEstimator):
def fit(self, X, y=None):
return self
def predict(self, X, y=None):
return np.zeros(X.shape[0])
= MLForecast(
fcst ={'zeros_model': Zeros()},
models=1,
freq=[Differences([1, 24]), LocalStandardScaler()],
target_transforms
)= fcst.fit(serie).predict(48)
preds = plt.subplots()
fig, ax 24 * 10), preds]).set_index('ds').plot(ax=ax)
pd.concat([serie.tail( plt.close()
'../../figs/target_transforms__zeros.png') fig.savefig(
全局变换
应用于所有序列的变换
全局Sklearn转换器
有一些变换不需要学习任何参数,例如应用对数。这些变换可以使用 GlobalSklearnTransformer
来轻松定义,该变换接受一个兼容 scikit-learn 的变换器并将其应用于所有序列。以下是如何定义一个变换的示例,该变换对序列中每个值加1后应用对数,这可以帮助避免计算0的对数。
import numpy as np
from sklearn.preprocessing import FunctionTransformer
from mlforecast.target_transforms import GlobalSklearnTransformer
= FunctionTransformer(func=np.log1p, inverse_func=np.expm1)
sk_log1p = MLForecast(
fcst ={'zeros_model': Zeros()},
models=1,
freq=[GlobalSklearnTransformer(sk_log1p)],
target_transforms
)= fcst.preprocess(serie)
log1p_transformed 'original': serie, 'Log transformed': log1p_transformed}, 'target_transforms__log.png') plot({
我们还可以将此与局部转换相结合。例如,我们可以先进行对数变换,然后再进行差分。
= MLForecast(
fcst =[],
models=1,
freq=[GlobalSklearnTransformer(sk_log1p), Differences([1, 24])],
target_transforms
)= fcst.preprocess(serie)
log_diffs 'original': serie, 'Log + Differences': log_diffs}, 'target_transforms__log_diffs.png') plot({
自定义变换
实现您自己的目标变换
为了实现您自己的目标转换,您必须定义一个继承自 mlforecast.target_transforms.BaseTargetTransform
的类(这会处理将列名设置为 id_col
、time_col
和 target_col
属性),并实现 fit_transform
和 inverse_transform
方法。下面是如何定义一个最小-最大缩放器的示例。
from mlforecast.target_transforms import BaseTargetTransform
class LocalMinMaxScaler(BaseTargetTransform):
"""将每个序列缩放到 [0, 1] 区间内。"""
def fit_transform(self, df: pd.DataFrame) -> pd.DataFrame:
self.stats_ = df.groupby(self.id_col)[self.target_col].agg(['min', 'max'])
= df.merge(self.stats_, on=self.id_col)
df self.target_col] = (df[self.target_col] - df['min']) / (df['max'] - df['min'])
df[= df.drop(columns=['min', 'max'])
df return df
def inverse_transform(self, df: pd.DataFrame) -> pd.DataFrame:
= df.merge(self.stats_, on=self.id_col)
df for col in df.columns.drop([self.id_col, self.time_col, 'min', 'max']):
= df[col] * (df['max'] - df['min']) + df['min']
df[col] = df.drop(columns=['min', 'max'])
df return df
现在您可以将该类的实例传递给 target_transforms
参数。
= MLForecast(
fcst =[],
models=1,
freq=[LocalMinMaxScaler()],
target_transforms
)= fcst.preprocess(serie)
minmax_scaled 'original': serie, 'min-max scaled': minmax_scaled}, 'target_transforms__minmax.png') plot({
Give us a ⭐ on Github