【實戰(zhàn)篇】隨機森林預(yù)測氣溫(一)

在介紹決策樹雕沉、隨機森林算法時,小魚使用的一直都是分類任務(wù)去件。從本篇文章開始坡椒,小魚會在連載中更新基于隨機森林的氣溫預(yù)測任務(wù),根據(jù)歷史數(shù)據(jù)來預(yù)測某一天的氣溫尤溜。

注:氣溫為連續(xù)值倔叼,接下來關(guān)于氣溫預(yù)測的任務(wù)也是回歸任務(wù)。

讀取數(shù)據(jù)

讀取本次任務(wù)的數(shù)據(jù)集:

import pandas as pd
import os

df = pd.read_csv("data" + os.sep + "temps.csv")
df.head()

數(shù)據(jù)集中的前 5 條數(shù)據(jù)如下:

數(shù)據(jù)特征介紹:

  • year moth day week 表示日期
  • temp_2 前天的溫度
  • temp_1 昨天的溫度
  • average 在歷史當(dāng)中宫莱,每年這一天的平均溫度
  • actual 當(dāng)天的真實溫度梢睛,也是我們本次任務(wù)預(yù)測的標(biāo)簽
  • friend 湊熱鬧的一列绝葡,相當(dāng)于噪聲

當(dāng)前數(shù)據(jù)集一共 348 個樣本藏畅,9 列中包含了 8 個特征和 1 個標(biāo)簽值愉阎。

>> df.shape
(348, 9)

觀察數(shù)值型指標(biāo)的統(tǒng)計特性:

df.describe()

其中年份的標(biāo)準(zhǔn)差為 0 榜旦,這是因為所有樣本都是 2016 年的數(shù)據(jù)溅呢。此外,對于時間特征绩蜻,在預(yù)處理時室埋,我們往往需要組合或者拆分姚淆。

拆分是為了提取特征,而組合則是為了繪圖分析所服務(wù)的昔驱。在繪圖或者計算的過程中骤肛,需要標(biāo)準(zhǔn)的時間格式:

from datetime import datetime

dates = [datetime(year,month,day) for year,month,day in zip(df.year, df.month, df.day)]

得到的 datetime 日期如下:

>> dates[:5]
[datetime.datetime(2016, 1, 1, 0, 0),
 datetime.datetime(2016, 1, 2, 0, 0),
 datetime.datetime(2016, 1, 3, 0, 0),
 datetime.datetime(2016, 1, 4, 0, 0),
 datetime.datetime(2016, 1, 5, 0, 0)]

觀察特征

導(dǎo)入畫圖工具,并設(shè)置繪圖風(fēng)格:

import matplotlib.pyplot as plt

# plt.style.available
# 指定默認(rèn)風(fēng)格
plt.style.use("seaborn-whitegrid")

%matplotlib inline

繪圖觀察真實氣溫吓笙、前一天氣溫以及 2 天前氣溫面睛、噪音列和日期的關(guān)系叁鉴。

# 設(shè)置布局
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, figsize=(10,6), dpi=80)
fig.autofmt_xdate(rotation = 45)

# 標(biāo)簽值
ax1.plot(dates, df.actual, linewidth=1, color='red')
ax1.set_xlabel('')
ax1.set_ylabel('Temperature')
ax1.set_title('Temp')

# 昨天
ax2.plot(dates, df.temp_1, linewidth=1, color='red')
ax2.set_xlabel('')
ax2.set_ylabel('Temperature')
ax2.set_title('Previous Temp')

# 前天
ax3.plot(dates, df.temp_2,linewidth=1, color='red')
ax3.set_xlabel('Date')
ax3.set_ylabel('Temperature')
ax3.set_title('Two Days Prior Temp')

# 噪聲
ax4.plot(dates, df.friend, linewidth=1, color='red')
ax4.set_xlabel('Date')
ax4.set_ylabel('Temperature')
ax4.set_title('Friend Estimate')

繪制結(jié)果:

溫度相關(guān)的特征無異常情況但壮,分布基本上是一致的蜡饵。friend 列雖然也是氣溫溯祸,但其分布與 actual temp_1temp_2 明顯不同, friend 列可以作為噪聲剔除鸟召。

數(shù)據(jù)預(yù)處理

原始數(shù)據(jù)中在 week 列中并不是一些數(shù)值特征,而是表示周幾的字符串仆抵,這些計算機可不認(rèn)識镣丑,需要我們來轉(zhuǎn)換一下 莺匠。

>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 348 entries, 0 to 347
Data columns (total 9 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   year     348 non-null    int64  
 1   month    348 non-null    int64  
 2   day      348 non-null    int64  
 3   week     348 non-null    object 
 4   temp_2   348 non-null    int64  
 5   temp_1   348 non-null    int64  
 6   average  348 non-null    float64
 7   actual   348 non-null    int64  
 8   friend   348 non-null    int64  
dtypes: float64(1), int64(7), object(1)
memory usage: 24.6+ KB

該列中的值為 object 類型,存放的都是星期幾這樣的屬性值:

week
Mon
Tue
Wed
Thu
Fri

需要使用獨熱編碼進(jìn)行轉(zhuǎn)換:

Mon Tue Wed Thu Fri
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

使用獨熱編碼處理之后旱物, week 列將被拆分成由不同屬性值構(gòu)成的所列单匣。對于某個特定的樣本宝穗,只有樣本屬性對應(yīng)的屬性列為 1逮矛,其余列全部為 0 橱鹏,所以又稱為獨熱編碼。

>> df= pd.get_dummies(df)
>> df.head()

度熱編碼之后的數(shù)據(jù)集:

劃分訓(xùn)練集與測試集:

from sklearn.model_selection import train_test_split

y = df.actual
X = df.drop("actual", axis=1)

feature_list = list(df.columns)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)

訓(xùn)練集與測試集樣本個數(shù):

>> print('訓(xùn)練集特征:', X_train.shape)
訓(xùn)練集特征: (261, 14)
>> print('訓(xùn)練集標(biāo)簽:', y_train.shape)
訓(xùn)練集標(biāo)簽: (261,)
>> print('測試集特征:', X_test.shape)
測試集特征: (87, 14)
>> print('測試集標(biāo)簽:', y_test.shape)
測試集標(biāo)簽: (87,)

隨機森林建模

下面,我們先不進(jìn)行調(diào)參杉辙,先從建模捶朵、訓(xùn)練、預(yù)測岖食、評估舞吭、可視化整體來一遍蔑穴,了解一下流程存和。

from sklearn.ensemble import RandomForestRegressor

rfr = RandomForestRegressor(n_estimators=1000, random_state=0)
rfr.fit(X_train, y_train)

在測試集進(jìn)行預(yù)測捐腿,并使用 MAPE 絕對百分比誤差來評估此回歸任務(wù)的結(jié)果叙量。

# 預(yù)測結(jié)果
>> predictions = rfr.predict(X_test)
# 計算誤差
>> errors = abs(predictions - y_test)
# MAPE(Mean Absolute Percentage Error)
>> mape = errors / y_test
>> print(f'MAPE:{mape.mean():.2%}')
MAPE:6.08%

絕對百分比誤差 MAPE 可以表示當(dāng)前模型的絕對誤差有多少绞佩,MAPE 越小則損失越小品山,模型越優(yōu)秀肘交。

隨機森林有一個非常大的好處涯呻,經(jīng)過隨機森林建模之后复罐,通過模型可以得到特征的重要性排名效诅。

feature_df = pd.DataFrame({
    'features': rfr.feature_names_in_,
    'importances': rfr.feature_importances_
})
feature_df.sort_values(by='importances',ascending=False)

從輸出結(jié)果來看咽笼,前 3 個最重要的特征分別是 temp_1 average temp_2 戚炫。

通過柱形圖叛甫,直觀地展示特征的重要性萌腿。

feature_df.plot(kind='bar')
# X軸名字
plt.xticks(feature_df.index, feature_df.features, rotation="vertical")
# 圖名
plt.xlabel('Features')
plt.title('Features Importances')

昨天的溫度 temp_1 和歷史中這一天的平均溫度 average 是兩個重要層度非常突出的指標(biāo)毁菱。

有的時候峦筒,我們?yōu)榱颂岣咝饰锱纾矔x擇最重要的幾個特征來進(jìn)行建模峦失。下面尉辑,小魚極端地使用兩個特征進(jìn)行建模隧魄,來看看絕對百分比誤差 MAPE 的變化隘蝎。

# 選擇最重要的那2個特征來試試
rfr_most_important = RandomForestRegressor(n_estimators=1000, random_state=0)
important_feature = ['temp_1', 'average']

# 重新訓(xùn)練模型
rfr_most_important.fit(X_train.loc[:,important_feature], y_train)
# 預(yù)測結(jié)果
predictions = rfr_most_important.predict(X_test.loc[:, important_feature])
# 評估結(jié)果
errors = abs(predictions - y_test)
mape = errors / y_test

print(f'mape:{mape.mean():.2%}')

為了公平起見购啄,建模時使用了相同的隨機數(shù)種子。使用最重要的兩個特征建模嘱么,得到的絕對百分比誤差為:

MAPE:6.38%

使用少數(shù)重要特征建模雖然比使用全部特征建模的損失增大了闸溃,但增加的損失并不多,但可以換來時間成本。

最后辉川,我們還可以通過繪圖的方式表蝙,來直觀地看到目前真實值同預(yù)測值之間的差異情況。

定義兩個 DataFrame 分別存儲日期和真實值乓旗、日期和預(yù)測值:

test_dates = [datetime(year,month,day) for year,month,day in zip(X_test.year, X_test.month, X_test.day)]

predictions_data = pd.DataFrame({'date':test_dates, 'prediction':predictions})
true_data = pd.DataFrame({'date':dates, 'actual':y})

繪圖:

plt.plot(true_data['date'], true_data['actual'], color='blue', linestyle='-', linewidth=1.5, label='actual')
plt.plot(predictions_data['date'], predictions_data['prediction'], 'ro', label='prediction')

plt.xticks(rotation=60)
plt.legend()

plt.xlabel('Date')
plt.ylabel('Maximum Temperature (F)')

plt.title('Actual and Predicted Values')

繪制結(jié)果:

看起來還可以府蛇,這個走勢我們的模型已經(jīng)基本能夠掌控了汇跨,接下來我們要再深入到數(shù)據(jù)中了蚪黑,考慮幾個問題:

1、如果可以利用的數(shù)據(jù)量增大郊愧,會對結(jié)果產(chǎn)生什么影響呢?
2、加入新的特征會改進(jìn)模型效果嗎?此時的時間效率又會怎樣?

我們下節(jié)見 (* ̄︶ ̄)

附:隨機森林中樹的可視化展示

上述我們訓(xùn)練隨機森林模型時,構(gòu)造了 1000 棵樹仍律,下面草则,小魚來展示其中的一顆決策樹。

from sklearn.tree import export_graphviz
from IPython.display import Image
import pydotplus

# 拿到其中一棵樹
tree = rfr.estimators_[0]
# 導(dǎo)出成dot文件
export_graphviz(tree, out_file="tree.dot", feature_names=X.columns, rounded=True, precision=1)
# 繪圖
graph = pydotplus.graph_from_dot_file("tree.dot")
# 在notebook中展示
Image(graph.create_png())

由于樹還是比較龐大的轮纫,在 Jupyter NoteBook 中無法清晰地呈現(xiàn):

可以保存成 PNG 文件在電腦的圖像軟件中查看:

graph.write_png("tree.png")

下面我們?yōu)榱烁玫卦?Notebook 中展示,小魚只選擇 3 個特征構(gòu)建隨機森林撩扒,將樹的深度限制在 3:

>> rfr_small = RandomForestRegressor(n_estimators=1000, max_depth=3, random_state=0)
>> rfr_small.fit(X_train, y_train)
RandomForestRegressor(max_depth=3, n_estimators=1000, random_state=0)

選取隨機森林中的一棵樹進(jìn)行展示:

# 提取一棵樹
tree_small = rfr_small.estimators_[0]

# 保存
graph = export_graphviz(
    tree_small, label="root", proportion=True, out_file=None, 
    feature_names=X.columns, filled=True, rounded=True)

graph = pydotplus.graph_from_dot_data(graph)
# 展示
Image(graph.create_png())

繪制結(jié)果:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌答倡,老刑警劉巖倔既,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡燕锥,警方通過查閱死者的電腦和手機厚棵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門查吊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坤邪,你說我怎么就攤上這事喂饥〉妓牵” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵胀溺,是天一觀的道長无埃。 經(jīng)常有香客問我澎埠,道長,這世上最難降的妖魔是什么剔应? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任席怪,我火速辦了婚禮,結(jié)果婚禮上纤控,老公的妹妹穿的比我還像新娘挂捻。我一直安慰自己,他們只是感情好船万,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布刻撒。 她就那樣靜靜地躺著惜辑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疫赎。 梳的紋絲不亂的頭發(fā)上盛撑,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音捧搞,去河邊找鬼抵卫。 笑死,一個胖子當(dāng)著我的面吹牛胎撇,可吹牛的內(nèi)容都是我干的介粘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晚树,長吁一口氣:“原來是場噩夢啊……” “哼姻采!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起爵憎,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤慨亲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宝鼓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刑棵,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年愚铡,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛉签。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡沥寥,死狀恐怖碍舍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邑雅,我是刑警寧澤片橡,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站蒂阱,受9級特大地震影響锻全,放射性物質(zhì)發(fā)生泄漏狂塘。R本人自食惡果不足惜录煤,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荞胡。 院中可真熱鬧妈踊,春花似錦、人聲如沸泪漂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至露筒,卻和暖如春呐伞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慎式。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工伶氢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘪吏。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓癣防,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掌眠。 傳聞我的和親對象是個殘疾皇子蕾盯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容