有時需要從時序數據中刪除趨勢,為下一步或數據清理過程的一部分做準備。如果您可以確定趨勢,那麼只需從數據中減去它,結果就是非趨勢數據。
如果趨勢是線性的,你可以通過線性回歸找到它。但如果趨勢不是線性的呢?我們一會兒就會看到我們能做些什麼。
但是在此之前,我們先看看什麼叫線性趨勢
線性趨勢
下面是帶有趨勢的時序數據:
raw.githubusercontent.com/FlorinAndrei/misc/master/qdata.csv
讓我們加載它,看看它是什麼樣子:
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.linear_model import LinearRegressionfrom sklearn.preprocessing import PolynomialFeaturesfrom sklearn.metrics import mean_squared_error, r2_scoreser = pd.read_csv('qdata.csv', index_col=0, squeeze=True)serx0 473.9177641 75.3248252 -306.9694793 53.2714764 372.966686... 95 4650.55047396 4604.57334497 4891.70463898 5265.94816299 5618.909339Name: y, Length: 100, dtype: float64plt.plot(ser)plt.show()
好的,這裡有一個趨勢。我們假設它是線性的,我們來做線性回歸來找出答案。這是線性回歸的一個直接應用。上面導入的sklearn庫擁有我們進行回歸所需要的一切。
X = ser.indexX = np.reshape(X, (len(X), 1))y = ser.valuesmodel = LinearRegression()model.fit(X, y)trend = model.predict(X)plt.plot(y)plt.plot(trend)plt.legend(['data', 'trend'])plt.show()
看起來很合適,但可能不是很合適。讓我們從數據中減去趨勢,看看非趨勢數據是什麼樣的:
detr = [y[i] - trend[i] for i in range(0, len(y))]plt.plot(detr)plt.title('data detrended in a linear fashion')plt.show()
不是很令人信服。數據中仍有一個凹的趨勢。最初的趨勢可能不是線性的。
讓我們計算數據和我們提取的趨勢之間的RMSE和R。
r2 = r2_score(y, trend)rmse = np.sqrt(mean_squared_error(y, trend))print('r2:', r2)print('rmse', rmse)r2: 0.8782399672701933rmse 553.6078593008505
多項式趨勢
如果趨勢不是線性的,我們可以嘗試用多項式曲線來擬合它。但問題是:即使我們擬合的曲線是高次多項式,我們仍然可以用線性回歸來找到它。
考慮這個二次表達式:
y = a + bx + cx
我們要找的值是a, b, c,和他們都是線性的。忘記x的權重,我們看的是權重,b和c,所以線性回歸——它只是發生,我們將不得不在多個維度做線性回歸。
假設數據呈二次趨勢。然後我們需要把X變換成二次形式:
pf = PolynomialFeatures(degree=2)Xp = pf.fit_transform(X)Xp
array([[1.000e+00, 0.000e+00, 0.000e+00],[1.000e+00, 1.000e+00, 1.000e+00], [1.000e+00, 2.000e+00, 4.000e+00], [1.000e+00, 3.000e+00, 9.000e+00], [1.000e+00, 4.000e+00, 1.600e+01], [1.000e+00, 5.000e+00, 2.500e+01], [1.000e+00, 6.000e+00, 3.600e+01],... [1.000e+00, 9.600e+01, 9.216e+03], [1.000e+00, 9.700e+01, 9.409e+03], [1.000e+00, 9.800e+01, 9.604e+03], [1.000e+00, 9.900e+01, 9.801e+03]])
第一列是X的0次方。第二列是X,第三列是X的2次方。這就像上面顯示的二次表達式(y = a + bx + cx)
現在我們將使用二次形式來擬合數據並生成二次趨勢。用線性回歸方法求出二次表達式的參數。
md2 = LinearRegression()md2.fit(Xp, y)trendp = md2.predict(Xp)
趨勢是怎樣的?
plt.plot(X, y)plt.plot(X, trendp)plt.legend(['data', 'polynomial trend'])plt.show()
更接近了,不是嗎?現在讓我們看看非趨勢數據:
detrpoly = [y[i] - trendp[i] for i in range(0, len(y))]plt.plot(X, detrpoly)plt.title('polynomially detrended data')plt.show()
這顯然更好。沒有任何可以從視覺上看出的趨勢。但是讓我們看看數字是怎麼說的:
r2 = r2_score(y, trendp)rmse = np.sqrt(mean_squared_error(y, trendp))print('r2:', r2)print('rmse', rmse)r2: 0.9343217231542871rmse 406.5937924291518
與線性趨勢相比,隨著多項式趨勢,R曲線增大,RMSE減小。兩者都是好的改變。兩種均值多項式的擬合效果都優於線性擬合。
高階多項式
你可以選擇任意階的多項式只要在這裡給N賦不同的值:
pf = PolynomialFeatures(degree=N)
一般來說,對N使用較低的值。如果增加了N,發生的情況不太嚴重,則返回較小的值。
只有一個彎曲的曲線可以用二次函數來描述。有兩個彎的曲線可以用三次函數來描述。等等。N-1彎需要一個N次冪的表達式。
如果N增加很多,最終你的「最佳擬合」曲線將開始跟隨數據中的雜音,而不是擬合趨勢。你已經超擬合了曲線,現在沒有意義了。或者減少N,或者增加更多數據點。
這樣我們將這個線性模型的數據去除(差值),使用剩餘的數據進行時間序列的訓練,可以得到更精確的結果