社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

【机器学习】kaggle实战:6大回归模型预测航班票价

机器学习初学者 • 3 年前 • 304 次点击  

公众号:尤而小屋
作者:Peter
编辑:Peter

今天给大家带来一篇新的kaggle案例文章:基于6大回归模型预测航空公司机票的价格

这篇文章涉及到的知识点会比较多,关键是数据预处理特征工程部分:

原notebook的学习地址为:

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

kaggle文章

基于LSTM模型实现共享自行车需求预测

6大监督学习模型:毒蘑菇分类

基于随机森林模型的心脏病患者预测分类

从统计和数据角度出发,如何看待房价?

Plotly+Seaborn+Folium可视化探索爱彼迎租房数据

Plotly+Pandas+Sklearn:实现用户聚类分群!

7000字,利用Python分析泰坦尼克数据!

导入库

数据基本信息

先把数据导进来:

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

查看数据的基本信息,包含:数据形状、字段类型等

# 字段类型
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']

具体字段的中文含义:

  • Airline:不同类型的航空公司
  • Date_of_Journey:旅客的旅行开始日期
  • Source:旅客出发地
  • Destination:旅客目的地
  • Route:航班路线
  • Dep_Time:出发时间
  • Arrival_Time:抵达时间
  • Duration:持续时间;指的是航班完成从出发到目的地的旅程的整个时间
  • Total_Stops:总共停留地
  • Additional_Info:其他信息,比如:食物、设备信息等
  • Price:整个旅程的航班票价

希望可以理解中文含义,帮助进行数据分析~



数值型字段的描述统计信息,这里主要是针对Price字段:

缺失值处理

通过上面的缺失值检查,我们看到有两个字段是存在缺失值的,进行可视化。

missingno是一个可视化缺失值的库,方便使用,我们可以用pip install missingno 即可下载该库

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

缺失值删除

正常的字段是10683条,其中两个字段是10682条;缺失值的占比很小,考虑直接删除的数据


时间相关字段处理

时间处理

  1. 通过pd.to_datetime()直接将字符型的数据转成时间类型的数据
  2. 通过dt.day或者df.month 直接获取天或者月的信息
# 将字段类型转成时间相关类型

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

对3个字段实施转换:

# 3个字段的转换

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

查看转换之后的字段类型:

df.dtypes
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)单独提取天和月,作为两个字段

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

最终生成了两个新的字段:

# 删除字段
df.drop('Date_of_Journey', axis=1, inplace=True)

起飞时间和抵达时间处理

主要提取两个时间中的“小时”和“分钟”信息;同时删除原字段:

分别调用函数来提取信息:

extract_hour(df,'Dep_Time')
extract_minute(df,'Dep_Time')
drop_col(df,'Dep_Time')  # 删除原字段
extract_hour(df,'Arrival_Time')
extract_minute(df,'Arrival_Time')
drop_col(df,'Arrival_Time')
df.head()

航班持续时间duration

1、将持续时间规范化处理,统一变成0h 1m

# # 原文方法

# 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字段中提取小时和分钟:

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

3、字段类型转化:查看dur_hour和dur_minute的字段类型变化

字段编码

字段类型区分

主要是区分object和非object类型的字段信息。

1、针对字符型的字段

column = [column for column in df.columns if df[column].dtype == "object"]
column
['Airline''Source''Destination''Route''Total_Stops''Additional_Info']

2、数值型(连续型)字段

continuous_col = [column for column in df.columns if df[column].dtype != "object"]
continuous_col
['Price',
 'day',
 'month',
 'Dep_Time_hour',
 'Dep_Time_minute',
 'Arrival_Time_hour',
 'Arrival_Time_minute',
 'dur_hour',
 'dur_minute']

2种编码技术

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、查看航空公司与价格关系

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、和价格的关系

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

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

2、实施硬编码;区别于航空公司的独热编码

出发地source

出发地和价格的关系:

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来表示:

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、缺失值字段

for


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

4、类型编码LabelEncoder

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

抵达目的地时间和价格的关系:

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

建模数据

删除无效字段

生成的全部字段信息:

categorical.columns

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

将原始的无效字段直接删除:

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

最终数据

将多个DataFrame进行拼接,组成最终的建模,其中Price进行最终的输出特征

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

final_df.head()

离群点检测

对上面生成的最终数据进行离群点检测:

对离群点填充均值,查看填充后的效果:

final_df['Price']=np.where(final_df['Price']>=40000,  # 替换部分
                           final_df['Price'].median(), # 替换数据
                           final_df['Price']) # 替换字段
plot(final_df, "Price")

数据切分

X=final_df.drop('Price',axis=1)
y=final_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 库:

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
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)

建模

导入多种模型:




    
# 逻辑回归
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

调优寻参

# 采用随机搜索调优
from sklearn.model_selection import RandomizedSearchCV
# 待调优的参数
random_grid = {
    'n_estimators' : [100120150180200,220],
    'max_features':['auto','sqrt'],
    'max_depth':[5,10,15,20],
    }
# 建模拟合
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指标

假设我们用表示数据真实的观测值,用表示真实观测值的平均值,用表示通过模型得到的预测值,则:

回归平方和:SSR

SSR可以表示为;

即估计值与平均值的误差,反映自变量与因变量之间的相关程度的偏差平方和

残差平方和:SSE

SSE可以表示为:

即估计值与真实值的误差,反映的是整个模型拟合程度

总离差平方和:SST

R2_score计算公式

R^2 score,即决定系数,反映因变量的全部变异能通过回归关系被自变量解释的比例。计算公式:

也可以写成:

进一步可以转成:

此时分子就变成了我们常用的评价指标均方误差MSE,分母就变成了方差Var。r2_score在0-1之间,越接近1越好。

两种常见的求解r2的方式:

# 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 = [123]
y_pred = [1.32.13.5]
r2_score(y_test,y_pred)  





    
往期 精彩回顾




Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/128670
 
304 次点击