项目5 PROJECT 5 基于排队时间预测的智能 导航推荐系统 本项目采用百度地图API获取步行时间,基于GBDT模型对排队时间进行预测。实现用户自主选择多个目的地,系统输出最佳路线规划的结果,并根据用户的选择给出智能化推荐。 5.1总体设计 本部分包括系统整体结构和系统流程。 5.1.1系统整体结构 系统整体结构如图51所示。 图51系统整体结构 5.1.2系统流程 系统流程如图52所示,路径规划流程如图53所示,计算路径耗时流程如图54所示。 图52系统流程 图53路径规划流程 图54计算路径耗时流程 5.2运行环境 本部分包括Python环境和Scikitlearn环境。 5.2.1Python环境 需要Python 3.6及以上配置,在Windows环境下推荐下载Anaconda完成Python所需的配置,下载地址为https://www.anaconda.com/,也可下载虚拟机在Linux环境下运行代码。 5.2.2Scikitlearn环境 安装CPU版本的Scikitlearn: pip install U ignoreinstalled scikitlearn,或者从Anaconda环境中直接搜索scikitlearn包进行下载、安装。 5.3模块实现 本项目包括6个模块: 数据预处理、客流预测、百度地图API调用、GUI界面设计、路径规划和智能推荐,下面分别给出各模块的功能介绍及相关代码。 5.3.1数据预处理 数据集链接地址为https://pan.baidu.com/s/1vRsr_ur7fgtSz7GHzlALSw 密码: x1r4。 乘车刷卡交易数据表train_data使用城市: use_city、线路名称: line_name、刷卡终端ID: terminal_id、卡片ID: card_id、发卡地: create_city、交易时间: deal_time、卡类型: card_type。 公交线路信息表line_desc线路名称: line_name、线路站点数量: stop_cnt、线路类型: _line_type。 广州市天气状况信息表weather_report,日期: data_time、天气状况: weather、气温: temperature、风向风力: wind_direction_force。 1. 加载数据集 使用DealTrainData.py程序加载数据集,初步查看数据集中所有项,以便后续选择需要的项作为特征,相关代码如下: import pandas as pd #乘车刷卡交易数据表 train_data = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/gd_train_data.txt',header=None,names=['use_city','line_name','terminal_id','card_id','create_city','deal_time','card_type']) #公交线路信息表 line_desc = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/gd_line_desc.txt',header=None,names=['line_name','stop_cnt','line_type']) #广州市天气状况信息表 weather_report = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/gd_weather_report.txt',header=None,names=['data','weather','temperature','wind_direction_force']) #虽然公交线路数据表中有21条线路,但是数据集只包含2条线路的详细数据 print(train_data) 初始train_data数据如图55所示。 print(line_desc) 图55初始train_data数据 初始line_desc数据如图56所示。 print(weather_report) 图56初始line_desc数据 初始weather_report数据如图57所示。 图57初始weather_report数据 2. 时间划分与保存 将deal_time按小时划分并保存,程序为DealTrainData.py,分别将线路6和线路11按小时划分,并保存至表格,相关代码如下: for i in ['线路6', '线路11']: train_data_lineX = train_data[train_data['line_name'] == i] #取原始乘车表中对应线路符合的数据 #把交易数据的日期和小时分成两个字段 train_data_lineX['date'] = train_data_lineX['deal_time'].apply(lambda x: str(x).split(' ')[0]) #日期 train_data_lineX['time'] = train_data_lineX['deal_time'].apply(lambda x: int(str(x).split(' ')[1].split(':')[0])) #小时 train_data_lineX_date_time = train_data_lineX.drop('deal_time', axis=1, inplace=False) #删除原始交易时间元素 #取交易时间在6~21点的数据 train_data_lineX_date_time_06 = train_data_lineX_date_time[6 <= train_data_lineX_date_time['time']] train_data_lineX_date_time_06_21 = train_data_lineX_date_time_06[train_data_lineX_date_time_06['time'] <= 21] #数据按小时划分并累计单位小时内客流量 lineX_passenger_hour = DataFrame(train_data_lineX_date_time_06_21.groupby(['date', 'time']).count()['card_id']).reset_index() #保存最终所需数据 if i == '线路6': lineX_passenger_hour.to_csv('C:/Users/99509/Desktop/信息系统设计/data/line6_passenger_hour.csv', header=1, index=0, encoding='utf-8') if i == '线路11': lineX_passenger_hour.to_csv('C:/Users/99509/Desktop/信息系统设计/data/line11_passenger_hour.csv', header=1, index=0, encoding='utf-8') 按小时划分后的客流数据如图58所示。 图58按小时划分后的客流数据 3. 处理天气预报数据 使用程序DealWeatherData.py将天气预报数据集中的天气weather、温度temperature、风力风向wind_direction_force数字标准化。 将天气按照以下方式划分并一一映射: 晴: 0,多云: 1,阴: 2,小雨: 3,小到中雨: 4,中雨: 5,中到大雨: 6,大雨: 7,大到暴雨: 8,霾: 9,阵雨: 10,雷阵雨: 11,取平均值作为特征。 将风力风向按照以下方式映射: 无持续风向≤3级: 0,无持续风向微风转3~4级: 1,北风微风转3~4级: 1,东北风3~4级: 2,北风3~4级: 2,东南风3~4级: 2,东风4~5级: 3,北风4~5级: 3,求平均值作为特征。 相关代码如下: #日期标准化 eg: '10/10'转换为'10','10' '1/1'转换为'01','01' def changeDate(date): dateList = date.split('/') if int(dateList[1]) < 10: month = '0' + dateList[1] else: month = dateList[1] if int(dateList[2]) < 10: day = '0' + dateList[2] else: day = dateList[2] return dateList[0] + month + day #转换天气预报数组中的日期格式 weather_report['datestr'] = weather_report['date'].apply(lambda x: changeDate(x)) #将相应字段分开 weather_report['weather_d'] = weather_report['weather'].apply(lambda x: x.split('/')[0]) #白天 weather_report['weather_n'] = weather_report['weather'].apply(lambda x: x.split('/')[1]) #晚上 weather_report['temperature_h'] = weather_report['temperature'].apply(lambda x: int(re.sub(r'\D', '', x.split('/')[0]))) #最高温 weather_report['temperature_l'] = weather_report['temperature'].apply(lambda x: int(re.sub(r'\D', '', x.split('/')[1]))) #最低温 weather_report['wind_direction_force_d'] = weather_report['wind_direction_force'].apply(lambda x: x.split('/')[0]) #白天风向风力 weather_report['wind_direction_force_n'] = weather_report['wind_direction_force'].apply(lambda x: x.split('/')[1]) #晚上风向风力 weather_report['temperature_average'] = (weather_report['temperature_h'] + weather_report['temperature_l']) / 2.0 #平均温度 weather_report['temperature_abs'] = abs(weather_report['temperature_h'] - weather_report['temperature_l']) #温差 #降低风向影响,着重考虑风力影响,按照以下形式将风力、风向数字化 print(pd.concat([weather_report['wind_direction_force_d'], weather_report['wind_direction_force_n']],ignore_index=True).drop_duplicates()) #数组合并去冗余,得到所有的风力、风向 #种类 windmap = {'无持续风向≤3级': 0, '无持续风向微风转3~4级': 1, '北风微风转3~4级': 1, '东北风3~4级': 2, '北风3~4级': 2, '东南风3~4级': 2, '东风4~5级': 3,'北风4~5级': 3} #划分 weather_report['wind_direction_force_d_map'] = weather_report['wind_direction_force_d'].map(windmap) #将原始数据替换为标准化数据 weather_report['wind_direction_force_n_map'] = weather_report['wind_direction_force_n'].map(windmap) #将原始数据替换为标准化数据 weather_report['wind_average'] = (weather_report['wind_direction_force_d_map'] + weather_report['wind_direction_force_n_map']) / 2.0 #做风力、风向平均值 weather_report['wind_abs'] = abs(weather_report['wind_direction_force_d_map'] - weather_report['wind_direction_force_n_map']) #做风力、风向差 #将天气按照以下形式数字化 print(pd.concat([weather_report['weather_d'], weather_report['weather_n']], ignore_index=True).drop_duplicates()) #数据组合并去冗余,得到所有天气种类 weathermap = {'晴': 0, '多云': 1, '阴': 2, '小雨': 3, '小到中雨': 4, '中雨': 5, '中到大雨': 6, '大雨': 7, '大到暴雨': 8, '霾': 9, '阵雨': 10,'雷阵雨': 11}#划分 weather_report['weather_d_map'] = weather_report['weather_d'].map(weathermap) #将原始数据替换为标准化数据 weather_report['weather_n_map'] = weather_report['weather_n'].map(weathermap) #将原始数据替换为标准化数据 weather_report['weather_average'] = (weather_report['weather_d_map'] + weather_report['weather_n_map']) / 2.0 #做天气平均值 weather_report['weather_abs'] = abs(weather_report['weather_d_map'] - weather_report['weather_n_map']) #做天气差 #删除多余项,留下标准化日期、风力风向、风力风向均值、风力风向差、天气、天气均值、天气差 weather_report_result = weather_report.drop(['date', 'weather', 'temperature', 'wind_direction_force', 'weather_d', 'weather_n', 'wind_direction_force_d','wind_direction_force_n'], axis=1, inplace=False) #输出 weather_report_result = weather_report_result.reset_index(drop=True) #重置索引 print(weather_report_result) for i in range(len(weather_report_result)): weather_report_result.loc[i, 'datestr'] = pd.to_datetime(weather_report_result.loc[i, 'datestr'],format='%Y%m%d').strftime('%Y-%m-%d') #将时间格式化为year-month-day weather_report_result.to_csv('C:/Users/99509/Desktop/信息系统设计/data/weather_report_result.csv', header=0, index=0, encoding='utf-8') 天气预报处理后的数据如图59所示。 图59处理后的天气预报数据 4. 增加特征 使用程序TestDataResult.py增加是否为节假日和星期几的特征,相关代码如下: weather_report_data = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/weather_report_result.csv', header=None,names=['date', 'temperature_h', 'temperature_l', 'temperature_average', 'temperature_abs', 'wind_d_map', 'wind_n_map', 'wind_average', 'wind_abs', 'weather_d_map', 'weather_n_map', 'weather_average', 'weather_abs']) holiday = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/date_holiday.txt', header=None, names=['date', 'isholiday']) #日期,是否是假期 testdate = pd.date_range('2014-12-25', '2014-12-31') #测试集日期范围: 2014.12.25-2014.12.31 #获得测试集完整日期时间范围 datetimelist = [] for idate in testdate: datetime = pd.date_range(str(idate) + ' 6:00', str(idate) + ' 21:00', freq='H') #测试集时间范围: 6: 00~21: 00 datetimelist.append(DataFrame(datetime)) datetimeDf = pd.concat(datetimelist, ignore_index=True) #忽略索引连接数组 datetimeDf.columns = ['datetime'] #修改列名为datetime datetimeDf['date'] = datetimeDf['datetime'].apply(lambda x: str(x).split(' ')[0]) #分离出日期 datetimeDf['time'] = datetimeDf['datetime'].apply(lambda x: int(str(x).split(' ')[1].split(':')[0])) #分离出时间 datetimeDf_date_time = datetimeDf.drop('datetime', axis=1, inplace=False) #保存测试所有时间段: 7天*15小时 #判断是否为节假日,数据预处理 for i in ['6', '11']: lineX_passenger_hour_path = "C:/Users/99509/Desktop/信息系统设计/data/line%s_passenger_hour.csv" % i #得到线路6/11的训练集中每小时客流量的路径(因为%s在字符串中,所以%i表示用i替换s) lineX_passenger_hour = pd.read_csv(lineX_passenger_hour_path) #得到线路6或11训练集中每小时的客流量 train_passenger_test = pd.concat([lineX_passenger_hour, datetimeDf_date_time], ignore_index=True) #连接测试集和训练集中线路6或11的每小时客流量 #取线路6或11的交集(得到完整数据: 日期、最高温、最低温、平均温度、温差、标准化白天风 #力风向、标准化夜晚风力风向、平均风力风向、风力风向差、标准化白天天气、标准化夜晚天气、 #平均天气、天气差、客流量) test_data_weather = pd.merge(train_passenger_test, weather_report_data, on='date', how='left') test_data_weather_holiday = pd.merge(test_data_weather, holiday, on='date', how='left') #测试集中节假日的数据 test_data_weather_holiday['dayofweek'] = test_data_weather_holiday['date'].apply(lambda x: pd.to_datetime(x).dayofweek) #添加是否为周末元素至测试集 5. 合并特征值 使用程序finaldata.py数据预处理最后一步,得到7项特征值(平均温度temperature_average、平均风力风向wind_average、平均天气weather_average、单位时间客流card_id、时间time、是否是节假日isholiday、星期几dayofweek),将所有特征值放入一个表格内。相关代码如下: weather_report_data = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/weather_report_result.csv', header=None,names=['date', 'temperature_h','temperature_l','temperature_average','temperature_abs', 'wind_d_map','wind_n_map','wind_average','wind_abs','weather_d_map','weather_n_map','weather_average','weather_abs']) #读取数据8.1~1.31 line6_weather = weather_report_data[:146] #读取数据8.1~12.24 (天) line6_dateinfo = pd.read_csv('C:/Users/99509/Desktop/信息系统设计/data/line6_train_data_no_dum_scale.csv',header=None,names=['card_id','date','time','temperature_h','temperature_l','temperature_average','temperature_abs', 'wind_d_map','wind_n_map','wind_average','wind_abs','weather_d_map','weather_n_map','weather_average','weather_abs', 'isholiday','dayofweek' ]) #读取数据8.1~12.24 (小时) #print(line6_dateinfo)调试代码 line6_weather_date = pd.DataFrame() line6_weather_date['card_id']=line6_dateinfo['card_id'].drop(0) line6_weather_date['time']=line6_dateinfo['time'] line6_weather_date['date']=line6_dateinfo['date'] line6_weather_date['isholiday']=line6_dateinfo['isholiday'] line6_weather_date['dayofweek']=line6_dateinfo['dayofweek'] line6_weather_date_f=pd.merge(line6_weather,line6_weather_date) line6_weather_date_final=line6_weather_date_f.drop(['date', 'temperature_h','temperature_l','temperature_abs','wind_d_map','wind_n_map','wind_abs','weather_d_map','weather_n_map','weather_abs'], axis=1,inplace=False) line6_weather_date_final.to_csv('C:/Users/99509/Desktop/信息系统设计/修改/line6_weather_date_final.csv',header=1, index=0, encoding='utf-8') 所有特征数据如图510所示。 图510所有特征数据 5.3.2客流预测 选用GBDT建立模型。GBDT通过多轮迭代,每轮迭代产生一个弱分类器,每个分类器在上一轮的残差基础上进行训练。 用数据处理阶段准备好的特征和标签来训练模型,保存以便后续使用。同时,自定义损失函数,为预测数据可靠性后续调参提供保障。 采用GBDT模型进行预测,输入当前天气、温度、风力风向、日期(是否是节假日、星期几)和时间即可得出当前客流量。当前客流量在后续预测排队时做一系列操作即可转换为排队时间。 1. 创建并保存模型 使用程序gbdtmodel.py建立GBDT模型。 #建立GBDT模型 gbdt=ensemble.GradientBoostingRegressor(learning_rate=0.5, n_estimators=80, max_depth=3) #用GridSearchCV进行调参后确定的参数 #print(cross_val_score(gbdt, features, lables, scoring=score, cv=cv)) gbdt.fit(features, lables) gbdt_model_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/模型/gbdt_6.model" joblib.dump(gbdt, gbdt_model_path) 2. 损失函数 使用程序error.py自定义损失函数。 #损失函数: 偏差=|真实值-预测值|/真实值 def error(true_labels, predict_labels): deviation = abs(true_labels - predict_labels) / true_labels return deviation.mean() 3. 测试集测试 使用程序predict.py测试并得出损失函数。 #将12.25~12.31的数据当作测试集测试 line6_passenger_hour_test_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/过程数据/line6_passenger_hour_test.csv" line_passenger_hour_test = pd.read_csv(line6_passenger_hour_test_path) test_labels = line_passenger_hour_test['card_id'] #得到测试集标签 gbdt_6_model_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/模型/gbdt_6.model" gbdt_model = joblib.load(gbdt_6_model_path) #载入模型 test_data_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/过程数据/test_line6_weather_date_final.csv" test_data = pd.read_csv(test_data_path) features =test_data #得到测试集特征 gbdt_predict_labels = gbdt_model.predict(features) #预测 plt.plot(gbdt_predict_labels,label=u'predict_value') plt.plot(test_labels,label=u'test_value') plt.xlabel("No.") #xlabel、ylabel: 分别设置X、Y轴的标题文字 plt.ylabel("passenger crowding") plt.legend(loc=0,ncol=2) plt.show() err = error(gbdt_predict_labels, test_labels) #得出损失函数 print('错误率为:'+str(err*100)+'%') 模型错误率如图511所示。 图511模型错误率 4. 自定义特征并预测 使用程序singlepredict.py自定义一个特征并预测。 #输入特征 test_data1=pd.DataFrame({'temperature_average':[20], 'wind_average':[0], 'weather_average':[6], 'time':[12], 'isholiday':[0], 'dayofweek':[1]}) features =test_data1 gbdt_predict_labels = gbdt_model.predict(features) #预测 print(gbdt_predict_labels) 自定义特征测试输出结果如图512所示。 图512自定义测试输出结果 5.3.3百度地图API调用 一是申请密钥(AK); 二是调用地址编码服务,获取每个地点的经纬度; 三是调用轻量级路线规划服务,获取任意两点之间的步行时间,最后获取输出。 1. 申请密钥 申请密钥网址为http://lbsyun.baidu.com,登录百度账号注册成为开发者,找到控制台→应用管理→我的应用,单击“创建应用”,如图513所示。 图513创建应用 依次输入应用名称、应用类型和启用服务,设置请求校验方式为IP白名单校验,设置IP白名单为0.0.0.0/0。 申请应用AK的详细信息如图514所示。 图514应用AK的详细信息 2. 地址编码服务 相关操作如下: (1) 地址编码服务是将结构化数据(如: 北京市海淀区西土城路10号)转换为对应坐标点(经纬度)的功能。 接口为http://api.map.baidu.com/geocoding/v3/?address=北京市海淀区西土城路10号&output=json&ak=您的ak&callback=showLocation,GET请求。 (2) 用熟知的场所确保精度满足需求,明确各参数含义。 (3) 获取所需经纬度,将每点的坐标写入文档。 #调用地址编码服务获取经纬度 getjingwei.py import requests import json url = 'http://api.map.baidu.com/geocoding/v3/' params = { 'address' : '北京欢乐谷-极速飞车',#输入地点 'ak' : 'YdBcaxxxxxxxxxxxxxxxxxxxxxG', #百度密钥 'output': 'json'#输出结果设置为json格式 } res = requests.get(url,params) jd = json.loads(res.text) #将json格式转化为Python字典 print(jd) coords = jd['result']['location'] print(coords) 运行结果如图515所示。 图515运行结果 得到起点、终点坐标.xlsx文件,如图516所示。 图516起点、终点坐标 3. 轻量级路线规划服务 相关操作如下: (1) 支持驾车、骑行、步行、公交路线规划。根据起点、终点坐标(经纬度)可规划步行路线的耗时。接口地址为 http://api.map.baidu.com/routematrix/v2/walking?origin=40.01116,116.339303&destination=39.936404,116.452562&ak=您的AK,GET请求。 (2) 获取任意两点之间的步行时间,写入文档。 #调用轻量级路线规划服务获取步行时间getdistance.py import pandas import csv import json from urllib.request import urlopen #原数据文件格式: 序号+起点纬度+起点经度+终点纬度+终点经度 origin_path = 'D:/~STUDY~/Grade3/信息系统设计/final_files/data/过程数据/起点终点坐标.xlsx' #原始坐标文件路径 result_path = r'D:/~STUDY~/Grade3/信息系统设计/final_files/data/过程数据/walk_result_raw.csv' #爬取数据文件保存路径 #声明坐标格式,bd09ll(百度经纬度坐标);bd09mc(百度摩卡托坐标);gcj02(国测局加密坐标), #WGS84坐标(GPS设备获取的坐标) cod = r"&coord_type=bd09ll" #AK为从百度地图网站申请的密钥 AK = ['YdBcxxxxxxxxxxxxxxxxxxxG',] dfBase = pandas.read_excel(origin_path, names=['序号','起点纬度','起点经度','终点纬度','终点经度']) dfBase.head() dataList = [] #存储获取的路线数据 akn = 0 #使用第几个ak for i in range(len(dfBase)): print(i) ak = AK[akn] out_lat = dfBase.at[i,'起点纬度'] out_lng = dfBase.at[i,'起点经度'] des_lat = dfBase.at[i,'终点纬度'] des_lng = dfBase.at[i,'终点经度'] #获取步行路径 url_walk=r"http://api.map.baidu.com/routematrix/v2/walking?output=json&origins={0},{1}&destinations={2},{3}&{4}&tactics=11&ak={4}".format(out_lat,out_lng,des_lat,des_lng,ak) result_walk = json.loads(urlopen(url_walk).read()) #json转dict status_walk = result_walk['status'] print('ak密钥: {0} 获取步行路线状态码status: {1}'.format(ak, status_walk)) if status_walk == 0: #状态码为0: 无异常 timesec_walk = result_walk['result'][0]['duration']['value']#耗时(秒) elif status_walk == 302 or status_walk == 210 or status_walk == 201: #302:额度不足;210:IP验证未通过 timesec_walk = 'AK错误' akn += 1 ak = AK[akn] else: timesec_walk = '请求错误' dataList.append([ak,status_walk,timesec_walk]) dfAll = pandas.DataFrame(dataList, columns=['ak','status_walk','timesec_walk']) dfAll.to_csv(result_path) #将生成的cvs保存到路径 walk_time_result.csv文件结果,如图517所示。 图517walk_time_result.csv结果 5.3.4GUI设计 导入Tkinter包进行GUI设计。用户通过下拉菜单手动选择当前位置,勾选目的地。单击 “确认”按钮后调用create()函数跳转至最佳路线输出界面。 1. 手绘地图导入 相关代码如下: #背景初始化部分 window = tkinter.Tk() window.geometry('900x500') window.title('智能导航系统——欢乐谷') #显示图片 #通过PIL打开图片 img = Image.open('C:/Users/99509/Desktop/map.jpg') img = img.resize((750,500),Image.ANTIALIAS) #Image.ANTIALIAS使图片不模糊 #通过PIL生成PhotoImage对象,即可正常加载 photo = ImageTk.PhotoImage(img) imageLabel = Label(window, image=photo) imageLabel.pack(side=LEFT) 2. 下拉菜单设计 相关代码如下: ss=Label(window,text="您当前位置为",justify=RIGHT) ss.pack() comvalue=tkinter.StringVar()#窗体自带的文本,新建一个值 comboxlist=ttk.Combobox(window,textvariable=comvalue) #初始化 comboxlist["values"]=("入口","玛雅天灾","雪域金翅","异域魔窟","奥德赛之旅","太阳神车","天地双雄","能量风暴") all_paths_first="" def xFunc(event): global all_paths_first #print(comboxlist.get()) #当前位置 if comboxlist.get()=="入口": all_paths_first=0 elif comboxlist.get()=="玛雅天灾": all_paths_first=1 elif comboxlist.get()=="雪域金翅": all_paths_first=2 elif comboxlist.get()=="异域魔窟": all_paths_first=3 elif comboxlist.get()=="奥德赛之旅": all_paths_first=4 elif comboxlist.get()=="太阳神车": all_paths_first=5 elif comboxlist.get()=="天地双雄": all_paths_first=6 elif comboxlist.get()=="能量风暴": all_paths_first=7 comboxlist.bind("<>",xFunc) #绑定事件,(下拉列表框被选中时,绑定xFunc()函数) comboxlist.pack() 3. 复选框设计 复选框格式是一致的,为了界面简洁,只展示一个复选框设计。 #count判断是否需要调用百度地图API,奇数表示选中 count1=0 N = [] def myEvent1(): global count1 if count1%2==0: count1+=1 else: count1+=1 #项目选择部分 v1=IntVar() c1=Checkbutton(window,text='入口',variable=v1,justify=RIGHT,command=myEvent1) #存放选中状态 c1.pack() l1=Label(window,textvariable=v1,justify=RIGHT) l1.pack() #未选中显示为0,选中显示为1 4. 最佳路线结果输出界面设计 相关代码如下: total=[] #存放被选择的目的地 total_n=[] #存放未被选择的地点 #选择地点完成后调出最佳路线结果输出界面 def create(): window2 = tkinter.Toplevel() #新建子窗口window2 window2.geometry('300*200') window2.title('计算页面') s0=Label(window2,text="您选择了: ") #文字框s0 s0.pack() #选择目的地 if count1%2==1: #如果被选中 N.append(0) #N列表中增加0 total.append("入口") #total列表中增加"入口" else: #如果未被选中 N_notchoose.append(0) #N_notchoose列表中增加0 total_n.append("入口") #total_n列表中增加"入口" if count2%2==1:#以下同理 N.append(1) total.append("玛雅天灾") else: N_notchoose.append(1) total_n.append("玛雅天灾") if count3%2==1: N.append(2) total.append("雪域金翅") else: N_notchoose.append(2) total_n.append("雪域金翅") if count4%2==1: N.append(3) total.append("异域魔窟") else: N_notchoose.append(3) total_n.append("异域魔窟") if count5%2==1: N.append(4) total.append("奥德赛之旅") else: N_notchoose.append(4) total_n.append("奥德赛之旅") if count6%2==1: N.append(5) total.append("太阳神车") else: N_notchoose.append(5) total_n.append("太阳神车") if count7%2==1: N.append(6) total.append("天地双雄") else: N_notchoose.append(6) total_n.append("天地双雄") if count8%2==1: N.append(7) total.append("能量风暴") else: N_notchoose.append(7) total_n.append("能量风暴") s_total=Label(window2,text=total) #输出用户选中的地点 s_total.pack() s9=Label(window2,text="最佳路线为: ") #文字框s9 s9.pack() get_time(N) #调用get_time进行运算 PLAN=[] #用于存放计划路线 #输出路线 for i in range(0,len(N)+1): if PATH[i]==0: PLAN.append("入口") elif PATH[i]==1: PLAN.append("玛雅天灾") elif PATH[i]==2: PLAN.append("雪域金翅") elif PATH[i]==3: PLAN.append("异域魔窟") elif PATH[i]==4: PLAN.append("奥德赛之旅") elif PATH[i]==5: PLAN.append("太阳神车") elif PATH[i]==6: PLAN.append("天地双雄") else: PLAN.append("能量风暴") s10=Label(window2,text=PLAN) #输出计划的最佳路线 s10.pack() s11=Label(window2,text="\n大约耗时: "+str(round(TIME[0],2))+"小时") #输出对应的总耗时 s11.pack() b2=Button(window2,text='猜你喜欢',command=create_guess) #新建"猜你喜欢"按钮进入智能推荐模块 b2.pack() b3=Button(window2,text='退出',command=window.destroy)#“退出”按钮,退出程序 b3.pack() 5. 智能推荐结果输出设计 相关代码如下: #创建智能推荐的页面 def create_guess(): window3 = tkinter.Toplevel() #新建子窗口window3 window3.geometry('400x250') window3.title('猜你喜欢') s0=Label(window3,text="您选择了: ")#文字框s0 s0.pack() s_total=Label(window3,text=total) #输出用户选中的地点 s_total.pack() s7=Label(window3,text="为您推荐: ") #文字框s7 s7.pack() try: guess_time(N) #调用guess_time函数,计算推荐地点及推荐最佳路线 except: tkinter.messagebox.showwarning("提示", "选取地点有误!\n请退出重新选取") #加入异常处理,跳出提示框 PLAN=[] #存放推荐最佳路线 #输出路线 for i in range(0,len(N)+2): if PATH_guess[i]==0: PLAN.append("入口") elif PATH_guess[i]==1: PLAN.append("玛雅天灾") elif PATH_guess[i]==2: PLAN.append("雪域金翅") elif PATH_guess[i]==3: PLAN.append("异域魔窟") elif PATH_guess[i]==4: PLAN.append("奥德赛之旅") elif PATH_guess[i]==5: PLAN.append("太阳神车") elif PATH_guess[i]==6: PLAN.append("天地双雄") else: PLAN.append("能量风暴") s8=Label(window3,text=location[recommend]) #输出推荐的地点 s8.pack() s9=Label(window3,text="加入推荐地点的最佳路线为: ") #文本框s9 s9.pack() s10=Label(window3,text=PLAN) #输出推荐的最佳路线 s10.pack() s11=Label(window3,text="\n大约耗时: "+str(round(TIME_guess[0],2))+"小时") #输出推荐的总耗时 s11.pack() s12=Label(window3,text="预计比原路线多花费: "+str(round((TIME_guess[0]-TIME[0]),2))+"小时") #输出推荐一个地点后的总耗时与之前总耗时的差 s12.pack() b3=Button(window3,text='返回',command=window3.destroy) #"返回"按钮,返回上一界面 b3.pack() b4=Button(window3,text='退出',command=window.destroy) #"退出"按钮,退出程序 b4.pack() 6. 界面展示 GUI主页面如图518所示,下拉菜单如图519所示。 图518GUI主界面 图519下拉菜单 5.3.5路径规划 通过调用百度地图API模块产生节点之间的步行时间矩阵和客流模型,应用穷举法设计算法,得出最佳路线规划。 GUI界面下拉菜单中选择当前位置作为所有可能线路的起点,复选框中目的地作为节点,调用itertools库中的permutations()函数进行全排列,路线每到节点时间、排队时间输出在cmd窗口,如图520所示,最佳路线和总耗时输出在GUI模块设计的界面当中,如图521所示。 图520cmd中显示所有路线及耗时 图521GUI界面显示最佳路线 相关代码如下: #获得最佳路线和出游时间 def get_time(N): global TIME global PATH gbdt_6_model_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/模型/gbdt_6.model" gbdt_model = joblib.load(gbdt_6_model_path) #加载模型 #初始化客流模型顺序不可改变 features=pd.DataFrame({'temperature_average':[2], 'wind_average':[0], 'weather_average':[6], 'time':[0], 'isholiday':[0], 'dayofweek':[1]}) #N = [0,1,2,3] #GUI界面选择 n=len(N)+1 print(all_paths_first) all_paths_tuple=list(itertools.permutations(N,n-1)) #得到的全排列是元组tuple all_paths=[] #tuple类型不能插入操作,所以转换成list for i in range(0,math.factorial(n-1)): all_paths.append(list(all_paths_tuple[i])) all_paths[i].insert(0,all_paths_first) path = list() all_time = float('inf') #无穷大 #第i条路线 for i in range(0,math.factorial(n-1)): time = 0 #(h) nowtime = 9 #可以获取当前时间,需要时间表示的转换(h) print("第"+str(i+1)+"条路线为: "+str(all_paths[i])) #第i条路线的第j个地点 for j in range(0,n-1): time = time + (walk_time[all_paths[i][j]][all_paths[i][j+1]]/3600) #到达一个地点的时间 nowtime = nowtime+time features['time'] = nowtime gbdt_predict_labels = gbdt_model.predict(features)/2000 #客流量/2000当作时间(h) print("现在的时间是: "+str(nowtime)+" 此时"+str(j)+"点的排队时间为: "+str(gbdt_predict_labels)+"h") time = time + gbdt_predict_labels + (PLAYTIME[j]/60) print("耗时为: "+str(time)) print("=============================================================") if all_time>time: all_time = time path = all_paths[i] print("耗时最短的路线为: "+str(path)+" 耗时为: "+str(all_time)) PATH = path TIME = all_time 5.3.6智能推荐 系统将用户未选择的地点一次分别加入已选择的队列中进行运算,其基本思路与最佳路线规划模块一致,采用穷举法得到所有路线及其总耗时,最后将它们输出,实现智能推荐。相关代码如下: #智能推荐"猜你喜欢"的运算 def guess_time(N): global TIME_guess global PATH_guess global recommend path_whole = list() all_time_whole = float('inf') #无穷大 gbdt_6_model_path = "D:/~STUDY~/Grade3/信息系统设计/final_files/data/模型/gbdt_6.model" gbdt_model = joblib.load(gbdt_6_model_path) #加载模型 features=pd.DataFrame({'temperature_average':[2], 'wind_average':[0], 'weather_average':[6], 'time':[0], 'isholiday':[0], 'dayofweek':[1]}) #N = [0,1,2,3] #GUI界面选择 print('起点是: '+str(all_paths_first)) for nn in range(0,len(N_notchoose)): #把未选择的地点分别加入已选择的队列中进行计算 guess_path = [] guess_path = N[:] guess_path.append(N_notchoose[nn]) #构建推荐的列表 print(guess_path) if(all_paths_first in guess_path): pass#如果未选择的地点是起点,则跳过 else: all_paths_tuple=list(itertools.permutations(guess_path,len(guess_path))) #得到的全排列是元组tuple all_paths=[] #tuple类型不能插入操作,所以转换成list #for i in range(0,math.factorial(n-1)):调试代码 for i in range(0,math.factorial(len(guess_path))): all_paths.append(list(all_paths_tuple[i])) all_paths[i].insert(0,all_paths_first) path = list() all_time = float('inf') #无穷大 #第i条路线 for i in range(0,math.factorial(len(guess_path))): time = 0 #(h) nowtime = 9 #可以获取当前时间,需要时间表示的转换(h) print("第"+str(i+1)+"条路线为: "+str(all_paths[i])) #第i条路线的第j个地点 for j in range(0,len(guess_path)): time = time + (walk_time[all_paths[i][j]][all_paths[i][j+1]]/3600) #到达一个地点的时间 nowtime = nowtime+time features['time'] = nowtime gbdt_predict_labels = gbdt_model.predict(features)/2000 #客流量/2000当作时间(h) print("现在的时间是: "+str(nowtime)+" 此时"+str(j)+"点的排队时间为: "+str(gbdt_predict_labels)+"h") time = time + gbdt_predict_labels + (PLAYTIME[j]/60) print("耗时为: "+str(time)) print("") if all_time>time: all_time = time path = all_paths[i] if all_time_whole>all_time: all_time_whole = all_time path_whole=path[:] recommend = N_notchoose[nn] print("推荐第"+str(recommend)+"个景点") print("推荐第"+str(recommend)+"个景点") print("智能推荐耗时最短的路线为a: "+str(path_whole)+"耗时为: "+str(all_time_whole)) PATH_guess = path_whole TIME_guess = all_time_whole 在cmd窗口输出所有路线及该路线每到一节点的时间,如图522所示。 图522cmd中显示所有路线及耗时 在GUI模块设计的界面中显示最佳路线和总耗时,如图523所示。 图523最佳路线 5.4系统测试 本部分包括训练准确率、测试效果及程序应用。 5.4.1训练准确率 经过调参后,测试的错误率为8.236%,如图524所示,准确率达到91.7%以上,意味着预测模型训练比较成功。 图524模型错误率 5.4.2测试效果 将数据代入模型进行测试,分类的标签与原始数据进行显示和对比,如图525所示,可以验证: 模型可以实现预测客流量的功能。 图525模型训练效果 5.4.3程序应用 打开gui.py,单击“运行”按钮,初始界面如图526所示。 图526程序初始界面 界面从左至右,分别是一张北京欢乐谷园区的手绘地图,一个下拉菜单、一列目的地复选框。在下拉菜单中选择当前位置,复选框中选择目的地,如图527所示。 图527选择目的地界面 单击“确定”按钮后,cmd窗口中会输出所有可能路线、耗时以及最短的路线、耗时,如图528所示。 图528最短路线规划在cmd窗口中的输出 单击“确定”按钮后出现第二个GUI界面,显示用户选择的目的地、经系统计算后的最佳路线及总耗时,如图529所示。 图529最佳路线规划界面 单击“退出”按钮即可退出程序。单击“猜你喜欢”按钮进入智能推荐模块,cmd窗口中会输出所有可能路线及耗时,并且输出推荐最佳路线和耗时,如图530所示。 单击“猜你喜欢”按钮后,会出现第三个GUI界面,显示用户选择的目的地、系统推荐的地点、系统计算后的推荐最佳路线及总耗时,如图531所示。 若用户选择的起点与目的地有重复,导致程序出现异常,则跳出异常提示框,如图532所示。 总体测试结果如图533所示。 图530智能推荐在cmd窗口中的输出 图531智能推荐的计算界面 图532异常提示界面 图533总体测试结果示例