kaggle實戰:6大回歸模型預測機票價格

語言: CN / TW / HK

theme: smartblue highlight: xcode


公眾號:尤而小屋
作者:Peter
編輯:Peter

大家好,我是Peter~

今天給大家帶來一篇新的kaggle案例文章:基於6大回歸模型預測航空公司機票。這篇文章涉及到的知識點很多:

原notebook的學習地址為:

https://www.kaggle.com/anshigupta01/flight-price-prediction/notebook

匯入庫

資料基本資訊

先把資料導進來:

python df = pd.read_excel("data_air.xlsx") df.head()

檢視資料的基本資訊,包含:資料形狀、欄位型別等

```python

欄位型別

df.dtypes

Airline object Date_of_Journey object Source object Destination object Route object Dep_Time object Arrival_Time object Duration object Total_Stops object Additional_Info object Price int64 dtype: object

全部欄位

columns = df.columns.tolist() columns

['Airline', 'Date_of_Journey', 'Source', 'Destination', 'Route', 'Dep_Time', 'Arrival_Time', 'Duration', 'Total_Stops', 'Additional_Info', 'Price'] ```

數值型欄位的描述統計資訊,這裡主要是針對Price欄位:

缺失值處理

通過上面的缺失值檢查,我們看到有兩個欄位是存在缺失值的,進行視覺化。

missingno是一個視覺化缺失值的庫,方便使用,我們可以用pip install missingno 即可下載該庫

python import missingno as mso mso.bar(df,color="blue") plt.show()

缺失值刪除

正常的欄位是10683條,其中兩個欄位是10682條;缺失值的佔比很小,考慮直接刪除的資料

時間相關欄位處理

時間處理

  1. 通過pd.to_datetime()直接將字元型的資料轉成時間型別的資料
  2. 通過dt.day或者df.month 直接獲取天或者月的資訊

```python

將欄位型別轉成時間相關型別

def change_to_datetime(col): df[col] = pd.to_datetime(df[col]) ```

對3個欄位實施轉換:

```python

3個欄位的轉換

for col in ['Date_of_Journey','Dep_Time', 'Arrival_Time']: change_to_datetime(col) ```

檢視轉換之後的欄位型別:

df.dtypes

python Airline object Date_of_Journey datetime64[ns] Source object Destination object Route object Dep_Time datetime64[ns] Arrival_Time datetime64[ns] Duration object Total_Stops object Additional_Info object Price int64 dtype: object

提取天和月

乘客旅程日期中(Date_of_Journey)單獨提取天和月,作為兩個欄位

python df["day"] = df["Date_of_Journey"].dt.day df["month"] = df["Date_of_Journey"].dt.month df.head()

最終生成了兩個新的欄位:

```python

刪除欄位

df.drop('Date_of_Journey', axis=1, inplace=True) ```

起飛時間和抵達時間處理

主要提取兩個時間中的“小時”和“分鐘”資訊;同時刪除原欄位:

分別呼叫函式來提取資訊:

python extract_hour(df,'Dep_Time') extract_minute(df,'Dep_Time') drop_col(df,'Dep_Time') # 刪除原欄位

python extract_hour(df,'Arrival_Time') extract_minute(df,'Arrival_Time') drop_col(df,'Arrival_Time')

df.head()

航班持續時間duration

1、將持續時間規範化處理,統一變成0h 1m

```python

# 原文方法

duration=list(df['Duration'])

for i in range(len(duration)):

if len(duration[i].split(' '))==2:

pass

else:

if 'h' in duration[i]:

duration[i]=duration[i] + ' 0m'

else:

duration[i]='0h '+ duration[i]

```

下面是個人版本寫法:

2、從Duration欄位中提取小時和分鐘:

python df.drop("Duration",inplace=True,axis=1)

3、欄位型別轉化:檢視dur_hour和dur_minute的欄位型別變化

欄位編碼

欄位型別區分

主要是區分object和非object型別的欄位資訊。

1、針對字元型的欄位

python column = [column for column in df.columns if df[column].dtype == "object"] column

python ['Airline', 'Source', 'Destination', 'Route', 'Total_Stops', 'Additional_Info']

2、數值型(連續型)欄位

python continuous_col = [column for column in df.columns if df[column].dtype != "object"] continuous_col

python ['Price', 'day', 'month', 'Dep_Time_hour', 'Dep_Time_minute', 'Arrival_Time_hour', 'Arrival_Time_minute', 'dur_hour', 'dur_minute']

2種編碼技術

python Nominal data -- Data that are not in any order -->one hot encoding ordinal data -- Data are in order --> labelEncoder

  • 標稱資料:沒有任何順序,使用獨熱編碼oneot encoding
  • 有序資料:存在一定的順序,使用型別編碼labelEncoder

生成標稱型欄位組成的資料

不同欄位編碼處理

航空公司-Airline

1、不同航空公司的數量統計:

2、檢視航空公司與價格關係

```python plt.figure(figsize=(15,8))

sns.boxplot(x="Airline", y="Price", data=df.sort_values('Price',ascending=False) )

plt.show() ```

3、2個明顯的結論

從上面的圖形中看出來:

  • Jet Airways Business公司的機票價格是最高的
  • 其他公司的價格中位數是比較接近的

4、實現獨熱編碼

由於航空公司屬性是一個標稱資料的欄位,我們進行獨熱編碼,通過啞變數的方式來實現:

停留地-Total_Stops

旅行期間的總共停留地,實施上面的同樣操作:

1、和價格的關係

```python plt.figure(figsize=(15,8))

sns.boxplot(x='Total_Stops', y='Price', data=df.sort_values('Price',ascending=False)) plt.show() ```

2、實施硬編碼;區別於航空公司的獨熱編碼

出發地source

出發地和價格的關係:

```python plt.figure(figsize=(18,12))

sns.catplot(x='Source', y='Price', data=df.sort_values('Price',ascending=False),kind='boxen')

plt.show() ```

獨熱編碼的過程:

目的地-destination

目的地的數量統計:

目的地和價格的關係:

獨熱編碼的實現:

路線Route

1、不同路線的數量統計:

2、路線名稱提取

從上面的結果中看出來,最長的路線中有5個地名,我們一次提取。

沒有出現的資料則用NaN來表示:

python categorical['Route1']=categorical['Route'].str.split('→').str[0] categorical['Route2']=categorical['Route'].str.split('→').str[1] categorical['Route3']=categorical['Route'].str.split('→').str[2] categorical['Route4']=categorical['Route'].str.split('→').str[3] categorical['Route5']=categorical['Route'].str.split('→').str[4] categorical.head()

3、缺失值欄位

python for i in ['Route3', 'Route4', 'Route5']: categorical[i].fillna('None',inplace=True)

4、型別編碼LabelEncoder

```python from sklearn.preprocessing import LabelEncoder le = LabelEncoder()

for i in ['Route1', 'Route2', 'Route3', 'Route4', 'Route5']: categorical[i]=le.fit_transform(categorical[i])

categorical.head() ```

抵達時間/小時-Arrival_Time_hour

抵達目的地時間和價格的關係:

python df.plot.hexbin(x='Arrival_Time_hour', y='Price', gridsize=15) plt.show()

建模資料

刪除無效欄位

生成的全部欄位資訊:

```python categorical.columns

Index(['Airline', 'Source', 'Destination', 'Total_Stops', 'Additional_Info', 'Route1', 'Route2', 'Route3', 'Route4', 'Route5'], dtype='object') ```

將原始的無效欄位直接刪除:

python drop_col(categorical,'Airline') drop_col(categorical,'Source') drop_col(categorical,'Destination') drop_col(categorical,'Additional_Info')

最終資料

將多個DataFrame進行拼接,組成最終的建模,其中Price進行最終的輸出特徵

```python final_df=pd.concat([categorical,Airline,source,destination,df[continuous_col]],axis=1)

final_df.head() ```

離群點檢測

對上面生成的最終資料進行離群點檢測:

對離群點填充均值,檢視填充後的效果:

python final_df['Price']=np.where(final_df['Price']>=40000, # 替換部分 final_df['Price'].median(), # 替換資料 final_df['Price']) # 替換欄位 plot(final_df, "Price")

資料切分

```python X=final_df.drop('Price',axis=1) y=df['Price']

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(X,y, test_size=0.20, random_state=123) ```

特徵選擇

本文中特徵選擇使用的是 mutual_info_classif 庫:

```python from sklearn.feature_selection import mutual_info_classif imp = pd.DataFrame(mutual_info_classif(X,y), index=X.columns)

imp.columns=['importance'] imp.sort_values(by='importance',ascending=False) ```

評價指標

本次建模中引入3個評價指標:

  • r2_score(重點關注)
  • mean_absolute_error
  • mean_squared_error

```python from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error

def predict(ml_model): print("Model is: ", ml_model)

model = ml_model.fit(X_train, y_train)

print("Training score: ", model.score(X_train,y_train))

predictions = model.predict(X_test)
print("Predictions: ", predictions)
print('-----------------')
r2score = r2_score(y_test, predictions)
print("r2 score is: ", r2score)

print('MAE:{}', mean_absolute_error(y_test,predictions))
print('MSE:{}', mean_squared_error(y_test,predictions))
print('RMSE:{}', np.sqrt(mean_squared_error(y_test,predictions)))

# 真實值和預測值的差值
sns.distplot(y_test - predictions)

```

建模

匯入多種模型:

```python

邏輯迴歸

from sklearn.linear_model import LogisticRegression

k近鄰迴歸

from sklearn.neighbors import KNeighborsRegressor

決策樹迴歸

from sklearn.tree import DecisionTreeRegressor

梯度提升迴歸,隨機森林迴歸

from sklearn.ensemble import GradientBoostingRegressor,RandomForestRegressor
```

隨機森林迴歸樹-RandomForestRegressor

邏輯迴歸-LogisticRegression

K近鄰迴歸-KNeighborsRegressor

決策樹迴歸DecisionTreeRegressor

支援向量機迴歸-SVR

梯度提升迴歸-GradientBoostingRegressor

模型調優-Hypertunning the model

調優尋參

```python

採用隨機搜尋調優

from sklearn.model_selection import RandomizedSearchCV ```

```python

待調優的引數

random_grid = { 'n_estimators' : [100, 120, 150, 180, 200,220], 'max_features':['auto','sqrt'], 'max_depth':[5,10,15,20], } ```

```python

建模擬合

rf=RandomForestRegressor() rf_random=RandomizedSearchCV( estimator=rf, param_distributions=random_grid, cv=3, verbose=2, n_jobs=-1)

rf_random.fit(X_train,y_train) ```

多次執行調優後找到最佳的引數組合:

調優後結果

通過r2_score指標發現:進行引數調優後,模型的效果得到提升~

補充:如何理解迴歸模型的r2_score指標

假設我們用$y_i$表示資料真實的觀測值,用$\bar{y}$表示真實觀測值的平均值,用$\hat{y_i}$表示通過模型得到的預測值,則:

迴歸平方和:SSR

SSR可以表示為;

即估計值與平均值的誤差,反映自變數與因變數之間的相關程度的偏差平方和

殘差平方和:SSE

SSE可以表示為:

即估計值與真實值的誤差,反映的是整個模型擬合程度

總離差平方和:SST

R2_score計算公式

R^2 score,即決定係數,反映因變數的全部變異能通過迴歸關係被自變數解釋的比例。計算公式:

也可以寫成:

進一步可以轉成:

此時分子就變成了我們常用的評價指標均方誤差MSE,分母就變成了方差Var。r2_score在0-1之間,越接近1越好。

兩種常見的求解r2的方式:

```python

1、利用python間接求解

from sklearn.metrics import mean_squared_error 1 - mean_squared_error(y_test, y_pred)/ np.var(y_test)

2、sklearn直接求解

from sklearn.metrics import r2_score y_test = [1, 2, 3] y_pred = [1.3, 2.1, 3.5] r2_score(y_test,y_pred)
```

資料獲取

關於本文的資料集獲取方式:關注公眾號 尤而小屋,回覆 航空機票 即可領取~