特徵工程之處理時間序列數據

維基百科對於特徵工程的定義是:利用相關領域知識,經過數據挖掘技術從原始數據中提取特徵的過程。這些特徵能夠用來提升機器學習算法的性能。python

不過,特徵工程不必定非得很花哨。特徵工程的一個簡單但廣泛的處理對象是時間序列數據。特徵工程在這個領域的重要性是由於(原始)時間序列數據一般只包含一個表示時間屬性的列,即日期時間(或時間戳)。算法

對於日期時間數據,特徵工程能夠看做是從獨立的(不一樣的)特徵數據中提取有用的信息。例如,從「2020–07–01 10:21:05」這日期時間數據中,咱們可能須要從中提取如下特徵:微信

  1. 月份:7app

  2. 本月第幾日:1機器學習

  3. 周幾:週三(經過2020-07-01判斷獲得)ide

  4. 時刻:10:21:05函數

從日期時間數據中提取這類特徵正是本文的目標。以後,咱們將結合咱們的工程實際中的特徵數據,將其做爲預測因子,而且創建一個gradient boosting 迴歸預測模型。具體來講,咱們將預測地鐵州際交通量。工具

本文目錄

本文主要包含如下內容:性能

詳細闡述如何從時間日期數據中提取如下特徵數據:學習

  1. 月份

  2. 時間數據處於每個月第幾日

  3. 周幾

  4. 時間

  5. 時段分類(早上、下午等)

  6. 週末標記(若是是週末則添加標記1,不然添加標記0)

如何將上述特種數據用於搭建Gradient Boosting 迴歸模型,而且實現對於地鐵州際交通量的預測

數據狀況

在本文中,咱們使用地鐵州際交通量數據集,它能夠在UCI機器學習庫中找到(https://archive.ics.uci.edu/ml/datasets/Metro+Interstate+Traffic+Volume)。該數據集是明尼蘇達州聖保羅州明尼阿波利斯市I-94的每小時交通量,其中包括2012-2018年的天氣和假日數據。這48204行數據包含如下屬性:

  1. holiday:類型數據,包含美國國家法定假日、區域假日、明尼蘇達州博覽會等

  2. temp:數值型數據,平均溫度(開爾文)

  3. rain_1h:數值型數據,每小時降雨(毫米)

  4. snow_1h:數值型數據,每小時降雪(毫米)

  5. clouds_all:數值型數據,雲層狀況(百分比)

  6. weather_main:類型數據,當前天氣的分類描述(簡要)

  7. weather_description:類型數據,當前天氣的分類描述(詳細)

  8. data_time:時間序列數據

  9. traffic_volume:數值型數據,每小時I-94 ATR 301記錄的西行交通量(本文預測目標)

接下來,咱們首先載入數據:

# import libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# load the data
raw = pd.read_csv('Metro_Interstate_Traffic_Volume.csv')
# display first five rows
raw.head()
# display details for each column
raw.info()

raw.head()

raw.info()

查看info信息,咱們發現data_time這一類目是object類型,因此咱們須要將其轉化爲datetime類型:

# convert date_time column to datetime type
raw.date_time = pd.to_datetime(raw.date_time)

特徵工程

從上面的info方法的輸出中,咱們知道除了date_time列以外還有其餘的分類特徵。可是因爲本文的主要主題是處理時間序列數據,咱們將重點關注針對date_time的特性工程。

Month

Pandas自身有許多易於使用的方法來處理datetime類型的數據。要提取時間/日期信息,咱們只需調用pd.Series.dtpd.Series.dt.month是提取month信息所需的函數。這將產生一系列int64格式的月份數字(例如1表明1月,10表明10月)。

# extract month feature
months = raw.date_time.dt.month

Day of month

Month相似,咱們只須要調用pd.Series.dt.day函數。以2012-10-27 09:00:00爲例,調用該函數提取結果爲27。

# extract day of month feature
day_of_months = raw.date_time.dt.day

Hour

相似地,pd.Series.dt.hour將生產對應的小時信息數據(範圍爲0-23的整數)。

# extract hour feature
hours = raw.date_time.dt.hour

Day name

獲取Day name的方式和上面幾個數據有所不一樣。咱們想要肯定raw.date_time序列中關於星期幾的信息,須要如下兩個步驟。首先,經過pd.Series.dt.day_name()生成day name序列。而後,咱們須要經過pd.get_dummies()進行獨熱編碼(one-hot encode)。

# first: extract the day name literal
to_one_hot = raw.date_time.dt.day_name()
# second: one hot encode to 7 columns
days = pd.get_dummies(to_one_hot)
#display data
days

獨熱編碼後的Day name信息

Daypart

在本部分中,咱們將基於Hour數據建立一個分組。咱們但願有六個小組表明每一天的各個部分。它們是黎明(02.00-05.59)、上午(06.00-09.59)、中午(10.00-13.59)、下午(14.00-17.59)、晚上(18.00-21.59)和午夜(22.00-第二天01.59)。

爲此,咱們建立了一個標識函數,稍後將使用該函數來做爲數據系列的apply方法。而後,咱們對獲得的dayparts執行一個熱編碼。

# daypart function
def daypart(hour):
   if hour in [2,3,4,5]:
       return "dawn"
   elif hour in [6,7,8,9]:
       return "morning"
   elif hour in [10,11,12,13]:
       return "noon"
   elif hour in [14,15,16,17]:
       return "afternoon"
   elif hour in [18,19,20,21]:
       return "evening"
   else: return "midnight"
# utilize it along with apply method
raw_dayparts = hours.apply(daypart)
# one hot encoding
dayparts = pd.get_dummies(raw_dayparts)
# re-arrange columns for convenience
dayparts = dayparts[['dawn','morning','noon','afternoon','evening','midnight']]
#display data
dayparts

獨熱編碼後的Day parts信息

Weekend flag

咱們從date_time時間序列數據中提取的最後一個特徵是is_weekend。這一特徵指示給定的日期時間是否在週末(星期六或星期日)。爲了實現這一目標,咱們將利用pd.Series.dt.day_name()方法以及lambda函數。

# is_weekend flag
day_names = raw.date_time.dt.day_name()
is_weekend = day_names.apply(lambda x : 1 if x in ['Saturday','Sunday'] else 0)

Holiday flag 以及 weather

幸運的是,這些數據還包含公共假日信息。信息是細粒度的,由於它提到每一個公共假日的名稱。儘管如此,本文假設對每一個假期進行編碼並無顯著的好處。所以,讓咱們建立一個二進制特性來指示對應的日期是不是假日。

# is_holiday flag
is_holiday = raw.holiday.apply(lambda x : 0 if x == "None" else 1)

咱們須要考慮的最後一個分類特徵是天氣。咱們只對該特徵進行以下獨熱編碼。

# one-hot encode weather
weathers = pd.get_dummies(raw.weather_main)
#display data
weathers

獨熱編碼後的Weather信息

特徵處理後的數據

如今,咱們終於有了最終的可用於訓練的數據!讓咱們建立一個名爲features的全新數據集,它包含全部的特徵,包括數值型特徵(咱們從原始數據中按原樣放置)和類型特徵(咱們設計的特性)。

# features table
#first step: include features with single column nature
features = pd.DataFrame({
   'temp' : raw.temp,
   'rain_1h' : raw.rain_1h,
   'snow_1h' : raw.snow_1h,
   'clouds_all' : raw.clouds_all,
   'month' : months,
   'day_of_month' : day_of_months,
   'hour' : hours,
   'is_holiday' : is_holiday,
   'is_weekend' : is_weekend
})
#second step: concat with one-hot encode typed features
features = pd.concat([features, days, dayparts, weathers], axis = 1)
# target column
target = raw.traffic_volume

在咱們將數據輸入模型以前,咱們須要分割數據(訓練集和測試集)。請注意,下面咱們不隨機化咱們的數據,這是因爲咱們的數據具備時間序列特徵。

#split data into training and test data
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.1, shuffle = False)

創建迴歸預測模型

如今咱們準備創建咱們的模型來預測地鐵州際交通量。在這項工做中,咱們將使用Gradient Boosting迴歸模型。

該模型的理論和具體細節超出了本文的討論範圍。可是簡單來講,gradient-boosting模型屬於集成模型,它使用梯度降低算法來下降弱學習模型(決策樹)中的預測損失。

訓練模型

讓咱們在訓練數據上實例化模型並訓練模型!

from sklearn import datasets, ensemble
# define the model parameters
params = {'n_estimators': 500,
         'max_depth': 4,
         'min_samples_split': 5,
         'learning_rate': 0.01,
         'loss': 'ls'}
# instantiate and train the model
gb_reg = ensemble.GradientBoostingRegressor(**params)
gb_reg.fit(X_train, y_train)

評價模型

咱們選擇兩個指標來評價模型:MAPE 和 R2得分。在測試集上使用訓練完成的模型進行預測,而後計算這兩個指標。

# define MAPE function
def mape(true, predicted):        
   inside_sum = np.abs(predicted - true) / true
   return round(100 * np.sum(inside_sum ) / inside_sum.size,2)
# import r2 score
from sklearn.metrics import r2_score
# evaluate the metrics
y_true = y_test
y_pred = gb_reg.predict(X_test)
#print(f"GB model MSE is {round(mean_squared_error(y_true, y_pred),2)}")
print(f"GB model MAPE is {mape(y_true, y_pred)} %")
print(f"GB model R2 is {round(r2_score(y_true, y_pred)* 100 , 2)} %")


測試集上的評價指標結果

咱們能夠看出咱們的模型性能至關不錯。咱們的MAPE低於15%,而R2得分略高於95%。

結果可視化

爲了直觀理解模型性能,結果可視化頗有必要。

因爲咱們的測試數據(4820個數據點)的長度,咱們只繪製了最後100個數據點上的實際值和模型預測值。此外,咱們還包括另外一個模型(在下面的繪圖代碼中稱爲gb_reg_lite),它不包含日期時間特徵做爲其預測因子(它只包含非日期時間列做爲特徵,包括tempweather等)。

fig, ax = plt.subplots(figsize = (12,6))
index_ordered = raw.date_time.astype('str').tolist()[-len(X_test):][-100:]
ax.set_xlabel('Date')
ax.set_ylabel('Traffic Volume')
# the actual values
ax.plot(index_ordered, y_test[-100:].to_numpy(), color='k', ls='-', label = 'actual')
# predictions of model with engineered features
ax.plot(index_ordered, gb_reg.predict(X_test)[-100:], color='b', ls='--', label = 'predicted; with date-time features')
# predictions of model without engineered features
ax.plot(index_ordered, gb_reg_lite.predict(X_test_lite)[-100:], color='r', ls='--', label = 'predicted; w/o date-time features')
every_nth = 5
for n, label in enumerate(ax.xaxis.get_ticklabels()):
   if n % every_nth != 0:
       label.set_visible(False)
ax.tick_params(axis='x', labelrotation= 90)
plt.legend()
plt.title('Actual vs predicted on the last 100 data points')
plt.draw()

後100個點的預測結果

該圖中藍色虛線與黑色實線十分接近。也就是說,咱們提出的gradient-boosting模型能夠很好地預測地鐵交通量。

同時,咱們看到不使用日期時間特徵的模型在性能上出現了差別(紅色虛線)。爲何會這樣?只是由於咱們會依賴交通工具,交通流量在週末趨於減小,但在高峯時段出現高峯。所以,若是咱們不對日期時間數據進行特徵工程處理,咱們將錯過這些重要的預測因子!

做者:Pararawendy Indarjo


deephub翻譯組 OliverLee



本文分享自微信公衆號 - DeepHub IMBA(deephub-imba)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索