纽约市计程车价格预测
纽约市计程车价格预测
来自专栏猴子聊数据分析7 人赞了文章
在开始之前,对项目做个简单的了解。这是kaggle在2个月前推出的项目,数据栏位不多,但是数据量蛮大。项目目的是根据时间和距离预测计程车车费。
下图为本次项目的步骤和思路
一、提出问题
目标很明确,就是预测乘坐计程车的费用。
二、理解数据
key - 唯一的ID栏位
pickup_datetime - 表示计程车开始的时间。
pickup_longitude - 计程车开始的经度坐标。
pickup_latitude - 计程车开始的纬度坐标。
dropoff_longitude - 计程车行程结束的经度坐标。
dropoff_latitude - 计程车行程结束的纬度坐标。
passenger_count - 表示乘坐计程车的乘客数量。
fare_amount - 需要预测的车费。
三、数据清洗
1、导入数据
import numpy as npimport pandas as pdtrain = pd.read_csv("D://train.csv",nrows=1000000)test = pd.read_csv("D://test.csv")
2、查看数据基本情况
训练集有100万行数据,8个栏位。预测集有9914行数据,7个栏位。
train.head()
训练集train的描述性分析
train.describe()
从中我们可以发现,fare_amout存在小于0的异常值。经纬度都有特别大和特别小的异常值。乘车人数passenger_count存在极大值208的异常。
3、处理缺失值
分别对train和test缺失值求和并降序排列。
train.isnull().sum().sort_values(ascending=False)
test.isnull().sum().sort_values(ascending=False)
可以发现在train里存在10条缺失经纬度的数据,我们作删除处理。
train = train.drop(train[train[dropoff_latitude].isnull()].index,axis=0) #删除10条含缺失值的数据
4、处理异常值
1)fare_amount(车费)
首先,对向量fare_amount异常值处理。车费少于0的删掉,因为司机不可能倒贴你钱。
train = train.drop(train[train[fare_amount]<0].index,axis=0)
再看看,fare_amount总体情况。
train[fare_amount].describe()
可以发现,车费最高价格为500美元。
2)passenger_count(乘客数)
之前,训练集的描述性分析里,我们已经看出passenger_count没有负值。由于限载等因素的影响,超过6人的属异常值。
train[train[passenger_count]>6]
显然载客数208,是不符合实际的,我把它作删除处理。
train = train.drop(train[train[passenger_count]>6].index,axis=0)
再次确认一下,passenger_count数据
train[passenger_count].describe()
这时passenger_count最大值为6,最小值为0,符合实际情况。
3)经纬度
再来看一下,乘客上车地点的经纬度和乘客下车地点的经纬度是否存在异常。
train[pickup_latitude].describe()
train[pickup_longitude].describe()
查看经纬度的最大、最小值发现,经纬度数据存在极端值。
根据实际情况,我把经纬度范围限制在,纬度在(-90,90)度之间,经度在(-180,180)度之间。超过范围的,作删除处理。
train = train.drop(((train[train[pickup_latitude]<-90])|(train[train[pickup_latitude]>90])) .index,axis=0)train = train.drop(((train[train[pickup_longitude]<-180])|(train[train[pickup_longitude]>180])) .index,axis=0)train = train.drop(((train[train[dropoff_latitude]<-90])|(train[train[dropoff_latitude]>90])) .index,axis=0)train = train.drop(((train[train[dropoff_longitude]<-180])|(train[train[dropoff_longitude]>180]) ).index,axis=0)train.shape
5、数据类型转化
首先,检查一下数据类型是否有问题,主要是查看是否影响建模。
train.dtypes
发现,key、pickup_datetime是字元串型,我们需要转化成时间类型。
train[key]=pd.to_datetime(train[key])train[pickup_datetime]=pd.to_datetime(train[pickup_datetime])
test数据集一同转化
test[key]=pd.to_datetime(test[key])test[pickup_datetime]=pd.to_datetime(test[pickup_datetime])
查看转化结果:
6、特征工程
特征工程,目的是最大限度地从原始数据中提取特征以供演算法和模型使用。
特征一:
数据集里只有乘客上车地的经纬度,和下车地的经纬度。我们知道计程车走的不可能都是直线,所以两点经纬度相减得到的直线距离,并不是很准确。但我们也不知道计程车走过的实际路程,只能尽可能的靠近实际,所以我的想法是计算两点间的球面距离。
自定义一个计算函数:
def haversine_distance(lat1, long1, lat2, long2): data = [train, test] for i in data: R = 6371 #radius of earth in kilometers phi1 = np.radians(i[lat1]) phi2 = np.radians(i[lat2]) delta_phi = np.radians(i[lat2]-i[lat1]) delta_lambda = np.radians(i[long2]-i[long1]) #a = sin2((φB - φA)/2) + cos φA . cos φB . sin2((λB - λA)/2) a = np.sin(delta_phi / 2.0) ** 2 + np.cos(phi1) * np.cos(phi2) * np.sin(delta_lambda / 2.0) ** 2 #c = 2 * atan2( √a, √(1?a) ) c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a)) #d = R*c d = (R * c) #in kilometers i[H_Distance] = d return d
然后带入四个参数
haversine_distance(pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude)
再查看一下新构建的特征向量情况
train[H_Distance].describe()
通过H_Distance的描述性统计值,可以看出H_Distance存在异常值。最大值太大了。
让我们通过探索性分析,处理H_Distance向量的异常值。
H_Distance,异常值处理:
- 删除车费为0,起点经纬度为零的数据
train.loc[((train[pickup_latitude]==0)&(train[pickup_longitude]==0))&((train[dropoff_latitude] !=0) & (train[dropoff_longitude]!=0))& (train[fare_amount]==0)]
删除这条数据
train = train.drop(train.loc[((train[pickup_latitude]==0) & (train[pickup_longitude]==0))&((train[dropoff_latitude]!=0)& (train[dropoff_longitude]!=0)) & (train[fare_amount]==0)].index, axis=0)
- 车费为0,下车地经纬度是0的数据
train.loc[((train[pickup_latitude]!=0) & (train[pickup_longitude]!=0))&((train[dropoff_latitude]==0) & (train[dropoff_longitude]==0))&(train[fare_amount]==0)]
删除这3条数据
train = train.drop(train.loc[((train[pickup_latitude]!=0) & (train[pickup_longitude]!=0))&((train[dropoff_latitude]==0)&(train[dropoff_longitude]==0)) & (train[fare_amount]==0)].index, axis=0)
我们以200为标量,距离大于200的,作删除处理。
train = train.drop(train.loc[(train[H_Distance]>200)&(train[fare_amount]!=0)].index,axis=0)
- 距离为0,车费小于2.5(起步价)
train.loc[(train[H_Distance]==0) & (train[fare_amount] < 2.5)]
距离为0,车费小于2.5,我们无法得到有价值的数据,所以删除。
train = train.drop(train.loc[(train[H_Distance]==0) & (train[fare_amount] < 2.5)].index,axis=0)
- 距离为0,车费大于3。距离为0却产生费用,可能距离数据缺失了,我们需要通过车费来计算距离。
F3h0 = train.loc[(train[fare_amount]>3)&(train[H_Distance]==0)]
F3h0[H_Distance] = F3h0.apply(lambda row: ((row[fare_amount]-2.50)/1.56), axis=1)train.update(F3h0)
- 反过来,距离不等于0,但是车费等于0,我们需要用公式计算车费。
H1f0 = train.loc[(train[H_Distance]!=0) & (train[fare_amount]==0)]H1f0[fare_amount] = H1f0.apply(lambda row: (row[H_Distance]*1.56+2.5),axis=1)train.update(H1f0)
特征二:
观察发现pickup_datetime是乘客上车时间,里面包含了年、月、日、小时。可以从里面提取出年、月、日、小时、星期几。
data = [train,test]for i in data: i[Year] = i[pickup_datetime].dt.year i[Month] = i[pickup_datetime].dt.month i[Date] = i[pickup_datetime].dt.day i[Day of week] = i[pickup_datetime].dt.dayofweek i[Hour] = i[pickup_datetime].dt.hour
我们把已经提取出特征工程的key和pickup_datetime删除掉。
train = train.drop([key,pickup_datetime], axis = 1)test = test.drop([key,pickup_datetime], axis = 1)
#导入包import seaborn as snsimport matplotlib.pyplot as plt
用散点图查看,乘车人数与价格关系。
plt.figure(figsize=(15,7))plt.scatter(x=train[passenger_count],y=train[fare_amount],s=1.5)plt.title(乘车人数与价格关系)plt.xlabel(乘车人数)plt.ylabel(价格)plt.show()
用散点图查看,日期与价格关系。
plt.figure(figsize=(15,7))plt.scatter(x=train[Date],y=train[fare_amount],s=1.5)plt.title(日期与价格关系)plt.xlabel(日期)plt.ylabel(价格)plt.show()
用散点图查看,时间与价格关系。
plt.figure(figsize=(15,7))plt.scatter(x=train[Hour],y=train[fare_amount],s=1.5)plt.title(时间与价格关系)plt.xlabel(时间)plt.ylabel(价格)plt.show()
用散点图查看,星期与价格关系。
plt.figure(figsize=(15,7))plt.scatter(x=train[Day of week],y=train[fare_amount],s=1.5)plt.title(星期与价格关系)plt.xlabel(星期)plt.ylabel(价格)plt.show()
四、构建模型
x_train = train.iloc[:,train.columns!=fare_amount] #测试集 xy_train = train[fare_amount].values #测试集 yx_test = test #预测数据
导入机器学习模型
from sklearn.ensemble import RandomForestRegressorrf = RandomForestRegressor()
训练数据
rf.fit(x_train, y_train)
预测数据
rf_predict = rf.predict(x_test)
保存预测数据,csv格式
submission = pd.read_csv(D://sample_submission.csv)submission[fare_amount] = rf_predictsubmission.to_csv(submission_1.csv, index=False)
五、提交数据
提交数据获取排名,425名。
推荐阅读: