今天给大家分享第三个kaggle竞赛项目,纽约出租车价格预测New-York-City-Taxi-Fare-Prediction。这个项目的特点是给到我们的数据集比较大,有5.3G,数据总量是5400W行。不过我们在做这个项目的时候并不需要这么多的数据量,下面我们就一起来看一下这个项目。

Part1.数据导入和初步分析

首先导入我们的数据集,由于数据量过大,我们只导入前500W行的数据进行建模。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

train = pd.read_csv('train.csv',nrows=5000000)
test = pd.read_csv('test.csv')
test_ids = test['key']

train.head()

纽约出租车大数据分析案例_纽约出租车大数据分析案例

可以看到我们本次的数据特征量还是比较少的,虽然数据总量大,特征只有8个。

train.info()

纽约出租车大数据分析案例_机器学习_02

key:索引
fare_amount:价格
pickup_datetime:出租车接到客人的时间
pickup_longitude:出发时的经度
pickup_latitude:出发时的纬度
dropoff_longitude:到达时的经度
dropoff_latitude:到达时的纬度
passenger_count:乘客的数目

train.describe()

纽约出租车大数据分析案例_机器学习_03


红圈圈住的数据当中是异常值:负的价格,乘客数目最小为0,乘客数目最大为208

而横线划出来的数据令人疑惑:什么样的旅程会产生1270的出租费用呢?

Part2.数据分析

首先观察价格特征的分布:

train.fare_amount.hist(bins=100,figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_纽约出租车大数据分析案例_04

train[train.fare_amount <100 ].fare_amount.hist(bins=100, figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_大数据_05

train[train.fare_amount >=100 ].fare_amount.hist(bins=100, figsize = (16,8))
plt.xlabel("Fare Amount")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_kaggle_06

train[train.fare_amount <100].shape

纽约出租车大数据分析案例_数据分析_07

train[train.fare_amount >=100].shape

纽约出租车大数据分析案例_机器学习_08


由上面的代码和图我们可以得到几个结论:
1、价格的分布大多数是在100以内的,少部分在100以上
2、100以内的价格大多数集中在0~20之间
3、100以外的价格大多数集中在200附近,有几个比较大的价格可能是异常值,也可能是去往机场的价格。

接下来观察乘客数目的分布:

train.passenger_count.hist(bins=100,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_机器学习_09

train[train.passenger_count<10].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_纽约出租车大数据分析案例_10

train[train.passenger_count<7].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_数据分析_11

train[train.passenger_count>7].passenger_count.hist(bins=10,figsize = (16,8))
plt.xlabel("passenger_count")
plt.ylabel("Frequency")

纽约出租车大数据分析案例_大数据_12

train[train.passenger_count >7]

纽约出租车大数据分析案例_纽约出租车大数据分析案例_13

train[train.passenger_count ==0].shape

纽约出租车大数据分析案例_大数据_14

plt.figure(figsize= (16,8))
sns.boxplot(x = train[train.passenger_count< 7].passenger_count, y = train.fare_amount)

纽约出租车大数据分析案例_机器学习_15

train[train.passenger_count <7][['fare_amount','passenger_count']].corr()

纽约出租车大数据分析案例_机器学习_16

由上面的代码和图我们可以得到几个结论:
1、人数的分布大多数都在7以内,少部分在7以外
2、人数为7以外的数据中大部分数据坐标有缺失且人数都为208
3、有17602个数据是乘客人数为0,有可能是运货的出租车,也有可能是数据的缺失
4、由箱型图可以看出,人数小于7的出租车平均价格都比较接近
5、使用.corr()接口查看passenger_count 与fare_amount的关联程度并不高只有0.013

Part3.数据处理

1、空值处理

train.isnull().sum()#找出空值

纽约出租车大数据分析案例_数据分析_17

train = train.dropna(how='any', axis=0)

36个缺失值对于我们500W的数据量显得微不足道,所以我选择直接将缺失值去掉

test = pd.read_csv('test.csv')
test_ids = test['key']
test.head()
test.isnull().sum()

纽约出租车大数据分析案例_数据分析_18


纽约出租车大数据分析案例_数据分析_19


对测试集进行同样的处理,不过测试集并没有缺失值

2、异常值处理

train = train[train.fare_amount>=0]

将价格为负数的数据去除

3、特征工程
①缩短训练集的范围
由于训练集的数据量比较大,我们可以根据测试集的坐标范围来对训练集进行一定的缩减

print(min(test.pickup_longitude.min(),test.dropoff_longitude.min()))
print(max(test.pickup_longitude.max(),test.dropoff_longitude.max()))
print(min(test.pickup_latitude.min(),test.dropoff_latitude.min()))
print(max(test.pickup_latitude.max(),test.dropoff_latitude.max()))

纽约出租车大数据分析案例_数据分析_20


得到了-74.2到-73作为经度的选取范围,40.5到41.8作为纬度的选取范围

def select_train(df, fw):
    return (df.pickup_longitude >= fw[0]) & (df.pickup_longitude <= fw[1]) & \
           (df.pickup_latitude >= fw[2]) & (df.pickup_latitude <= fw[3]) & \
           (df.dropoff_longitude >= fw[0]) & (df.dropoff_longitude <= fw[1]) & \
           (df.dropoff_latitude >= fw[2]) & (df.dropoff_latitude <= fw[3])
fw = (-74.2, -73, 40.5, 41.8)
train = train[select_train(train, fw)]

利用select_train将训练集数据进行缩减

②构造新的时间特征
原本的时间特征并不适合我们直接使用,考虑到出租车不同时间段,不同年份,月份都可能会提价,我们要在原本的时间特征中提取出新的年,月,日,时作为新的特征,供我们的模型使用。

def deal_time_features(df):
    df['pickup_datetime'] = df['pickup_datetime'].str.slice(0, 16)
    df['pickup_datetime'] = pd.to_datetime(df['pickup_datetime'], utc=True, format='%Y-%m-%d %H:%M')
    df['hour'] = df.pickup_datetime.dt.hour
    df['month'] = df.pickup_datetime.dt.month
    df["year"] = df.pickup_datetime.dt.year
    df["weekday"] = df.pickup_datetime.dt.weekday
    return df
train = deal_time_features(train)
test = deal_time_features(test)
train.head()

纽约出租车大数据分析案例_大数据_21


处理后的时间特征由时,月,年,周几组成

③构造新的距离特征
直接使用经纬度坐标不利于我们的模型运转,我们用转换公式将经纬度坐标转化为距离

def distance(x1, y1, x2, y2):
    p = 0.017453292519943295 
    a = 0.5 - np.cos((x2 - x1) * p)/2 + np.cos(x1 * p) * np.cos(x2 * p) * (1 - np.cos((y2 - y1) * p)) / 2
    dis = 0.6213712 * 12742 * np.arcsin(np.sqrt(a))
    return dis  
train['distance_miles'] = distance(train.pickup_latitude,train.pickup_longitude,train.dropoff_latitude,train.dropoff_longitude)
test['distance_miles'] = distance(test.pickup_latitude, test.pickup_longitude,test.dropoff_latitude,test.dropoff_longitude)
train.head()

纽约出租车大数据分析案例_数据分析_22

train[(train['distance_miles']==0)&(train['fare_amount']==0)]

纽约出租车大数据分析案例_数据分析_23


构造完距离特征后,我们会发现还有15个距离和价格都为0的无用数据,可以将它删除

train = train.drop(index= train[(train['distance_miles']==0)&(train['fare_amount']==0)].index, axis=0)

④特殊处理
1、删除fare_amount小于2.5的数据,因为纽约出租车的起步价为2.5

train = train.drop(index= train[train['fare_amount'] < 2.5].index, axis=0)

2、去除人数大于7的数据

train[train.passenger_count >= 7]

纽约出租车大数据分析案例_大数据_24

train = train.drop(index= train[train.passenger_count >= 7].index, axis=0)

Part4.数据建模

看一下数据处理完后的最终样子

train.describe().T

纽约出租车大数据分析案例_大数据_25


利用.corr接口看下这些新的特征跟价格的关联

train.corr()['fare_amount']

纽约出租车大数据分析案例_纽约出租车大数据分析案例_26


进入建模的步骤:

df_train = train.drop(columns= ['key','pickup_datetime'], axis= 1).copy()
df_test = test.drop(columns= ['key','pickup_datetime'], axis= 1).copy()
#使用copy后的数据进行建模


from sklearn.model_selection import train_test_split 
X_train, X_test, y_train, y_test = train_test_split(df_train.drop('fare_amount',axis=1)
                                                    ,df_train['fare_amount']
                                                    ,test_size=0.2
                                                    ,random_state = 42)


#用train_test_split分出训练集和测试集

import xgboost as xgb
params = { 
    'max_depth': 7,
    'gamma' :0,
    'eta':0.3, 
    'subsample': 1,
    'colsample_bytree': 0.9, 
    'objective':'reg:linear',
    'eval_metric':'rmse',
    'silent': 0
}
def XGBmodel(X_train,X_test,y_train,y_test,params):
    matrix_train = xgb.DMatrix(X_train,label=y_train)
    matrix_test = xgb.DMatrix(X_test,label=y_test)
    model=xgb.train(params=params,
                    dtrain=matrix_train,
                    num_boost_round=5000, 
                    early_stopping_rounds=10,
                    evals=[(matrix_test,'test')])
    return model

model = XGBmodel(X_train,X_test,y_train,y_test,params)

#建模

纽约出租车大数据分析案例_机器学习_27

prediction = model.predict(xgb.DMatrix(df_test), ntree_limit = model.best_ntree_limit)
prediction
#数据预测

纽约出租车大数据分析案例_纽约出租车大数据分析案例_28

res = pd.DataFrame()
res['key'] = test_ids
res['fare_amount'] = prediction
res.to_csv('submission.csv', index=False)
#结果保存

Part5.小结

我的做法只是比较简单的思路,因为出租车价格我能想到的有关价格的因素只有不同时间段,还有距离,这两个因素影响的程度会比较大。如果有其他更好的想法和做法的话,欢迎在讨论区里面留言告诉博主。
另外,在kaggle社区上有一种做法是构造一个新的特征,代表坐标到当地三个不同机场的距离,这种做法博主在未调参时直接去使用,结果提高了0.03,我觉得提高并不算太大,作用也与距离有点重合,所以我最后没有采用。这里也贴出来分享给大家。

# def transform(data):
#     # Distances to nearby airports, 
#     jfk = (-73.7781, 40.6413)
#     ewr = (-74.1745, 40.6895)
#     lgr = (-73.8740, 40.7769)

#     data['pickup_distance_to_jfk'] = distance(jfk[1], jfk[0],
#                                          data['pickup_latitude'], data['pickup_longitude'])
#     data['dropoff_distance_to_jfk'] = distance(jfk[1], jfk[0],
#                                            data['dropoff_latitude'], data['dropoff_longitude'])
#     data['pickup_distance_to_ewr'] = distance(ewr[1], ewr[0], 
#                                           data['pickup_latitude'], data['pickup_longitude'])
#     data['dropoff_distance_to_ewr'] = distance(ewr[1], ewr[0],
#                                            data['dropoff_latitude'], data['dropoff_longitude'])
#     data['pickup_distance_to_lgr'] = distance(lgr[1], lgr[0],
#                                           data['pickup_latitude'], data['pickup_longitude'])
#     data['dropoff_distance_to_lgr'] = distance(lgr[1], lgr[0],
#                                            data['dropoff_latitude'], data['dropoff_longitude'])
    
#     return data

# train = transform(train)
# test = transform(test)

谢谢你们的阅读!