時間序列分析平穩數據(單變量時間序列平滑方法介紹)
2023-09-23 01:39:12
間序列是由按時間排序的觀察單位組成的數據。 可能是天氣數據、股市數據。,也就是說它是由按時間排序的觀察值組成的數據。
在本文中將介紹和解釋時間序列的平滑方法,時間序列統計方法在另一篇文章中進行了解釋。本文將解釋以下 4 個結構概念:
1、穩態(Stationary)
穩態是指系統的狀態不再隨時間發生改變的一種狀態。換句話說,如果一個時間序列的均值、方差和協方差隨時間保持不變,則該序列被稱為平穩的。
為什麼穩態很重要呢?:理論上有一種解釋,即時間序列的結構在一定的平穩性下,即在一定的模式下,更容易預測。也就是說,如果是平穩的,則運動下一步也是可以預測的。因此通常會有一個期望:時間序列是平穩的嗎?如果它是平穩的,我們可以更容易地做出預測。為了捕捉這一點,可以通過查看時間序列圖像而不是統計測試來了解這一點。
2、趨勢(Trend)
它是一個時間序列的長期增減結構。如果存在趨勢,則序列不太可能是平穩的,因為周期的統計數據(平均值、標準偏差等)將以增加或減少的趨勢變化。
3、季節性(Seasonality)
季節性是指一個時間序列以一定的間隔重複某種行為。
4、周期(Cycle)
它包含類似於季節性的重複模式,但是這兩個問題可能會相互混淆。季節性可以映射到特定的時間段。它與日、周、年、季等時間段重疊。例如,市場在周末有更多的生意,或者一個產品在冬天更受關注等等。
周期性發生在更長的時間,更不確定的結構,以不與日、周等結構重疊的方式發生。它的發生主要是出於結構性原因,並具有周期性變化。例如,一些促銷活動雖然這不是完全季節性的,它是在一定時期內發生的,但具體會在什麼時期發生,會根據不同營銷策略來決定。
理解時間序列模型的本質:我們已經看到了上面時間序列的基本結構。時間序列的假設是:時間序列在t時間段內的值受前一個時間段(t-1)的值影響最大。例如今天是星期天,它前面的值最能解釋星期天時間序列的值。有了這些基礎知識,我們可以開始進行平滑方法的介紹
單變量的平滑方法1、單指數平滑法(Simple Exponential Smoothing - SES)
它只在平穩的時間序列中表現良好,因為它要求序列中不應該有趨勢和季節性。 它可以對Level(水平)進行建模(Level可以認為是序列的平均值)。 過去的影響是在「未來與最近的過去更相關」的假設上進行加權。 SES適用於沒有趨勢和季節性的單變量時間序列,它在平穩序列中是最成功的。
2、雙指數平滑法( Double Exponential Smoothing - DES)
它在SES的基礎上增加了趨勢的判斷。所以它適用於具有和不具有季節性的單變量時間序列。
3、三重指數平滑(TES — Holt-Winters):
它是目前最先進的平滑方法。 該方法通過動態評估Level(水平)、趨勢和季節性的影響來進行預測。 它可以用於具有趨勢和/或季節性的單變量序列。
平滑方法使用樣例我們這裡將使用來自 sm 模塊的數據集。 它根據時間顯示夏威夷大氣中的二氧化碳。記錄期間:1958 年 3 月 — 2001 年 12 月
import itertoolsimport warningsimport matplotlib.pyplot as pltimport numpy as npimport pandas as pdimport statsmodels.api as smfrom sklearn.metrics import mean_absolute_errorfrom statsmodels.tsa.holtwinters import ExponentialSmoothingfrom statsmodels.tsa.holtwinters import SimpleExpSmoothingfrom statsmodels.tsa.seasonal import seasonal_decompose# to split the time series into componentsimport statsmodels.tsa.api as smtwarnings.filterwarnings('ignore')data = sm.datasets.co2.load_pandas# A dataset from the sm moduley = data.data# target varibley
索引為日期, 通過查看日期,可以確定這個數據集是每周一次!如果需要查看每月數據則需要進行轉換。
y = y['co2'].resample('MS').meany.isnull.sum
數據中有缺失值。 但是時間序列中的缺失值無法用平均值,中位數填充。所以我們這裡用在它之前或之後的值填充,它也可以通過平均它之前和之後的值來填充。
y = y.fillna(y.bfill)y.isnull.sum # output => 0y.plot(figsize=(10, 7))plt.show
可以看到在這個序列中有一個趨勢,也包含了一個很明顯的季節性。
我們這裡使用 Hold-out 方法是因為模型在訓練模型時往往會過度擬合,無論是時間序列還是深度學習方法。 因此,我們應該使用一些方法來防止這種情況並更準確地評估錯誤並驗證模型。 這就是我們將數據集拆分為訓練測試的原因。
train = y[:'1997-12-01']print("Lenght of train", len(train)) # 478 monthstest = y['1998-01-01':]print("Lenght of test", len(test)) # 48 months
1、SES
SES = Level (Unsuccessful if Trend and Seasonality).
這裡有一個「smoothing_level」參數。 如果沒有輸入smoothing_level,方法也不會報錯出錯。這是因為後面還會有更多的參數,所以這裡先暫時設定一個值
ses_model = SimpleExpSmoothing(train).fit(smoothing_level=0.5)y_pred = ses_model.forecast(48)
可視化函數
def plot_co2(train, test, y_pred, title):mae = mean_absolute_error(test, y_pred)train["1985":].plot(legend=True,, title=f"{title}, MAE: {round(mae,2)}")test.plot(legend=True,, figsize=(6, 4))y_pred.plot(legend=True,)plt.showplot_co2(train, test, y_pred, "Single Exponential Smoothing")
可以看到SES預測的並不好,這是因為我們的序列中包含了季節性相關的信息,我們再做一個微調
def ses_optimizer(train, alphas, step=48): # step for length of test setbest_alpha, best_mae = None, float("inf")for alpha in alphas:ses_model = SimpleExpSmoothing(train).fit(smoothing_level=alpha)y_pred = ses_model.forecast(step)mae = mean_absolute_error(test, y_pred)if mae 加y(t) = Level *Trend * Seasonality * Noise => 乘
在乘法運算中,結構的變化更具依賴性。如果季節性和殘差與趨勢無關,則該級數是可加的。如果季節性和殘差根據趨勢形成,則是相乘的。
季節性和殘差隨機分布在0附近。所以可以確定趨勢並沒有影響殘差,所以這個我們確定這個級數是加性的。
通常我們應該建立兩個模型,並決定使用有較低的誤差的模型。但是在這裡確認殘差和季節性與趨勢無關。所以直接使用「add」參數。
des_model = ExponentialSmoothing(train,).fit(smoothing_level=0.5, smoothing_trend=0.5)y_pred = des_model.forecast(48)plot_co2(train, test, y_pred, "Double Exponential Smoothing")
雖然結果看起來不錯,已經捕捉到了趨勢,但由於缺乏季節性,這個估計也不太好,我們繼續優化
def des_optimizer(train, alphas, betas, step=48):best_alpha, best_beta, best_mae = None, None, float("inf")for alpha in alphas:for beta in betas:des_model = ExponentialSmoothing(train,).fit(smoothing_level=alpha, smoothing_slope=beta)y_pred = des_model.forecast(step)mae = mean_absolute_error(test, y_pred)if mae < best_mae:best_alpha, best_beta, best_mae = alpha, beta, maeprint("alpha:", round(alpha, 2), "beta:", round(beta, 2), "mae:", round(mae, 4))print("best_alpha:", round(best_alpha, 2), "best_beta:", round(best_beta, 2), "best_mae:", round(best_mae, 4))return best_alpha, best_beta, best_maealphas = np.arange(0.01, 1, 0.10)betas = np.arange(0.01, 1, 0.10)best_alpha, best_beta, best_mae = des_optimizer(train, alphas, betas)
最終的模型參數:
final_des_model = ExponentialSmoothing(train,).fit(smoothing_level=best_alpha, smoothing_slope=best_beta)y_pred = final_des_model.forecast(48)plot_co2(train, test, y_pred, "Double Exponential Smoothing")
MAE小了很多,趨勢也很平穩,但是沒有季節性
3、TES
TES = SES DES Seasonality
當我們輸入趨勢和季節性作為參數時,將使用 TES。
tes_model = ExponentialSmoothing(train,trend="add",seasonal="add",seasonal_periods=12).fit(smoothing_level=0.5,smoothing_slope=0.5,smoothing_seasonal=0.5)
我們把季節性周期 設為12,因為數據集中的年份是由月份決定的。也就是說我們確定季節性的周期是12個月(1年)
y_pred = tes_model.forecast(48)plot_co2(train, test, y_pred, "Triple Exponential Smoothing")
可以看到,趨勢和季節性都有了,我們繼續優化
alphas = betas = gammas = np.arange(0.20, 1, 0.10)
這裡有包含了3個超參數:
abg = list(itertools.product(alphas, betas, gammas))def tes_optimizer(train, abg, step=48):best_alpha, best_beta, best_gamma, best_mae = None, None, None, float("inf")for comb in abg:tes_model = ExponentialSmoothing(train,,, seasonal_periods=12).\fit(smoothing_level=comb[0], smoothing_slope=comb[1], smoothing_seasonal=comb[2])# her satırın 0., 1., 2. elemanlarını seç ve model kury_pred = tes_model.forecast(step)mae = mean_absolute_error(test, y_pred)if mae < best_mae:best_alpha, best_beta, best_gamma, best_mae = comb[0], comb[1], comb[2], mae# print([round(comb[0], 2), round(comb[1], 2), round(comb[2], 2), round(mae, 2)])print("best_alpha:", round(best_alpha, 2), "best_beta:", round(best_beta, 2), "best_gamma:", round(best_gamma, 2),"best_mae:", round(best_mae, 4))return best_alpha, best_beta, best_gamma, best_maebest_alpha, best_beta, best_gamma, best_mae = tes_optimizer(train, abg)# best_alpha: 0.8 best_beta: 0.5 best_gamma: 0.7 best_mae: 0.606
上面給出的參數 smoothing_slope 與 smoothing_trend 相同。 此處使用 smoothing_trend 來表明它們是相同的,看看結果:
final_tes_model = ExponentialSmoothing(train,,, seasonal_periods=12).\fit(smoothing_level=best_alpha, smoothing_trend=best_beta, smoothing_seasonal=best_gamma)y_pred = final_tes_model.forecast(48)plot_co2(train, test, y_pred, "Triple Exponential Smoothing")
無論從MAE還是可視化的結果看,都很不錯,對吧。
總結以上就是三個平滑方法的介紹
作者:Furkan Akdağ
,