手把手教你用Python的Prophet庫進行時間序列預測

資料派THU發表於2020-10-09

時間序列預測通常具有十足的挑戰性,這是由時間序列預測的方法眾多、且每種方法都包含很多不同的引數所造成的。

Prophet是一個專門為預測單變數時間序列資料集而設計的開源庫。如果你想要自動化地尋找一組好的模型引數,從而對擁有趨勢及季節性週期變化結構的資料做出有效預測,使用Prophet來處理是一件輕而易舉的事情——它本來就是為此而設計的。

在本教程中,你將去探索如何使用這個由Facebook開發的Prophet庫進行時間序列預測

完成這個教程後,你將會學到:

Prophet是一個由Facebook開發的開源庫,專為單變數時間序列資料的自動化預測而設計;

如何擬合Prophet模型,並使用模型進行樣本內及樣本外預測;

如何使用透過留出法所劃分出的不參與訓練的資料集來評估Prophet模型的效能。

那我們就開始吧。

教程概覽

本教程共有3個部分,它們分別是:

Prophet預測庫介紹

汽車銷量資料集

載入資料並進行統計描述

載入資料並進行圖表繪製

使用Prophet進行汽車銷量預測

擬合Prophet模型

進行樣本內預測

進行樣本外預測

手動對預測模型進行效能評估

Prophet預測庫介紹

Prophet,或稱“Facebook Prophet”,是一個由Facebook開發的用於單變數時間序列預測的開源庫。

Prophet實現的是一個可加的時間序列預測模型,支援趨勢、季節性週期變化及節假日效應。

“該模型所實現的是一個基於可加模型的時間序列資料預測過程,擬合了年度、周度、日度的季節性週期變化及節假日效應的非線性趨勢。”

— Package ‘prophet’, 2019.

Prophet的設計初衷就是簡單易用、完全自動,因此適合在公司內部場景中使用,例如預測銷量、產能等。 

這裡有一篇不錯的概覽,介紹了Prophet及它的功能:

Prophet: forecasting at scale, 2017

https://research.fb.com/blog/2017/02/prophet-forecasting-at-scale/

這個庫的介面在R和Python中均可被呼叫,本篇將會聚焦於Python中的使用方法。 

第一步是使用Pip對Prophet庫進行安裝,操作如下:

sudo pip install fbprophet

接下來,我們需要確認Prophet庫已經被正確安裝。

我們可以在Python中匯入該庫並列印它的版本號。完整的例子見下方:

# check prophet version
import fbprophet
# print version number
print('Prophet %s' % fbprophet.__version__)

執行上述例子並列印Prophet庫的版本號。你應該安裝的是如下或更高的版本。

Prophet 0.5

現在我們已經安裝好了Prophet,接下來就選擇一個資料集並使用這個庫來進行探索。

汽車銷量資料集

我們將會使用汽車月度銷量資料集。 

這是一個標準的單變數時間序列資料集,同時包含趨勢及季節性週期變化。它包含108個月的汽車銷量資料,使用基準模型對其進行預測便能達到3235(輛汽車)的平均絕對誤差,從而提供了較低的誤差限制。 

無需下載資料集,我們會在每個例子中自動下載它。 

Monthly Car Sales Dataset (csv)

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv

Monthly Car Sales Dataset Description

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.names

1. 載入資料並進行統計描述

首先,讓我們來載入資料並且對它進行統計描述。

Prophet要求輸入的資料為Pandas DataFrames的形式。所以我們要用Pandas庫進行資料載入和統計描述。

我們可以透過呼叫Pandas庫中的read_csv()函式,從而直接透過URL載入資料。接下來我們可以對資料集的行數和列數進行統計,並檢視一下前幾行資料。

完整的例子如下: 

# load the car sales dataset
from pandas import read_csv
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# summarize shape
print(df.shape)
# show first few rows
print(df.head())

執行示例程式碼,我們將會得到資料集的行數和列數,以及前5行資料。 

正如我們預期的一樣,資料集包含108行(分別代表108個月)及2列(欄位)的資料。第一列是日期,第二列是銷量。 

需要注意的是,輸出中的第一列所顯示的行標(index)並不是原始資料集中的一部分,而是Pandas中對資料行進行排列時使用的一個頗有幫助的工具而已。

(108, 2)
     Month  Sales
0  1960-01   6550
1  1960-02   8728
2  1960-03  12026
3  1960-04  14395
4  1960-05  14587

2. 載入資料並繪製圖表

一個時間序列資料集只有被繪製出來後才會有意義。

繪製時間序列能夠讓我們觀察到趨勢、季節性週期、異常波動等變化是否真的存在。它能帶給我們一些對資料的“感覺”。 

我們可以呼叫Pandas庫中的plot()函式輕鬆地對DataFrame進行繪製。 

完整的示例見下方: 

# load and plot the car sales dataset
from pandas import read_csv
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# plot the time series
df.plot()
pyplot.show()

執行示例程式碼,我們能夠得到一張顯示時間序列的圖。 

我們能夠清晰地觀察到銷量隨時間變化的趨勢以及月度週期變化規律。這些都是我們希望預測模型能夠考慮在內的規律。

 現在我們已經熟悉了這一資料集,那麼就來探索一下如何使用Prophet庫進行預測吧。

使用Prophet進行汽車銷量預測

在這一部分中,我們將會探索如何使用Prophet進行汽車銷量資料預測。

讓我們從將資料擬合成模型開始吧。

1. 擬合Prophet模型 

想要使用Prophet進行預測,首先我們需要定義和配置一個Prophet()物件,然後透過呼叫fit()函式並將資料傳入該函式,從而對資料集進行擬合。

Prophet()物件會使用所傳入的引數來配置你想要的模型,例如增長和季節性週期等變化的型別。預設情況下,模型幾乎會自動找出所有的內容。 

fit()函式接受時間序列資料以DataFrame的形式被傳入,同時對這個DataFrame也有特殊的格式要求:第一列必須被命名為“ds”幷包含日期資訊;第二列必須被命名為“y”幷包含觀測結果。 

這就意味著我們需要修改原資料集中的列名,同時把第一列轉為日期時間物件(date-time objects)——前提是如果你沒有事先做好這一步的話(可以在呼叫read_csv函式時透過輸入正確的引數來完成這個操作)。 

舉例來說,我們可以把已載入的汽車銷量資料集修改成自己想要的樣式,如下所示:

...
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])

 關於如何將汽車銷量資料集擬合成一個Prophet模型,完整的示例如下:

 # fit prophet model on the car sales dataset
from pandas import read_csv
from pandas import to_datetime
from fbprophet import Prophet
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)

執行示例程式碼,載入資料集,將DataFrame調整成需要的格式,並擬合出一個Prophet模型。

預設情況下,這個庫會輸出擬合過程中所產生的大量結果資訊——我通常覺得這樣不是很好,因為這容易讓開發者忽略輸出中那些真正重要的資訊。

但不管怎麼說,輸出資訊還是總結了模型擬合過程中發生的情況,尤其是執行的最佳化過程。

INFO:fbprophet:Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.
INFO:fbprophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.
Initial log joint probability = -4.39613
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
      99       270.121    0.00413718       75.7289           1           1      120
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     179       270.265    0.00019681       84.1622   2.169e-06       0.001      273  LS failed, Hessian reset
     199       270.283   1.38947e-05       87.8642      0.3402           1      299
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     240       270.296    1.6343e-05       89.9117   1.953e-07       0.001      381  LS failed, Hessian reset
     299         270.3   4.73573e-08       74.9719      0.3914           1      455
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     300         270.3   8.25604e-09       74.4478      0.3522      0.3522      456
Optimization terminated normally:
  Convergence detected: absolute parameter change was below tolerance

在接下來的部分中,我就不再展示如上輸出了。那麼我們就開始預測吧。

2. 進行樣本內預測

對歷史資料進行預測可能是有用的。 

也就是說,我們可以對那些被當作訓練模型時的輸入資料進行預測。理想情況下,模型之前就已經見過了這些資料從而能做出完美的預測。 

然而,情況並非如此,因為模型在試圖對資料中的所有情況進行歸納總結。 

這叫做樣本內(訓練集的樣本內)預測,透過觀察它的結果我們能夠得知模型的效能如何——模型對訓練資料的學習效果如何。 

透過呼叫predict()函式並傳入一個DataFrame就可以進行預測了,該DataFrame包含一個名為“ds”的列及所有待預測日期時間的行。 

建立預測DataFrame有很多種方式。在這裡,我們迴圈一年中的所有日期(即資料集中的最後12個月),併為每一個月建立一個字串。接下來我們把這個日期列表轉為DataFrame,並把字串轉為日期時間物件。 

...
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])

這樣我們就有了可以作為predict()函式所需的引數被傳入的DataFrame,然後進行預測計算。

Predict()函式的計算結果是一個包含多個列的DataFrame,其中最重要的列或許是被預測的日期時間(“ds”列)、預測值(“yhat”列)以及預測值的上下限(“yhat_lower”列和“yhat_upper”列)——為預測的不確定性提供區間估計。 

如下方示例,我們可以列印出預測結果的前幾行:

...
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())

Prophet同樣提供了一個內建工具,用於對訓練資料預測結果進行視覺化。

對模型呼叫plot()函式並傳入預測結果DataFrame即可實現。訓練資料集的圖將會被繪製出來,被預測日期的預測值及其上下限也會被展示在圖中。

...
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

彙總以上程式碼,一個樣本內預測的完整示例如下:

# make an in-sample forecast
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

執行示例程式碼,我們將得到資料集最後12個月的預測值。

前5個月的預測如下,我們能夠觀察到這些預測值和原資料集中的真實值相差並不大。

          ds          yhat    yhat_lower    yhat_upper
0 1968-01-01  14364.866157  12816.266184  15956.555409
1 1968-02-01  14940.687225  13299.473640  16463.811658
2 1968-03-01  20858.282598  19439.403787  22345.747821
3 1968-04-01  22893.610396  21417.399440  24454.642588
4 1968-05-01  24212.079727  22667.146433  25816.191457

接下來是繪製一個結果圖,我們可以觀察到訓練資料被使用黑色圓點顯示在圖中,預測值被使用藍線顯示,預測值的上下限為藍色陰影區域。

手把手教你用Python的Prophet庫進行時間序列預測3. 進行樣本外預測

在實踐中,我們往往是想構建一個預測模型來對訓練資料以外的情況進行預測。這被稱為樣本外預測。

我們可以透過和進行樣本內預測時同樣的方法來實現這一目標,只要指定一段不同的預測期間即可。 

在本例中,訓練資料集以外的日期區間從1969-01開始。 

...
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1969-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])

彙總以上程式碼,一個樣本外預測的完整示例如下:

# make an out-of-sample forecast
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1969-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

執行示例程式碼,我們將得到汽車銷售量樣本外資料的預測值。

下方是前5行預測的列印結果,可是我們很難知道它們是不是合理的預測值。

          ds          yhat    yhat_lower    yhat_upper
0 1969-01-01  15406.401318  13751.534121  16789.969780
1 1969-02-01  16165.737458  14486.887740  17634.953132
2 1969-03-01  21384.120631  19738.950363  22926.857539
3 1969-04-01  23512.464086  21939.204670  25105.341478
4 1969-05-01  25026.039276  23544.081762  26718.820580

繪製一張圖能幫助我們評估由訓練資料得到的預測值是否合理。

至少從肉眼上來看,我們對下一年(1969年)的預測還是比較合理的。

手把手教你用Python的Prophet庫進行時間序列預測

4. 手動對預測模型進行效能評估

對預測模型的效能進行客觀評估至關重要。

這一目標可以透過留出一部分資料不參與模型訓練來實現,例如最後12個月的資料。接下來,我們就可以用一部分的資料對模型進行擬合,然後對事先預留不參與訓練的資料進行預測,並計算誤差度量,例如預測中的平均絕對誤差——這是模擬出的樣本外預測過程。 

這個誤差度量的值能夠幫助我們評估模型在進行樣本外預測時的表現水準。

我們可以透過建立一個在原資料集基礎上去除最後12個月資料的新DataFrame來實現這一過程。

...
# create test dataset, remove last 12 months
train = df.drop(df.index[-12:])
print(train.tail())

然後對最後12個月進行預測。

我們可以提取出預測值和來自原始資料集中的期望值(真實值),使用scikit-learn庫計算它們之間的平均絕對誤差度量。 

...
# calculate MAE between expected and predicted values for december
y_true = df['y'][-12:].values
y_pred = forecast['yhat'].values
mae = mean_absolute_error(y_true, y_pred)
print('MAE: %.3f' % mae)

 同樣的,如果把期望值(真實值)和預測值繪製在一張圖中,它會幫助我們瞭解樣本外預測和已知真實值之間的匹配程度。

...
# plot expected vs actual
pyplot.plot(y_true, label='Actual')
pyplot.plot(y_pred, label='Predicted')
pyplot.legend()
pyplot.show()

彙總以上程式碼,以下示例演示瞭如何使用留出集來評估一個Prophet模型的效能。

# evaluate prophet time series forecasting model on hold out dataset
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from sklearn.metrics import mean_absolute_error
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# create test dataset, remove last 12 months
train = df.drop(df.index[-12:])
print(train.tail())
# define the model
model = Prophet()
# fit the model
model.fit(train)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds'] = to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# calculate MAE between expected and predicted values for december
y_true = df['y'][-12:].values
y_pred = forecast['yhat'].values
mae = mean_absolute_error(y_true, y_pred)
print('MAE: %.3f' % mae)
# plot expected vs actual
pyplot.plot(y_true, label='Actual')
pyplot.plot(y_pred, label='Predicted')
pyplot.legend()
pyplot.show()

執行示例程式碼,我們能看到訓練資料集的後幾行。

這就確認了模型訓練過程止於1967年的最後一個月,而1968整年的資料被用作了留出集。

           ds      y
91 1967-08-01  13434
92 1967-09-01  13598
93 1967-10-01  17187
94 1967-11-01  16119
95 1967-12-01  13713

接下來,我們來計算預測日期區間的絕對平均誤差。

在本例中,我們可以看到誤差大約為1336輛(汽車),與對同一日期區間的銷售量進行預測基準模型的3235輛(汽車)相比,我們所訓練出的模型誤差更低,既表現更好。

MAE: 1336.814

最後,我們來繪製一張真實值vs預測值的對比圖。在本例中,我們能觀察到預測結果很好地擬合了真實情況。模型表現得不錯,給出的預測也比較合理。

手把手教你用Python的Prophet庫進行時間序列預測

Prophet庫同樣提供了一些能夠評估模型效能及繪製預測結果的自動化工具,儘管它們在本例的資料上並不是很有效。

更多閱讀

如果你想對本文主題做更深入的瞭解,這裡有更多資料可供學習參考:

Prophet Homepage

https://facebook.github.io/prophet/

Prophet GitHub Project

https://github.com/facebook/prophet

Prophet API Documentation

https://facebook.github.io/prophet/docs/

Prophet: forecasting at scale, 2017

https://research.fb.com/blog/2017/02/prophet-forecasting-at-scale/

Forecasting at scale, 2017

https://peerj.com/preprints/3190/

Car Sales Dataset

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv

Package ‘prophet’, R Documentation

https://cran.r-project.org/web/packages/prophet/prophet.pdf

總結

在本教程中,你將探索如何使用這個由Facebook開發的Prophet庫進行時間序列預測

完成這個教程後,你將會學到:

Prophet是一個由Facebook開發的開源庫,專為單變數時間序列資料的自動化預測而設計;

如何擬合Prophet模型,並使用模型進行樣本內及樣本外預測;

如何使用透過留出法所劃分出的不參與訓練的資料集來評估Prophet模型的效能。

原文標題:

Time Series Forecasting With Prophet in Python

原文連結:

https://machinelearningmastery.com/time-series-forecasting-with-prophet-in-python/

譯者簡介

殷之涵(Jane),研究生畢業於康奈爾大學生物統計與資料科學專業,本科畢業於普渡大學精算與應用統計專業。目前在騰訊擔任資料科學家,主要負責騰訊影片使用者增長&市場營銷資料科學方面的工作;此前在京東資料分析師一年半,負責透過指標體系搭建、統計分析、資料探勘機器學習建模來驅動決策、制定並落地億級使用者的精細化運營策略。對資料科學充滿興趣和熱情,希望透過多年勤懇深耕成長為真正的領域專家。


相關文章