[深度学习项目] - 时间序列预测 (1)

时间序列预测问题的定义与应用场景

时间序列: 按时间发生的先后顺序进行排序的数据。
通常,时间序列存在时间间隔(每隔固定的时间,记录和收集一次信息),时间间隔也称为时间颗粒度,比如 小时,天,周等。

时序预测(time series forecasting):我们想要根据过去和现在已有的数据未来某时间点或者时间段的某物理量(观测值)进行预测。 而我们知道,某个物理量会受到一系列物理量的影响(外部变量:external regressor)
比如,在零售中,我们想要预测未来四周内某产品的销售情况,我们就需要过去一段时间的销售历史以及影响销量的相关变量(天气,节日,温度等等),制定 生产,订货和定价。
工厂:根据订单量的估计,制定招聘和排版。
基金:根据股价进行预估,决定股票的买卖。
IT运维:根据并发量的估计,决定服务器的数量和值班人数。

因此,时间序列并不满足 马尔可夫性质(根据当前的状态就能得到预测的结果:因为 我们不可能收集到所有影响观测值的状态;同时当前的观测值会受到历史数据的影响)。我们在对未来的观测量进行预测时,输入变量不仅要包括未来外部变量的状态,也要包括过去一段时间间隔的历史数据(包括外部变量和观测量)

基础知识

  1. 编程: python(pandas, numpy, scipy, matplotlib), SQL
  2. 数学基础:高数,线代,概率论(高斯分布,泊松分布,负二项分布;条件概率分布,置信区间
  3. 机器学习:(训练集,测试集,交叉验证;线性回归,决策树; LightGBM, logistic Regression)
  4. LSTM, RNN, CNN, Transformer
  5. 时间序列预测算法: Prophet, DeepAR, MQRNN, Arima, ETS…

另外需要结合业务需求来进行问题分析: 对时间序列规律进行分析,对模型进行选型,误差分析,模型优化。

时序预测算法的发展与分类

  1. 传统的统计学预测算法: ETS, Arima。
  2. 机器学习预测算法: Xgboost,LightGBM,Prophet
  3. 深度学习预测算法:DeepAR(LSTM), MQRNN(CNN)
特点传统统计学算法机器学习算法深度学习算法
对问题的理解(不同外部变量的自相关性,稳定性,周期)需要不需要不需要
能够刻画外部变量对于观测值的非线性关系不可以根据算法(线性,非线性算法)可以
数据量的要求中等很多
特征工程 难度取决于算法
是否有可解释性根据算法无可解释性

时序预测的步骤

  1. 应用场景和问题重述
股价预测销量预测
场景根据某公司明天的股价走势,决定目前是否买入/卖出某公司的股票根据某店铺每周根据未来1-4周的销量,决定每个产品的备货情况
预测对象(观测值)股价产品销量
结果产生的频率(每过多久,进行一次预测)每天每周
预测的周期 (预测的观测对象的未来时间长度)未来一天未来4周
时间颗粒度(收集数据的频率)每天收集数据每周收集数据
截面颗粒度(预测的观测值对应的对象)某公司某店铺的某产品
准确性标准MAPE(mean average percentage error)MAPE
  1. 预测难度分析和预判
    2.1 确定主要的外部变量,以及外部变量对于预测值的影响程序。 另外预测值会不会有大量的波动,并且受到随机变量的影响比较大。 一方面 用于定性的说明 预测值最终估计的准确性。

2.2 预测值的周期性规律(历史规律是否会在未来重复出现,以及出现呢的周期性规律唱长度)
2.3 我们需要的数据量多少。一方面取决于 预测值的周期长度,另一方面也取决于算法的类型。(数据越多越好)
2.4 使用最简单的移动平均法来预测效果


移动平均法 主要是根据 预测值在过去一段时间内的数据 来对未来的数据进行预测和估计。 也就是说 移动平均法 假设 外部变量是长期不变的,适用于 不快速增长/减少的变量的预测,主要用于消除 随机变量对于 预测值的影响,得到预测值的趋势变化。如果预测值有周期性/季节性的因素影响,移动平均法在对未来数据预测时,需要考虑超过一个周期的数据,用于消除 周期/季节的影响。

移动平均法分为 简单平均法和加权平均法:
简单平均法: A t + 1 = A t + A t − 1 + ⋯ + A t − N + 1 N A_{t+1}=\frac{A_{t}+A_{t-1}+\dots+A_{t-N+1}}{N} At+1=NAt+At1++AtN+1
加权平均法: A t + 1 = w 1 A t + w 2 A t − 1 + ⋯ + w N A t − N + 1 A_{t+1}=w_1A_{t}+w_2 A_{t-1}+\dots+w_{N} A_{t-N+1} At+1=w1At+w2At1++wNAtN+1,满足 w 1 + w 2 + ⋯ + w N = 1 w_1+w_2+\dots+w_{N}=1 w1+w2++wN=1 一般来说,时间上距离 t + 1 t+1 t+1较近的预测值权重较大;另外权重值也取决于周期的影响: 对于周期性的预测值,那么权重值也是周期性的。


  1. 收集数据
    历史的 观测值 与 外部变量数据
    未来的 外部变量数据

  2. 对数据进行探索性分析(数据处理和特征工程)
    4.1 数据的范围
    4.2 数据中是否存在 异常值和缺失值,clip, 是否满足 高斯分布等等
    4.3 预测对象的趋势和周期性,自相关性系数等等
    4.4 外部变量对于 观测值的 如何影响(正相关,负相关的程度),是否是多个外部变量耦合 对 观测值进行影响。

  3. 算法选择
    5.1 是否具有 可解释性(是否要求可解释性)(同时,如果没有可解释性,我们如何在建模过程中对于算法好坏与算法建模正确与否 进行评估)
    5.2 算法能够达到精度要求,运算性能要求
    5.3 考虑鲁棒性,是否能够后续算法的改进和参数优化

  4. 建模和测试
    6.1 常用符号
    t t t: 时间点
    y t y_t yt:t时刻的观测值
    x t x_t xt:t时刻的外部变量, x t x_t xt是一个向量,长度表示了外部变量的个数
    T T T:基于多久的历史数据来对未来进行预测
    h h h: 预测未来多少个时间点的观测值

6.2 模型输入
输入: 过去T个时刻的 观测值与外部变量,未来h个时间点的外部变量(对于问题理解越透彻,外部变量选择越好,最后的预测效果更好)
[ y 1 , x 1 y 2 , x 2 , … , y T , x T , x T + 1 , … , x T + h ] [y_1,x_1y_2,x_2,\dots,y_T,x_T,x_{T+1},\dots,x_{T+h}] [y1,x1y2,x2,,yT,xT,xT+1,xT+h]

输出: 未来h个时间点的观测值
[ y T , y T + 1 , … , y T + h ] [y_T,y_{T+1},\dots,y_{T+h}] [yT,yT+1,,yT+h]
使用滑动窗口 讲过去的数据 喂给 模型,让模型进行学习。

6.3 划分 训练集,验证集,测试集
时间预测和传统监督学习数据集划分不同
传统监督学习 的不同数据采集 都是独立同分布的,也就是 不同时间段采集的数据都是 同分布的,不存在时间关系。 此时数据集划分时 随机划分就可以。

但是对于时间预测模型来说,数据本身是存在先后顺序的,而我们期望的泛化能力是 对未来(而不是过去的数据)进行预测。 因此,我们在测试数据时,我们需要测试的 模型对于未来数据的预测能力,所以划分数据时,不能随即划分数据。 而应该讲 最开始的部分划分为训练集,将最新数据部分划分为测试集。 从而考察 对于过去数据集学习,然后对于未来数据进行预测的泛化能力考察。

PS: 划分 训练集与测试集的细节: 我们使用滑动窗口来对数据进行获取。 而输出h如果长度不是1,那么直接形成的数据中 训练集和测试集的输出 会存在 重叠部分,但这是不可以的。 因此我们在 划分训练集和测试集时,会剔除掉 h-1组数据。

我们进行多个算法/多组超参数选择时,就需要划分成 训练集,验证集和测试集。 这三组数据的输出是不能有交集的。而且 数据的时间先后顺序也是按照: 训练集,验证机,测试集。
time series forecasting procedure

Homework

  1. 数据的主键: 根据一些特征,可以唯一性地确定 该行的数据。 这些特征就叫主键。
  2. 数据的时间颗粒度 和 时间范围
  3. 是否存在主键重复的数据
  4. 一共存在多少条时间序列
  5. 是否每条时间序列都连续,不存在确实的时间点

数据: stock_price.csv, store_sales.csv

Pandas读取数据

import pandas as pd

df_price = pd.read_csv("stock_price.csv")
df_sales = pd.read_csv("store_sales.csv")

查看数据量 和 特征(特征数量与类型)

# 数据量大小, 数据的特征数量,特征的类型
print( df_price.info() )
print( df_sales.info() )

>>>
# stock prices
RangeIndex: 8398 entries, 0 to 8397
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   date     8398 non-null   object 
 1   company  8398 non-null   object 
 2   price    8398 non-null   float64
dtypes: float64(1), object(2)
memory usage: 197.0+ KB

###############
# store sales 
RangeIndex: 421570 entries, 0 to 421569
Data columns (total 4 columns):
 #   Column  Non-Null Count   Dtype  
---  ------  --------------   -----  
 0   store   421570 non-null  int64  
 1   dept    421570 non-null  int64  
 2   week    421570 non-null  object 
 3   sales   421570 non-null  float64
dtypes: float64(1), int64(2), object(1)
memory usage: 12.9+ MB

股票有 8398个数据,有特征 data,company两个特征,输出price,并且没有缺失值。 data和company 是 object类型(具体类型是什么,后续查看),输出price是float64类型。

商店销售量 有 421570个数据, 有 store, dept, week三个特征,输出sales,没有缺失值。 store,dept是Int64类型,week是object格式,sales是 float64类型。

# 查看数据
print( df_price.head(2) )
print( df_sales.head(2) )

>>> 
         date company      price
0  2012-05-18      FB  38.230000
1  2012-05-21      FB  34.029999

################

   store  dept        week     sales
0      1     1  2010-02-01  24924.50
1      1     1  2010-02-08  46039.49

股票数据中 data是time string类型, company是 公司名称。
商店销售中 store和dept是 Int类型,表示不同的商店和部门。 week是time string类型。

print(df_price.groupby("company").count() )

print(df_sales.groupby("store").count())
print(df_sales.groupby("dept").count())

>>>
         date  price
company             
ETSY     1758   1758
FB       2488   2488
PINS      749    749
SNAP     1285   1285
TWTR     2118   2118
        dept   week  sales
store                     
1      10244  10244  10244
2      10238  10238  10238
3       9036   9036   9036
4      10272  10272  10272
5       8999   8999   8999
6      10211  10211  10211
7       9762   9762   9762
8       9895   9895   9895
9       8867   8867   8867
10     10315  10315  10315
11     10062  10062  10062
12      9705   9705   9705
13     10474  10474  10474
14     10040  10040  10040
15      9901   9901   9901
16      9443   9443   9443
17      9864   9864   9864
18      9859   9859   9859
19     10148  10148  10148
20     10214  10214  10214
21      9582   9582   9582
22      9688   9688   9688
23     10050  10050  10050
24     10228  10228  10228
25      9804   9804   9804
26      9854   9854   9854
27     10225  10225  10225
28     10113  10113  10113
29      9455   9455   9455
30      7156   7156   7156
31     10142  10142  10142
32     10202  10202  10202
33      6487   6487   6487
34     10224  10224  10224
35      9528   9528   9528
36      6222   6222   6222
37      7206   7206   7206
38      7362   7362   7362
39      9878   9878   9878
40     10017  10017  10017
41     10088  10088  10088
42      6953   6953   6953
43      6751   6751   6751
44      7169   7169   7169
45      9637   9637   9637
      store  week  sales
dept                    
1      6435  6435   6435
2      6435  6435   6435
3      6435  6435   6435
4      6435  6435   6435
5      6347  6347   6347
...     ...   ...    ...
95     6435  6435   6435
96     4854  4854   4854
97     6278  6278   6278
98     5836  5836   5836
99      862   862    862

所以我们知道,在股价数据中主键是date和company; 在商店销售量中 store, dept和week是主键。

print(df_price.groupby("date").count())
print(df_sales.groupby("week").count())

>>>
            company  price
date                      
2012-05-18        1      1
2012-05-21        1      1
2012-05-22        1      1
2012-05-23        1      1
2012-05-24        1      1
...             ...    ...
2022-03-31        5      5
2022-04-01        5      5
2022-04-04        5      5
2022-04-05        5      5
2022-04-06        5      5

[2488 rows x 2 columns]
            store  dept  sales
week                          
2010-02-01   2955  2955   2955
2010-02-08   2956  2956   2956
2010-02-15   2977  2977   2977
2010-02-22   2951  2951   2951
2010-03-01   2944  2944   2944
...           ...   ...    ...
2012-09-24   2962  2962   2962
2012-10-01   2976  2976   2976
2012-10-08   2990  2990   2990
2012-10-15   2950  2950   2950
2012-10-22   2959  2959   2959

[143 rows x 3 columns]

股价时间颗粒度是 天, 商店销售时间颗粒度是周。
股价 时间范围是: 2012-05-18 ~ 2022-04-06
商店销售量 时间范围是: 2010-02-01 ~ 2012-10-22

判断主键重复

# 主键重复判断
print(  df_price.duplicated(subset=["date","company"]).sum() )
print(  df_sales.duplicated(subset=["store","dept","week"]).sum() )

>>>
0
0

主键无重复数据

时间序列条数

对于股价数据来说, 除了"date"之外,只有"company"一个主键,一共有5个公司,所以有5条时间序列

# store and dept  时间序列条数
print( df_sales.groupby(["store","dept"]).count() )

>>>
[3331 rows x 2 columns]

所以对于商店销量数据中,一共有 3331条时间序列

判断时间序列是否连续

# 判断时间序列是否连续:
# 时间上的最大 - 时间上的最小 +1 == 数量: 连续 否则就不连续
## stocks
df_price["date"] = pd.to_datetime( df_price["date"] )
df_sales["week"] = pd.to_datetime( df_sales["week"] )

price_summary = df_price.groupby("company")["date"].agg(["min","max","nunique"]).reset_index()
sales_summary = df_sales.groupby(["store","dept"])["week"].agg(["min","max","nunique"]).reset_index()

price_summary["time_nt_continue"] =(price_summary["max"]-price_summary["min"]).dt.days+1 - price_summary["nunique"]
print(  "not continue num in stock: ",price_summary[ price_summary["time_nt_continue"]>0 ].count() )

sales_summary["time_nt_continue"] = (sales_summary["max"]-sales_summary["min"]).dt.days/7+1 - sales_summary["nunique"]
print(  "not continue num in sales: ",sales_summary[ sales_summary["time_nt_continue"]>0 ].count() )

>>>
not continue num in stock:  company             5
min                 5
max                 5
nunique             5
time_nt_continue    5
dtype: int64
not continue num in sales:  store               605
dept                605
min                 605
max                 605
nunique             605
time_nt_continue    605
dtype: int64

所以股价中 五条都不连续。 而商品中有605条不连续。

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值