fbprophet:如何用加法模型探索時間序列數據


 fbprophet

fbprophet是facebook開源的的一個時間序列預測算法,能夠幾乎全自動地預測時間序列未來地走勢。它是基於時間序列分解和機器學習的擬合來做的,其中在擬合模型的時候使用pyStan這個開源庫,因此能夠在較快時間內得到需要預測的結果。

優點很多,但是在windows下安裝有點坑,推薦使用conda安裝。我這里是在Google Drive Notebook演示(其自帶fbprophet)。

時間序列建模的加和模型

時間序列是日常生活中其中一種最常見的數據類型。金融市場的價格、天氣、家庭耗能、甚至體重都是可以定期收集數據的例子。幾乎每個數據科學家都會在日常工作中碰到時間序列,而學習如何為時間序列建模是數據科學中重要的技能。

用以分析和預測周期數據的加和模型便是一種簡單但強大的模型。背后直觀的概念是:把時間序列分成不同時間間隔和整體趨勢的組合,間隔可以是每天、每周、每季度、每年。你的家也許在夏天比懂冬天耗能,但整體上因為更有效率的能源使用呈遞減趨勢。加和模型能夠展現出規律/趨勢並根據這些觀察作出預測。

以下的圖展示一個時間序列分解成整體趨勢、年趨勢還有周趨勢。

准備數據

通常,一個數據科學的項目有大約 80%的時間花在獲取和清洗數據上。本項目中,Quandl 庫可以將這個工作量減少到 5%左右。Quandl 可以在命令行中通過 pip 命令安裝:

pip install quandl

Quandl 是免費的,你可以每天提出 50 個訪問請求而無需注冊。如果注冊一個免費的帳戶,你會得到一個 API 密鑰,允許無限制次數的請求。

首先,引入所需的庫並獲取數據。Quandl 中的數據幾乎是無限的,但我想集中比較同行業中的兩家公司,即特斯拉和通用汽車。特斯拉是一個引人注目的公司,不僅因為它是 111 年以來美國第一個成功的汽車創業公司,它也是 2017 年美國最值錢的汽車公司。它的競爭者是通用汽車,通用汽車最近已經通過制造一些非常酷的全電動車來展現擁抱未來汽車的跡象。

我們可以通過一句簡單的 quandl 命令來獲得兩家公司的每日股票市值:

# quandl for financial data
import quandl
# pandas for data manipulation
import pandas as pd
quandl.ApiConfig.api_key = 'getyourownkey!'
# Retrieve TSLA data from Quandl
tesla = quandl.get('WIKI/TSLA')
# Retrieve the GM data from Quandl
gm = quandl.get('WIKI/GM')
gm.head(5)

Quandly 自動將數據放入 Pandas 數據框(DataFrame)中,DataFrame 是數據科學家的首選數據類型。(對於其他公司,只需用「TSLA」或「GM」替換股票代碼,你也可以指定日期范圍)

數據探索

在建模之前,最好先了解一下數據的結構和范圍。這也將有助於找出需要糾正的異常值或缺失值。

Pandas dataframe 可以很容易地用內置方法繪圖:

# The adjusted close accounts for stock splits, so that is what we should graph
plt.plot(gm.index, gm['Adj. Close'])
plt.title('GM Stock Price')
plt.ylabel('Price ($)');
plt.show()
plt.plot(tesla.index, tesla['Adj. Close'], 'r')
plt.title('Tesla Stock Price')
plt.ylabel('Price ($)');
plt.show();

 僅僅比較這兩家公司的股票價格,並沒有顯示出哪個更有價值,因為公司的總市值也取決於股票數量(市值=股價*數量)。Quandl 沒有免費的股票數量數據,但是我找到了兩家公司的平均年度股票數。這是不精確的,但是對我們的分析來說足夠准確。有時我們不得不使用不完善的數據!

在這里,我們使用 Pandas 的一些技巧,如改變列的索引(reset_index)、使用 loc 命令添加索引和更改 dataframe 中的值。

# Yearly average number of shares outstanding for Tesla and GM
tesla_shares = {2018: 168e6, 2017: 162e6, 2016: 144e6, 2015: 128e6, 2014: 125e6, 2013: 119e6, 2012: 107e6, 2011: 100e6, 2010: 51e6}
gm_shares = {2018: 1.42e9, 2017: 1.50e9, 2016: 1.54e9, 2015: 1.59e9, 2014: 1.61e9, 2013: 1.39e9, 2012: 1.57e9, 2011: 1.54e9, 2010:1.50e9}
# Create a year column 
tesla['Year'] = tesla.index.year
# Take Dates from index and move to Date column 
tesla.reset_index(level=0, inplace = True)
tesla['cap'] = 0
# Calculate market cap for all years
for i, year in enumerate(tesla['Year']):
    # Retrieve the shares for the year
    shares = tesla_shares.get(year)


    # Update the cap column to shares times the price
    tesla.loc[i, 'cap'] = shares * tesla.loc[i, 'Adj. Close']

這為特斯拉創建了名為「cap」的列。我們對通用汽車數據進行同樣的處理,然后將兩者關聯(merge)。關聯實質上是數據科學工作流的一部分,因為它允許我們在共享列的基礎上合並不同的數據集。在這種情況下,該列是日期。我們進行「inner」關聯,只保存兩個數據框中有相同日期的數據行。

# Merge the two datasets and rename the columns
cars = gm.merge(tesla, how='inner', on='Date')
cars.rename(columns={'cap_x': 'gm_cap', 'cap_y': 'tesla_cap'}, inplace=True)
# Select only the relevant columns
cars = cars.ix[:, ['Date', 'gm_cap', 'tesla_cap']]
# Divide to get market cap in billions of dollars
cars['gm_cap'] = cars['gm_cap'] / 1e9
cars['tesla_cap'] = cars['tesla_cap'] / 1e9
cars.head()

市值的單位為十億美元。我們可以看到,開始時通用汽車的市場份額超過特斯拉 30 倍。隨着時間推移,事情會保持不變嗎?

我們觀察到特斯拉的急劇上升以及通用汽車在期間的小幅上漲。特斯拉在 2017 年甚至超過了通用汽車!

import numpy as np
# Find the first and last time Tesla was valued higher than GM
first_date = cars.loc[np.min(list(np.where(cars['tesla_cap'] > cars['gm_cap'])[0])), 'Date']
last_date = cars.loc[np.max(list(np.where(cars['tesla_cap'] > cars['gm_cap'])[0])), 'Date']
print("Tesla was valued higher than GM from {} to {}.".format(first_date.date(), last_date.date()))
Tesla was valued higher than GM from 2017-04-10 to 2017-09-21.

在此期間,特斯拉銷售約 4.8 萬輛汽車,而通用汽車售出 150 萬輛。即使銷售了 30 多倍汽車,通用汽車的價值仍低於特斯拉。這絕對顯示了有號召力的執行官和高質量的產品(如果極低產量)的力量。盡管特斯拉的價值現在低於通用汽車,但是一個好問題可能是,我們可以預測特斯拉再次超越通用汽車嗎?什么時候會發生?為此,我們轉向預測加法模型,預測未來。

用 Prophet 建模

Prophet 設計目的是用日常觀測數據分析時間序列,這些數據在不同尺度衡量下具有模式規律。它同時對建模節日效應的時間序列和添加人工變化點(changepoint)有出色的能力,但在本文中我們將僅運用基本功能來建模和運行。

我們首先引入 prophet,並將我們數據中的列重新命名為正確的格式。日期列必須被稱為「ds」,數值列被稱為「y」。在這里,數值列是市值。然后,我們創建 prophet 模型並傳入數據訓練,就像 Scikit-Learn 機器學習模型一樣:

import fbprophet
# Prophet requires columns ds (Date) and y (value)
gm = gm.rename(columns={'Date': 'ds', 'cap': 'y'})
# Put market cap in billions
gm['y'] = gm['y'] / 1e9
# Make the prophet model and fit on the data
gm_prophet = fbprophet.Prophet(changepoint_prior_scale=0.15)
gm_prophet.fit(gm)

創建 prophet 模型時,我將 changepoint 先驗設置為 0.15,高於默認值 0.05。這個超參數用於控制趨勢對變化的敏感程度,數值越高越敏感,數值越低越不敏感。這個數值用於權衡機器學習中最基本的一對統計量:偏差(bias)與方差(variance)。

過擬合和欠擬合都是我們不願看見的。

在創建一個 prophet 模型中,我們也可以指定變化點,如時間,當希望序列從上升到下降趨勢時,反之亦然;如節日,當希望影響時間序列時。如果我們不指定變化點,prophet 會為我們計算它們。

為了進行預測,我們需要用 prophet 模型創建所謂的用於預測的未來數據框。我們指定預測的未來時期區間(兩年)和預測的頻率(每天)。

# Make a future dataframe for 2 years
gm_forecast = gm_prophet.make_future_dataframe(periods=365 * 2, freq='D')
# Make predictions
gm_forecast = gm_prophet.predict(gm_forecast)

我們的未來數據框包含未來兩年特斯拉和通用汽車的估計市值。我們可以用 prophet 的繪圖函數來可視化預測。

gm_prophet.plot(gm_forecast, xlabel = 'Date', ylabel = 'Market Cap (billions $)')
plt.title('Market Cap of GM');

黑點代表實際值(注意實際值測量截止到 2018 年初),藍線表示預測值,淡藍色陰影區域表示不確定性(這是預測的關鍵部分)。未來時間距離越遠,不確定性區域越大,因為初始的不確定性隨着時間的推移而增長。在天氣預報中也觀察到這種情況,時間越遠天氣預報越不准確。

我們也可以檢查模型檢測出的 changepoints。重申一點,changepoints 代表的是當時間序列的增速有明顯變化的時候(例如從增到減)。

tesla_prophet.changepoints[:10]

61    2010-09-24
122   2010-12-21
182   2011-03-18
243   2011-06-15
304   2011-09-12
365   2011-12-07
425   2012-03-06
486   2012-06-01
547   2012-08-28
608   2012-11-27

我們可以對比一下這個時間段從谷歌搜索到的特斯拉趨勢看看結果是否一致。changepoints(垂直線)和搜索結果放在同一個圖中:

# 加載數據
tesla_search = pd.read_csv('data/tesla_search_terms.csv')

# 把月份轉換為 datetime
tesla_search['Month'] = pd.to_datetime(tesla_search['Month'])
tesla_changepoints = [str(date) for date in tesla_prophet.changepoints]
# 畫出搜索頻率
plt.plot(tesla_search['Month'], tesla_search['Search'], label = 'Searches')

# 畫 changepoints
plt.vlines(tesla_changepoints, ymin = 0, ymax= 100, colors = 'r', linewidth=0.6, linestyles = 'dashed', label = 'Changepoints')

# 整理繪圖
plt.grid('off'); plt.ylabel('Relative Search Freq'); plt.legend()
plt.title('Tesla Search Terms and Changepoints');

特斯拉市值的一些 changepoints 跟特斯拉搜索頻率的變化一致,但不是全部。我認為谷歌搜索頻率不能稱為股票變動的好指標。

我們依然需要知道特斯拉的市值什么時候會超越通用汽車。既然有了接下來兩年的預測,我們可以合並兩個數據框后在同一幅圖中畫出兩個公司的市值。合並之前,列需要更名方便追蹤。

gm_names = ['gm_%s' % column for column in gm_forecast.columns]
tesla_names = ['tesla_%s' % column for column in tesla_forecast.columns]

# 合並的數據框
merge_gm_forecast = gm_forecast.copy()
merge_tesla_forecast = tesla_forecast.copy()

# 更名列
merge_gm_forecast.columns = gm_names
merge_tesla_forecast.columns = tesla_names

# 合並兩組數據
forecast = pd.merge(merge_gm_forecast, merge_tesla_forecast, how = 'inner', left_on = 'gm_ds', right_on = 'tesla_ds')

# 日期列更名
forecast = forecast.rename(columns={'gm_ds': 'Date'}).drop('tesla_ds', axis=1)

首先我們會只畫估算值。估算值(prophet包的 “yhat”)除去一些數據中的噪音因而看着跟原始數據圖不太一樣。除雜的程度取決於 changepoint prior 的大小 - 高的 prior 值表示更多的模型靈活度和更多的高低起伏。

plt.figure(figsize=(10, 8))
plt.plot(forecast['Date'], forecast['gm_trend'], 'b-')
plt.plot(forecast['Date'], forecast['tesla_trend'], 'r-')
plt.legend(); plt.xlabel('Date'); plt.ylabel('Market Cap ($)')
plt.title('GM vs. Tesla Trend');

 

plt.figure(figsize=(10, 8))
plt.plot(forecast['Date'], forecast['gm_yhat'], 'b-')
plt.plot(forecast['Date'], forecast['tesla_yhat'], 'r-')
plt.legend(); plt.xlabel('Date'); plt.ylabel('Market Cap (billions $)')
plt.title('GM vs. Tesla Estimate');

overtake_date = min(forecast.ix[forecast['tesla_yhat'] > forecast['gm_yhat'], 'Date'])
print('Tesla overtakes GM on {}'.format(overtake_date))
#Tesla overtakes GM on 2018-02-03 00:00:00

當生成以上的圖像,我們遺漏了預測中最重要的一點:不確定性!

# Create subplots to set figure size
fig, ax = plt.subplots(1, 1, figsize=(10, 8));

# Plot estimate
ax.plot(forecast['Date'], forecast['gm_yhat'], label = 'gm prediction');

# Plot uncertainty values
ax.fill_between(forecast['Date'].dt.to_pydatetime(), forecast['gm_yhat_upper'], forecast['gm_yhat_lower'], alpha=0.6, edgecolor = 'k');

# Plot estimate and uncertainty for tesla
ax.plot(forecast['Date'], forecast['tesla_yhat'], 'r', label = 'tesla prediction');
ax.fill_between(forecast['Date'].dt.to_pydatetime(), forecast['tesla_yhat_upper'], forecast['tesla_yhat_lower'], alpha=0.6, edgecolor = 'k');
plt.legend();
plt.xlabel('Date'); plt.ylabel('Billions $'); plt.title('Market Cap Prediction for GM and Tesla');

這更好代表預測的結果。圖中顯示兩個公司預期會增長,特斯拉的增長速度會比通用更快。再強調一下,不確定性會隨着時間的推移而增加,而 2020 年特斯拉的下限比通用的上限高意味着通用可能會一直保持領先地位。

市值分析的最后一步是看整體趨勢和規律。預言家讓我們輕易地達到這個目的。

# 描繪趨勢和規律
gm_prophet.plot_components(gm_forecast)

這個趨勢非常明顯:通用汽車的股價正在上漲並將繼續上漲。年度模式很有意思,因為這似乎揭示了通用汽車的股價在年底會有所增長,但隨后會緩慢下滑直到夏季。因此,我們可以嘗試計算年度市值與通用汽車在此期間平均每月的銷售額之間是否存在相關關系。

對通用的歷年來每月銷售額的平均值做統計:

 年度成分市值統計:

 看起來月銷量與市值不相關。八月份的月銷售額是第二高的,但此時是市值的最低點!

而且,每周趨勢沒有如預期顯示出意義。經濟學中的隨機游走理論指出,股票價格每天都沒有可預測的模式。正如我們的分析所證明的那樣,長期來看,股票往往會上漲,但在每日來看,幾乎沒有我們可以利用的模式。(也就是說對股票的預測都是不靠譜的?)

道瓊斯工業平均指數(反映證券交易所 30 家最大公司的市場指數)很簡單地說明了這一點:

Prophet 也可以應用於更大規模的數據測量,如國內生產總值(衡量一個國家經濟總體規模)。我根據美國和中國的歷史 GDP 創建了 prophet 模型並做了以下預測。

總結

有很多方法來模擬時間序列,從簡單線性回歸到具有 LSTM 的循環神經網絡(recurrent neural network)。加法模型是有用的,因為它們可以快速開發和運行,可以解釋並預測不確定性。Prophet 的能力令人印象深刻,我們在這里只涉及到基本功能。我鼓勵你使用本文和 notebook 來探索 Quandl 提供的一些數據或者利用你自己的時間序列數據。作為探索時間序列的第一步,Python 中的加法模型是必經之路!

 

 

原文鏈接:https://towardsdatascience.com/time-series-analysis-in-python-an-introduction-70d5a5b1d52a

完整代碼和數據集:https://github.com/WillKoehrsen/Data-Analysis/blob/master/additive_models/Additive%20Models%20for%20Prediction.ipynb

參考鏈接:

1. https://cloud.tencent.com/developer/article/1119611

2. https://www.zhihu.com/question/21229371/answer/559468427


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM