当前位置:   article > 正文

Kaggle时间序列(Time Series)教程 4-时间序列作为特征(Time Series As Features)_kaggle序列依赖常用算法

kaggle序列依赖常用算法

什么是序列依赖(Serial Dependence)?

在前面的课程中,我们研究了像*时间依赖(time dependent)属性这种容易建模的时间序列性质 ,也就是说,我们可以直接从时间索引中获得特征。 然而,一些时间序列只能使用序列依赖(serially dependent)*的属性来建模,即 使用目标序列的过去值作为特征。 根据时间来绘图,这些时间序列的结构可能并不明显; 然而,根据过去的值来绘制,结构就会变得清晰——如下图所示。
在这里插入图片描述

这两个系列具有序列依赖,但不具有时间依赖。 右边的点的坐标为 (时间t-1的值, 时间t的值).

借助趋势和季节性,我们训练模型以将曲线拟合到上图左侧的图上 – 模型学习了时间依赖性。 本节课的目标是训练模型以将曲线拟合到右侧的图上 – 我们希望它们学习序列依赖。

周期(Cycles)

周期是指在一个时间序列中,某个时间点的值的增长或衰减取决于它之前时间的值,和时间步长本身不一定有关。它是一种特别常见的序列依赖表现方式。 周期行为是可以影响自身或随时间存在持续影响的系统特点。 经济、流行病、动物种群、火山爆发和类似的自然现象经常表现出周期行为。
在这里插入图片描述

四个具有周期行为的时间序列。

周期性行为与季节性的区别在于,周期不一定像季节那样依赖于时间。 一个周期中发生的事情与特定的发生日期无关,而更多地与最近发生的事情有关。 与时间的(至少是相对的)独立性意味着周期行为可能比季节性更不规则。

滞后序列(Lagged Series)和滞后图(Lag Plots)

为了调查时间序列中可能存在的序列依赖性(如周期),我们需要创建序列的“滞后”副本。 Lagging 一个时间序列意味着将其值向前移动一个或多个时间步长,或者等效地将其索引中的时间向后移动一个或多个步骤。 在任何一种情况下,结果都是滞后序列中的观察值似乎发生在较晚的时间。

这显示了美国的月度失业率 (y) 及其第一个和第二个滞后序列(分别为 y_lag_1 和 y_lag_2)。 注意滞后序列的值是如何及时向前移动的。


import pandas as pd

# Federal Reserve dataset: https://www.kaggle.com/federalreserve/interest-rates
reserve = pd.read_csv(
    "./ts-course-data/reserve.csv",
    parse_dates={'Date': ['Year', 'Month', 'Day']},
    index_col='Date',
)

y = reserve.loc[:, 'Unemployment Rate'].dropna().to_period('M')
df = pd.DataFrame({
    'y': y,
    'y_lag_1': y.shift(1),
    'y_lag_2': y.shift(2),    
})

df.head()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述

通过滞后时间序列,我们可以使其过去的值与我们试图预测的值同时出现(换句话说,在同一行中)。 这使得滞后序列可用作建模序列依赖的特征。 为了预测美国失业率序列,我们可以使用y_lag_1y_lag_2作为特征来预测目标y。 这使用前两个月失业率来预测未来失业率。

滞后图(Lag plots)

时间序列的滞后图显示了其值与滞后值的关系。 通过查看滞后图,时间序列中的序列依赖性通常会变得明显。 我们可以从 US Unemployment 的滞后图中看到,当前失业率与过去的失业率之间存在强烈且明显的线性关系。

在这里插入图片描述

自相关的美国失业滞后图。

最常用的序列依赖性度量称为自相关,它只是时间序列与其滞后之一的相关性。 US Unemployment 在滞后1时的自相关为0.99,在滞后2时为0.98,依此类推。

选择滞后

在选择滞后作为特征时,包含每个具有较大自相关的滞后 通常没有用处。 例如,在 US Unemployment 中,滞后2处的自相关可能完全来自滞后1的“衰减”信息 – 只是从上一步结转的相关性。 如果滞后 2 不包含任何新内容,那么如果我们已经有了滞后 1,就没有理由包含它。

偏自相关告诉您滞后与所有先前滞后的相关性 – 也可以说,是滞后贡献的“新”相关性的数量。 绘制偏自相关可以帮助您选择要使用的滞后特征。 在下图中,滞后1到滞后6不在“无相关性”区间(蓝色),因此我们可以选择滞后 1到滞后6作为 US Unemployment 的特征。 (滞后 11 可能是误报。)
在这里插入图片描述

Partial autocorrelations of US Unemployment through lag 12 with 95% confidence intervals of no correlation.

像上面这样的图被称为相关图。 相关图适用于滞后特征,本质上就像周期图适用于傅里叶特征。

最后,我们需要注意自相关和偏自相关是线性依赖性的度量。 因为现实世界的时间序列通常有很大的非线性依赖,在选择滞后特征时最好看滞后图(或者使用一些更一般的依赖度量,比如互信息)。 太阳黑子系列具有非线性相关的滞后,我们可能会忽略自相关。
在这里插入图片描述

太阳黑子 系列滞后图.

像这样的非线性关系可以转换为线性关系,也可以通过适当的算法学习。

示例 - 流感趋势

Flu Trends 数据集包含 2009 年至 2016 年间数周因流感而就诊医生的记录。我们的目标是预测未来几周的流感病例数。

我们将采取两种方法。 第一种方法是,我们使用滞后特征预测就诊次数。 第二种方法是使用另一组(谷歌趋势捕获的与流感相关的搜索词)时间序列的滞后来预测医生的就诊次数。


from pathlib import Path
from warnings import simplefilter

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from scipy.signal import periodogram
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from statsmodels.graphics.tsaplots import plot_pacf

simplefilter("ignore")

# Set Matplotlib defaults
plt.style.use("seaborn-whitegrid")
plt.rc("figure", autolayout=True, figsize=(11, 4))
plt.rc(
    "axes",
    labelweight="bold",
    labelsize="large",
    titleweight="bold",
    titlesize=16,
    titlepad=10,
)
plot_params = dict(
    color="0.75",
    style=".-",
    markeredgecolor="0.25",
    markerfacecolor="0.25",
)
%config InlineBackend.figure_format = 'retina'


def lagplot(x, y=None, lag=1, standardize=False, ax=None, **kwargs):
    from matplotlib.offsetbox import AnchoredText
    x_ = x.shift(lag)
    if standardize:
        x_ = (x_ - x_.mean()) / x_.std()
    if y is not None:
        y_ = (y - y.mean()) / y.std() if standardize else y
    else:
        y_ = x
    corr = y_.corr(x_)
    if ax is None:
        fig, ax = plt.subplots()
    scatter_kws = dict(
        alpha=0.75,
        s=3,
    )
    line_kws = dict(color='C3', )
    ax = sns.regplot(x=x_,
                     y=y_,
                     scatter_kws=scatter_kws,
                     line_kws=line_kws,
                     lowess=True,
                     ax=ax,
                     **kwargs)
    at = AnchoredText(
        f"{corr:.2f}",
        prop=dict(size="large"),
        frameon=True,
        loc="upper left",
    )
    at.patch.set_boxstyle("square, pad=0.0")
    ax.add_artist(at)
    ax.set(title=f"Lag {lag}", xlabel=x_.name, ylabel=y_.name)
    return ax


def plot_lags(x, y=None, lags=6, nrows=1, lagplot_kwargs={}, **kwargs):
    import math
    kwargs.setdefault('nrows', nrows)
    kwargs.setdefault('ncols', math.ceil(lags / nrows))
    kwargs.setdefault('figsize', (kwargs['ncols'] * 2, nrows * 2 + 0.5))
    fig, axs = plt.subplots(sharex=True, sharey=True, squeeze=False, **kwargs)
    for ax, k in zip(fig.get_axes(), range(kwargs['nrows'] * kwargs['ncols'])):
        if k + 1 <= lags:
            ax = lagplot(x, y, lag=k + 1, ax=ax, **lagplot_kwargs)
            ax.set_title(f"Lag {k + 1}", fontdict=dict(fontsize=14))
            ax.set(xlabel="", ylabel="")
        else:
            ax.axis('off')
    plt.setp(axs[-1, :], xlabel=x.name)
    plt.setp(axs[:, 0], ylabel=y.name if y is not None else x.name)
    fig.tight_layout(w_pad=0.1, h_pad=0.1)
    return fig


data_dir = Path("./ts-course-data")
flu_trends = pd.read_csv(data_dir / "flu-trends.csv")
flu_trends.set_index(
    pd.PeriodIndex(flu_trends.Week, freq="W"),
    inplace=True,
)
flu_trends.drop("Week", axis=1, inplace=True)

ax = flu_trends.FluVisits.plot(title='Flu Trends', **plot_params)
_ = ax.set(ylabel="Office Visits")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

在这里插入图片描述
我们的流感趋势数据显示了不规则的周期而不是常规的季节性:高峰往往出现在新年前后,但有时更早或更晚,有时更大或更小。 使用滞后特征对这些周期进行建模将使我们的预测能够对不断变化的条件做出动态反应,而不是像季节性特征那样受限于确切的日期和时间。

让我们先看一下滞后和自相关图:

_ = plot_lags(flu_trends.FluVisits, lags=12, nrows=2)
_ = plot_pacf(flu_trends.FluVisits, lags=12)
  • 1
  • 2

在这里插入图片描述

滞后图表明 FluVisits 与其滞后的关系主要是线性的,而偏自相关图表明可以使用滞后 1、2、3 和 4 来捕获依赖关系。我们可以在 Pandas 中使用shift 方法来滞后时间序列。 同时,我们将用 0.0 填充滞后创建的缺失值。

def make_lags(ts, lags):
    return pd.concat(
        {
            f'y_lag_{i}': ts.shift(i)
            for i in range(1, lags + 1)
        },
        axis=1)


X = make_lags(flu_trends.FluVisits, lags=4)
X = X.fillna(0.0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在之前的课程中,我们能够为训练数据之外的任意多个步长创建预测。 然而,当使用滞后特征时,我们仅限于预测滞后值可用的时间步长。 在星期一使用lag1的特征,我们无法对星期三进行预测,因为它所需的lag值是星期二,此时还没发生。

我们将在第6课中看到处理这个问题的策略。对于这个例子,我们将只使用来自测试集的值。


# Create target series and data splits
y = flu_trends.FluVisits.copy()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=60, shuffle=False)

# Fit and predict
model = LinearRegression()  # `fit_intercept=True` since we didn't use DeterministicProcess
model.fit(X_train, y_train)
y_pred = pd.Series(model.predict(X_train), index=y_train.index)
y_fore = pd.Series(model.predict(X_test), index=y_test.index)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

ax = y_train.plot(**plot_params)
ax = y_test.plot(**plot_params)
ax = y_pred.plot(ax=ax)
_ = y_fore.plot(ax=ax, color='C3')
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

只看预测值,我们可以看到我们的模型需要一个时间步长来对目标序列的突然变化做出反应。 这是仅使用目标值的滞后作为特征的模型的一个常见限制。


ax = y_test.plot(**plot_params)
_ = y_fore.plot(ax=ax, color='C3')
  • 1
  • 2
  • 3

在这里插入图片描述

为了改进预测,我们可以尝试找到领先指标,即可以为流感病例变化提供“预警”的时间序列。 对于我们的第二种方法,我们将在我们的训练数据中添加一些由谷歌趋势测量的与流感相关的搜索词的流行度。

将搜索词组“FluCough”与目标“FluVisits”绘制成图表表明,此类搜索词可用作领先指标:与流感相关的搜索往往在就诊前几周变得更多。


ax = flu_trends.plot(
    y=["FluCough", "FluVisits"],
    secondary_y="FluCough",
)
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
该数据集包含 129 个这样的术语,但我们只会使用其中的几个。

search_terms = ["FluContagious", "FluCough", "FluFever", "InfluenzaA", "TreatFlu", "IHaveTheFlu", "OverTheCounterFlu", "HowLongFlu"]

# Create three lags for each search term
X0 = make_lags(flu_trends[search_terms], lags=3)

# Create four lags for the target, as before
X1 = make_lags(flu_trends['FluVisits'], lags=4)

# Combine to create the training data
X = pd.concat([X0, X1], axis=1).fillna(0.0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们的预测有点粗略,但我们的模型似乎能够更好地预测流感访问量的突然增加,这表明搜索流行度的几个时间序列作为领先指标确实是有效的。


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=60, shuffle=False)

model = LinearRegression()
model.fit(X_train, y_train)
y_pred = pd.Series(model.predict(X_train), index=y_train.index)
y_fore = pd.Series(model.predict(X_test), index=y_test.index)

ax = y_test.plot(**plot_params)
_ = y_fore.plot(ax=ax, color='C3')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

本课中说明的时间序列可以称为“纯周期”:它们没有明显的趋势或季节性。 尽管时间序列同时拥有趋势、季节性和周期——这三个部分的并不少见。 您可以通过为每个组件添加适当的特征来使用线性回归对此类序列进行建模。 您甚至可以将经过训练以分别学习组件的模型结合起来,我们将在下一课中学习如何使用预测混合模型进行操作。

轮到你了

为商店销售创建滞后特征 并探索其他类型的时间序列特征。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/134941
推荐阅读
相关标签
  

闽ICP备14008679号