竞赛圈 > 【最新baseline】汽车目的地智能预测大赛
baseline作者:XBuder
作者公众号:麻婆豆腐AI
思路:
统计车辆在星期1-7天最常去哪些地点,对这些地点做标记,之后去过为1,否则为0,以此做标签,把概率最高的地点推荐给选手
使用模型:
逻辑回归
优化方法:
1.继续细化统计车辆在1-7天的哪个小时去过哪些地点(如统计距0点的分钟数或者小时数),即按小时做推荐。另外还可以针对是否为节假日做标记构建特征。
2.挖掘同时间,同地域的车辆是否有相似性,对节假日的推荐单独处理。
3.尝试使用svm,xgb,lgb等模型,并进行参数调整
4.模型融合
导入所需要的包,忽略警告
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
评分算法
from math import radians, atan, tan, sin, acos, cos
def getDistance(latA, lonA, latB, lonB):
ra = 6378140 # 赤道半径: 米
rb = 6356755 # 极线半径: 米
flatten = (ra - rb) / ra # Partial rate of the earth
# change angle to radians
radLatA = radians(latA)#弧度
radLonA = radians(lonA)
radLatB = radians(latB)
radLonB = radians(lonB)
try:
pA = atan(rb / ra * tan(radLatA))
pB = atan(rb / ra * tan(radLatB))
x = acos(sin(pA) * sin(pB) + cos(pA) * cos(pB) * cos(radLonA - radLonB))
c1 = (sin(x) - x) * (sin(pA) + sin(pB)) ** 2 / cos(x / 2) ** 2
c2 = (sin(x) + x) * (sin(pA) - sin(pB)) ** 2 / sin(x / 2) ** 2
dr = flatten / 8 * (c1 - c2)
distance = ra * (x + dr)
return distance # meter
except:
return 0.0000001
def f(d):
return 1 / (1 + np.exp(-(d-1000)/250))
计算误差值
def getDistanceFromDF(data):
tmp = data[['end_lat','end_lon','predict_end_lat','predict_end_lon']].astype(float)
#从数据中取出'end_lat','end_lon','predict_end_lat','predict_end_lon'4列,转为浮点型
error = []#设置一个空列表error
for i in tmp.values:
t = getDistance(i[0],i[1],i[2],i[3])#逐条计算误差
error.append(t)#将误差加入列表error
print (np.sum(f(np.array(error))) / tmp.shape[0])#打印
转化数据集中的日期为pandas中的datatime类型
这里生成了2个新的特征,出发的星期和出发的小时,实际我们没有使用小时这个特征,同学们可以尝试用一下
def dateConvert(data,isTrain):
print ('convert string to datetime')
data['start_time'] = pd.to_datetime(data['start_time'])#转化开始时间
if isTrain:#如果是训练集
data['end_time'] = pd.to_datetime(data['end_time'])#转化结束时间
data['weekday'] = data['start_time'].dt.weekday + 1#生成新的一列,为星期几,weekday对应0到6,所以这里加1
data['hour']= data['start_time'].dt.hour
return data
合并经纬度
注意:更常用的处理方法为将经纬度转化为geohash编码,这里只是做了精度和合并处理
def latitude_longitude_to_go(data,isTrain):
tmp = data[['start_lat','start_lon']]#取出出发地经纬度
start_geohash = []#定义一个空列表
for t in tmp.values:#逐行遍历出发地经纬度
start_geohash.append(str(round(t[0],5)) + '_' + str(round(t[1],5)))#保留小数点后5位,将经纬度合并
data['startGo'] = start_geohash#生成新的一列,值为合并后的经纬度
if isTrain:#如果是训练集,对目的地经纬度做如上处理
tmp = data[['end_lat','end_lon']]
end_geohash = []
for t in tmp.values:
end_geohash.append(str(round(t[0],5))+ '_' + str(round(t[1],5)))
data['endGo'] = end_geohash#生成新的一列,值为合并后目的地经纬度
return data
统计用户去过最多的7个地方
def getMostTimesCandidate(candidate):
#取9月前的数据
mostTimeCandidate = candidate[candidate['start_time']<='2018-08-30 23:59:59']
#取'out_id','endGo','end_lat','end_lon','weekday'列
mostTimeCandidate = mostTimeCandidate[['out_id','endGo','end_lat','end_lon','weekday']]
#按车辆id、目的地、星期分组(agg)统计目的地出现的次数,放入生成的mostCandidateCount列
mostTimeCandidate_7 = mostTimeCandidate.groupby(['out_id','endGo','weekday'],as_index=False)['endGo'].agg({'mostCandidateCount':'count'})
#按出现次数和车辆id降序排列
mostTimeCandidate_7.sort_values(['mostCandidateCount','out_id'],inplace=True,ascending=False)
#按车辆id和星期分组,取去的最多的7条数据
mostTimeCandidate_7 = mostTimeCandidate_7.groupby(['out_id','weekday']).head(7)
return mostTimeCandidate_7
将合并后的经纬度拆分
def geoHashToLatLoc(data):
#取出目的地
tmp = data[['endGo']]
#设置空列表,预测的目的地纬度
predict_end_lat = []
#设置空列表,预测的目的地经度
predict_end_lon = []
#逐行遍历
for i in tmp.values:
#取出每行的经纬度,放入列表
lats, lons = str(i[0]).split('_')
predict_end_lat.append(lats)
predict_end_lon.append(lons)
#生成新的列
data['predict_end_lat'] = predict_end_lat
data['predict_end_lon'] = predict_end_lon
return data
计算两地点的距离
def calcGeoHasBetween(go1,go2):
latA, lonA = str(go1).split('_')
latB, lonB = str(go2).split('_')
distence = getDistance(float(latA), float(lonA), float(latB), float(lonB))
return distence
对整个数据集计算距离
def calcGeoHasBetweenMain(data):
distance = []
tmp = data[['endGo','startGo']]
for i in tmp.values:
distance.append(calcGeoHasBetween(i[0],i[1]) / 1000 )
data['distance'] = distance
return data
读取数据&特征工程
读取训练集
用1-6月去提取最常去的地方 用7月去训练
print ('begin')
train = pd.read_csv('train_new.csv',low_memory=False)#读取数据集,注意声明low_memory=False,否则会类型错误
print (train['start_time'].min(),train['start_time'].max())#打印训练集出发的最早时间和最晚时间
print (train[train['start_time']>'2018-06-30 23:59:59'].shape)#打印训练集出发时间在6月30日之后的数据的维度
print (train[train['start_time']<='2018-06-30 23:59:59'].shape)#打印训练集出发时间在6月30日及之前的数据的维度
begin
2018-01-01 00:01:58 2018-08-01 00:49:30
(205111, 8)
(1290703, 8)
test = pd.read_csv('test_new.csv',low_memory=False)#读取测试集
print (test['start_time'].min(),test['start_time'].max())
print (test.shape)
2018-09-01 00:32:42 2018-10-23 23:59:48
(58097, 5)
trainIndex = train.shape[0]#设置训练集索引
testIndex = test.shape[0]#设置测试集索引
print (trainIndex,testIndex)
1495814 58097
时间转化
train = dateConvert(train,True)
test = dateConvert(test,False)
convert string to datetime
convert string to datetime
合并经纬度
train = latitude_longitude_to_go(train,True)
test = latitude_longitude_to_go(test,False)
生成特征工程完成后的文件
train.to_csv('train1.csv',index=False)
test.to_csv('test1.csv',index=False)
userMostTimes3loc = getMostTimesCandidate(train)
val = train[train['start_time']>'2018-06-30 23:59:59']#取6月30日后数据做验证
#取出'r_key','out_id','end_lat','end_lon','weekday','startGo','endGo','start_lat','start_lon'列
val = val[['r_key','out_id','end_lat','end_lon','weekday','startGo','endGo','start_lat','start_lon']]
#将'endGo'重命名为'trueEndGo'
val.rename(columns={'endGo':'trueEndGo'},inplace=True)
#以'out_id','weekday'为关键字,对左表取交集,合并val和userMostTimes3loc表
val = pd.merge(val,userMostTimes3loc,on=['out_id','weekday'],how='left',copy=False)
#将val['endGo']中的目的地缺失值填充为起始地点
val['endGo'] = val['endGo'].fillna(val['startGo'])
#判断是否真的去了推荐的地点,如果去了为Ture,否则为False
val['flag1'] = val['trueEndGo'] == val['endGo']
#将True转化为1,False转化为0
val['flag1'] = val['flag1'].astype(int)
#计算距离
val = calcGeoHasBetweenMain(val)
#取出'r_key','out_id','weekday','startGo','start_lat','start_lon'列作为测试集
test = test[['r_key','out_id','weekday','startGo','start_lat','start_lon']]
#以'out_id','weekday'为关键字,对左表取交集,合并test和userMostTimes3loc表
test = pd.merge(test,userMostTimes3loc,on=['out_id','weekday'],how='left',copy=False)
#将测试集endGo列填充为出发地点
test['endGo'] = test['endGo'].fillna(test['startGo'])
#计算距离
test = calcGeoHasBetweenMain(test)
使用模型进行预测
这里使用了逻辑回归模型 同学们可以使用xgb,lgb等模型,相信会取得更好的成绩
feature = ['start_lat','start_lon','weekday','distance','mostCandidateCount']
#这里可以导入其它算法如svm,xgb,lgb等
from sklearn.linear_model import LogisticRegression
print ('training')
model= LogisticRegression()#使用逻辑回归模型训练,修改这里为sklearn中的其它模型
model.fit(val[feature].fillna(-1).values,val['flag1'].values)#x为fearture,将空值填充为-1,y值为val['flag1'],即是否推荐成功
pre = model.predict_proba(val[feature].fillna(-1).values)[:,1]#在验证集预测结果,为概率值
training
val_result = val[['r_key','endGo','end_lat','end_lon',]]
val_result['predict'] = pre
val_result = val_result.sort_values(['predict'],ascending=False)#按预测概率降序排列
val_result = val_result.drop_duplicates(['r_key'])#按r_key字段去重
val = geoHashToLatLoc(val)#将预测的结果切片为维度和经度
getDistanceFromDF(val)#计算在验证集上预测的得分
0.7703523722443778
subPre = model.predict_proba(test[feature].fillna(-1).values)[:,1]#在测试集上预测
test_result = test[['r_key','endGo']]#取出预测结果
test_result['predict'] = subPre#生成概率列
#按预测概率降序排列
test_result = test_result.sort_values(['predict'],ascending=False)
#按r_key字段去重
test_result = test_result.drop_duplicates(['r_key'])
#将预测的结果切片为维度和经度
test_result = geoHashToLatLoc(test_result)
#生成提交结果
submit = test_result[['r_key','predict_end_lat','predict_end_lon']]
submit.columns = ['r_key','end_lat','end_lon']
submit.to_csv('result.csv',index=False)