时间序列规则和时间序列模型
1. 時間序列規則
1.1 什么是時間序列規則
對于賽題/業務的規則之前已經描述了它的重要性和應用,在此不再贅述。這章主要了解時間序列及其規則,和周期的應用。
1.1.1 時間序列
時間序列(或稱動態數列):指將同一統計指標的數值按其發生的時間先后順序排列而成的數列。通常是在相等間隔的時間段內依照給定的采樣率對某種潛在過程進行觀測的結果。
目的:時間序列數據本質上反映的是某個或者某些隨機變量隨時間不斷變化的趨勢,而時間序列預測方法的核心就是從數據中挖掘出這種規律,并利用其對將來的數據做出估計。
- 可以反映社會經濟現象的發展變化過程,描述現象的發展狀態和結果。
- 可以研究社會經濟現象的發展趨勢和發展速度。
- 可以探索現象發展變化的規律,對某些社會經濟現象進行預測。
- 利用時間序列可以在不同地區或國家之間進行對比分析,這也是統計分析的重要方法之一。
構成要素:長期趨勢,季節變動,循環變動,不規則變動。
- 長期趨勢( T )現象在較長時期內受某種根本性因素作用而形成的總的變動趨勢。
- 季節變動( S )現象在一年內隨著季節的變化而發生的有規律的周期性變動。
- 循環變動( C )現象以若干年為周期所呈現出的波浪起伏形態的有規律的變動。
- 不規則變動(I )是一種無規律可循的變動,包括嚴格的隨機變動和不規則的突發性影響很大的變動兩種類型。
時間序列的規則:
- 時期長短最好一致
- 總體范圍大小應該一致
- 指標的經濟內容應該統一
- 時間段內的計算方法應該統一
- 計算價格和計量單位可比性
1.2 時間序列的周期簡介
A. 平穩序列
是基本上不存在趨勢的序列,序列中的各觀察值基本上在某個固定的水平上波動,在不同時間段波動程度不同,可緩和,可波動,不存在規律周期。
B. 非平穩序列
是包含趨勢、季節性或周期性的序列,只含有其中一種成分,也可能是幾種成分的組合??煞譃?#xff1a;有趨勢序列、有趨勢和季節性序列、幾種成分混合(例如T、S、C、I全有)而成的復合型序列。
a. 趨勢T
時間序列在長時期內呈現出來的某種持續上升或持續下降的變動,也稱長期趨勢。時間序列中的趨勢可以是線性和非線性。
b. 季節性S
季節變動(seasonal fluctuation),是時間序列在一年內重復出現的周期波動。這里的季節不僅指一年中的四季,其實是指任何一種周期性的變化。含有季節成分的序列可能含有趨勢,也可能不含有趨勢。
c. 周期性C
循環波動(cyclical fluctuation),是時間序列中呈現出來的圍繞長期趨勢的一種波浪形或振蕩式波動。周期性通常是由經濟環境的變化引起。
不同于趨勢變動,不是朝著單一方向的持續運動,而是漲落相間的交替波動。
不同于季節變動,季節變動有比較固定的規律,且變動周期大多為一年,循環波動則無固定規律,變動周期多在一年以上,且周期長短不一。
d. 不規則性I
除此之外,還有偶然性因素對時間序列產生影響,致使時間序列呈現出某種隨機波動。時間序列除去趨勢、周期性和季節性后的偶然性波動,稱為隨機性(random),也稱不規則波動(irregular variations)。
關于時間序列模型下個章節會詳細介紹。
1.3 中位數、臨近數據等簡單統計量
時間序列的幾個模型在第一章節已做介紹,下面對常用特征數據進行篩選分析。
中位數
常見得時間序列特征數據之一:中位數。中位數作為時間序列的特征是一種簡單而有效的提取周期因子的方法。因為中位數穩健又十分魯棒,不受極端值的影響。不過缺點也比較明顯,中位數損失了很多信息,后續可以進行優化,例如提取均值和中位數進行融合,按照權重以測試集的結果暫定。
均值
剛才有提到,可以應用均值和中位數進行融合優化特征。其實均值也是常用的模型特征之一,不過由于分布問題,在均勻正態分布的時候較適合使用。
臨近數據
對于時間序列特征而言不得不提臨近數據,在有一定延續性,季節性以及小區建的趨勢時候,臨近數據是必不可少的特征之首。
1.4 基于周期因子的時間序列預測
對于同個群體/個人或同類群體/個人在一定的時間段內進行的固定活動都具有一定的時間序列規則,有一定的周期性,例如支付、金融、市場、交通數據等等。對于時間序列而言,周期因子是重要的核心。
1.4.1 周期因子的時間序列預測案例
舉個簡單的栗子:
由圖可以看到周一到周日的周期波動。預測的核心任務就是盡可能準確的提取這種周期。
第一步: 除以周均值,得到一個比例。
第二步: 按列取中位數,綜上就可以得到一組魯棒的周期因子。
周期因子1 = 每個日子(工作日或周末)的總值 / 整體的均值
周期因子2 = 每個日子(工作日或周末)/ 周均值 的中位數
做預測時,只要將周期因子,乘以一個base,就可以做下一周的預測。例如根據每周客流量的延續性取最后一周的平均客流量100作為base,選擇周期因子2-中位數為這次的周期因子,那么直接乘上中位數,就得到下一周的預測:
直接用最后一周的平均客流量作為base并不一定是最好的方法。也許最后三天或最后五天的均值能更好的反映最新的情況。但是不能直接對最后三天客流量取均值(最后三天是周末,這樣取的base就偏大了)。
需要去掉周期性因素后,再取平均。具體做法:
客流量 = 用最近一周的客流量 / 周期因子2
這樣可以取最后三天的平均:(108+91.4+120)/3=106.5,作為base。具體取多少天的,也要通過測試集的表現來確定,也可以按某些函數形式來給每天賦予不同的權重。
1.4.1 周期因子的時間序列預測步驟
根據上述周期因子預測的案例,進行以下的步驟總結:
- 中位數因子:計算每個最小單位量與時間段均值的比值,根據固定的時間單位,例如周幾或每月1號或N天中的第一天作為時間點,計算其中位數的占比值為中位數因子。例如上述案例的“中位數”
- 加權因子:計算固定時間單位的總量與時間段均值的比值。例如上述案例中的“因子”
- 去周期性:就近時間段的單位數值除以對應的周期因子。
- 新的Base:選擇一定的時間(可以根據測試集進行優化),取其均值。
1.5 代碼區
import pandas as pd import sklearn as skr import numpy as np import datetime import matplotlib.pyplot as plt import seaborn as sns from dateutil.relativedelta import relativedelta #實現月份相加減功能上一章節對數據進行了分析探索,由于業務特點,暫時選擇201404及后的數據進行趨勢分析預測。
# 加載申購贖回的數據 def load_data(path: str = 'user_balance_table.csv')->pd.DataFrame:data_balance = pd.read_csv(path)data_balance = add_timestamp(data_balance)return data_balance.reset_index(drop=True)# 在申購贖回數據集上添加時間標簽 def add_timestamp(data: pd.DataFrame, time_index: str = 'report_date')->pd.DataFrame:data_balance = data.copy()data_balance['date'] = pd.to_datetime(data_balance[time_index], format= "%Y%m%d")data_balance['day'] = data_balance['date'].dt.daydata_balance['month'] = data_balance['date'].dt.monthdata_balance['year'] = data_balance['date'].dt.yeardata_balance['week'] = data_balance['date'].dt.weekdata_balance['weekday'] = data_balance['date'].dt.weekdayreturn data_balance.reset_index(drop=True)# 計算單位時間的申購和贖回總量 def get_total_balance(data: pd.DataFrame, date: str = '2014-03-31')->pd.DataFrame:df_tmp = data.copy()df_tmp = df_tmp.groupby(['date'])['total_purchase_amt','total_redeem_amt'].sum()df_tmp.reset_index(inplace=True)return df_tmp[(df_tmp['date']>= date)].reset_index(drop=True)# 將測試集數據拼接到原數據集匯總 def generate_test_data(data: pd.DataFrame)->pd.DataFrame:total_balance = data.copy()start = datetime.datetime(2014,9,1)testdata = []while start != datetime.datetime(2014,10,15):temp = [start, np.nan, np.nan]testdata.append(temp)start += datetime.timedelta(days = 1)testdata = pd.DataFrame(testdata)testdata.columns = total_balance.columnstotal_balance = pd.concat([total_balance, testdata], axis = 0)total_balance = total_balance.reset_index(drop=True)return total_balance.reset_index(drop=True)# L下載查看乘客信息數據集 def load_user_information(path: str = 'user_profile_table.csv')->pd.DataFrame:return pd.read_csv(path) # 載入數據balance_data = load_data('user_balance_table.csv') balance_data = add_timestamp(balance_data) total_balance = get_total_balance(balance_data, date = '2014-03-01') total_balance = generate_test_data(total_balance) total_balance = add_timestamp(total_balance, 'date') # 創建數據的深層拷貝data = total_balance.copy() # 定義生成時間序列規則預測結果的方法def generate_base(df: pd.DataFrame, month_index: int)->pd.DataFrame:# 選中固定時間段的數據集total_balance = df.copy()total_balance = total_balance[['date','total_purchase_amt','total_redeem_amt']]total_balance = total_balance[(total_balance['date'] >= datetime.datetime(2014,3,1)) & (total_balance['date'] < datetime.datetime(2014, month_index, 1))]# 加入時間戳total_balance['weekday'] = total_balance['date'].dt.weekdaytotal_balance['day'] = total_balance['date'].dt.daytotal_balance['week'] = total_balance['date'].dt.weektotal_balance['month'] = total_balance['date'].dt.month# 統計翌日因子mean_of_each_weekday = total_balance[['weekday']+['total_purchase_amt','total_redeem_amt']].groupby('weekday',as_index=False).mean()for name in ['total_purchase_amt','total_redeem_amt']:mean_of_each_weekday = mean_of_each_weekday.rename(columns={name: name+'_weekdaymean'})mean_of_each_weekday['total_purchase_amt_weekdaymean'] /= np.mean(total_balance['total_purchase_amt'])mean_of_each_weekday['total_redeem_amt_weekdaymean'] /= np.mean(total_balance['total_redeem_amt'])# 合并統計結果到原數據集total_balance = pd.merge(total_balance, mean_of_each_weekday, on='weekday', how='left')# 分別統計翌日在(1~31)號出現的頻次weekday_count = total_balance[['day','weekday','date']].groupby(['day','weekday'],as_index=False).count()weekday_count = pd.merge(weekday_count, mean_of_each_weekday, on='weekday')# 依據頻次對翌日因子進行加權,獲得日期因子weekday_count['total_purchase_amt_weekdaymean'] *= weekday_count['date'] / len(np.unique(total_balance['month']))weekday_count['total_redeem_amt_weekdaymean'] *= weekday_count['date'] / len(np.unique(total_balance['month']))day_rate = weekday_count.drop(['weekday','date'],axis=1).groupby('day',as_index=False).sum()# 將訓練集中所有日期的均值剔除日期殘差得到baseday_mean = total_balance[['day'] + ['total_purchase_amt','total_redeem_amt']].groupby('day',as_index=False).mean()day_pre = pd.merge(day_mean, day_rate, on='day', how='left')day_pre['total_purchase_amt'] /= day_pre['total_purchase_amt_weekdaymean']day_pre['total_redeem_amt'] /= day_pre['total_redeem_amt_weekdaymean']# 生成測試集數據for index, row in day_pre.iterrows():if month_index in (2,4,6,9) and row['day'] == 31:breakday_pre.loc[index, 'date'] = datetime.datetime(2014, month_index, int(row['day']))# 基于base與翌日因子獲得最后的預測結果day_pre['weekday'] = day_pre.date.dt.weekdayday_pre = day_pre[['date','weekday']+['total_purchase_amt','total_redeem_amt']]day_pre = pd.merge(day_pre, mean_of_each_weekday,on='weekday')day_pre['total_purchase_amt'] *= day_pre['total_purchase_amt_weekdaymean']day_pre['total_redeem_amt'] *= day_pre['total_redeem_amt_weekdaymean']day_pre = day_pre.sort_values('date')[['date']+['total_purchase_amt','total_redeem_amt']]return day_pre # 生成預測結果(以及殘差)base_list = [] for i in range(4, 10):base_list.append(generate_base(data, i).reset_index(drop=True))base = pd.concat(base_list).reset_index(drop=True) for i in ['total_purchase_amt','total_redeem_amt']:base = base.rename(columns={i: i+'_base'})data = pd.merge(data.reset_index(drop=True), base.reset_index(drop=True), on='date', how='left').reset_index(drop=True)data['purchase_residual'] = data['total_purchase_amt'] / data['total_purchase_amt_base']data['redeem_residual'] = data['total_redeem_amt'] / data['total_redeem_amt_base'] # 對結果表重命名data = data[['date','purchase_residual','redeem_residual','total_purchase_amt_base', 'total_redeem_amt_base']] for i in data.columns:if i == 'date':data[i] = data[i].astype(str)data[i] = data[i].str.replace('-','') data.columns = [['date'] + ['total_purchase_amt','total_redeem_amt'] + ['total_purchase_predicted_by_cycle','total_redeem_predicted_by_cycle'] ] # 保存預測結果到本地data.to_csv('base_one.csv',index=False)將第一次計算的結果上傳,得到分數
分數不高,有待優化。
2. 時間序列
上面對時間序列進行了基本介紹,下面將對時間序列模型進行詳細介紹。
時間序列模型通常有三種:
混合模型實際上是可根據業務多種分析組裝的,例如IJCAI的季軍曾用過周期與周期項后剩余的殘殺作為周期因子,如下:
2.1 時間序列分解
時間序列對于模型分解方法常用的有四種:經典分解法、X11分解法、SEATS分解法以及STL分解法。
基本思路:
- 運用移動平均法剔除長期趨勢和周期變化,得到序列TC。然后再用按月(季)平均法求出季節指數S。
- 做散點圖,選擇合適的曲線模型擬合序列的長期趨勢,得到長期趨勢T。
- 計算周期因素C。用序列TC除以T即可以得到周期變動因素C。
- 將時間序列T、S、C分解出來以后,剩余的即為不規則變動,即:I=Y/(TSC)。
經典分解法(Classical decomposition)
經典分解法屬于最早的分解法,步驟相對簡單,同時也是很多其他分解算法的基礎。
經典分解法的假設周期性成分在每個周期內都是相同的。
算法比較古老,已不適用。
** X11分解法(X11 decomposition)**
季度性數據和月度數據的分解算法是 X11分解法,它發明于美國人口普查局和加拿大統計局。X11有一些 復雜的方法來處理交易日、假期、一些已知的影響因素的影響。它同時處理了加性模型和乘性模型。這個過程是全自動的,而且對于時間序列中的異常值和數據平平變動很魯棒。
X11-ARIMA模型屬于長應用模型。對于X11分解法在次不做詳解。
** SEATS分解(SEATS decomposition)**
“SEATS”是指“Seasonal Extraction in ARIMA Time Series”。這個方法是由西班牙銀行開發 的,現在被廣泛應用在各國的政府部門中。這個算法只是針對季度性和月度數據。因此天級數據、小時級數據或者周數據,需要其他的方法。
STL分解(STL decomposition)
STL是時間序列分解的一種versatile和魯棒的方法。它是“Seasonal and Trend decomposition using Loess”的縮寫,其中Loess是一種魯棒的回歸算法。
本章索要詳細介紹的正是STL分解法。
2.2 STL分解
2.3 時間序列模型
這次主要介紹的是ARIMA模型:
參考文獻
https://blog.csdn.net/roger_royer/article/details/86439723
https://blog.csdn.net/qq_29831163/article/details/89440215
https://blog.csdn.net/mengjizhiyou/article/details/82683448
https://zhuanlan.zhihu.com/p/70282110
https://blog.csdn.net/snowdroptulip/article/details/79125912
https://www.cnblogs.com/en-heng/p/7390310.html
https://www.ucloud.cn/yun/41868.html
總結
以上是生活随笔為你收集整理的时间序列规则和时间序列模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树莓派3B使用问题#1 关于3.5mm
- 下一篇: WebRTC中的SDP