基于出租车数据的城市出行研究

项目介绍

对重庆市出租车GPS数据进行清洗和挖掘,通过提取出租车载客行程、交通小区划分、计算出行OD表等一系列操作量化城市不同区域流量,并挖掘出城市热点区域进行可视化。

数据介绍

本项目数据为重庆市2017年某两日内的出租车GPS记录数据,数据集为TXT文件,数据集压缩包可通过百度网盘自行获取
链接:https://pan.baidu.com/s/1ALzjE0De4g8CjhitmTCCBA
提取码:4vy0
获取数据集后将数据导入MySQL数据库中,然后对数据进行全面清洗,数据可能存在以下方面的问题:
1.记录重复
2.记录经纬度超出重庆市甚至中国经纬度范围
3.车辆时刻表在变而GPS坐标不变
4.车辆状态字段频繁跳动
上述问题几乎都可以用SQL语句解决,在此不做详细介绍。
已将出租车GPS数据存入到MySQL数据库中,并进行了全面的数据清洗,并按照车牌号进行排序后的数据如下:
在这里插入图片描述
字段有记录id、出租车id、日期、时间、经度、纬度、海拔、乘客状态(0表示出租车内无客,1表示有客)、GPS状态、时间日期组合字段datetime,原数据共有2700多万条记录,经过清洗后还剩1900多万条合理的记录数据。

1.提取出租车载客行程

python连接数据库,对表中相关字段进行读取,构造<车牌号id,上车时间,下车时间,上车经度,上车纬度,下车经度,下车纬度>

#连接数据库
import pymysql
import pandas as pd

db_config={
    'host':'localhost',
    'user':'root',
    'password':'123456',
    'db':'transdb',
    'charset':'utf8mb4'
}
connection=pymysql.connect(**db_config)
cursor=connection.cursor()
sql='select id,datetime,longitude,latitude,passenger from taxiorder'
cursor.execute(sql)
rows=cursor.fetchall()
df=pd.DataFrame(rows)
df.columns=['id','datetime','longitude','latitude','passenger']

在这里插入图片描述
上面表已经是根据车牌号和时间进行排序后的数据,接下来根据passenger载客状态字段提取出租车载客行程,passenger由0-1表示上车,由1-0表示下车。
创建一个新字段passenger1,它的值是passenger整体往上移一行,再创建一列passengerchange,它的值是passenger1-passenger,记录载客状态的变化。passengerchange为1的记录是上车记录,为-1的记录是下车记录,提取上车记录和下车记录

#创建一列passenger1,它的值是passenger整体往上移一行
df['passenger1']=df['passenger'].shift(-1)
print(df[['passenger','passenger1']])
#创建一列passengerchange,它的值是passenger1减去pasenger,表示载客状态的变化
df['passengerchange']=df['passenger1']-df['passenger']
#print(df['passengerchange'])
#提取上车记录和下车记录(即上车记录:passengerchange=1;下车信息:passengerchange=-1)
oddata=df[(df['passengerchange']==1)|(df['passengerchange']==-1)&(df['id']==df['id'].shift(-1))]

在这里插入图片描述
提取上面表中的车牌号、时间、经纬度和乘客状态变化字段,分配新字段为<id,STime,SLng,SLat,passengerchange>,然后STime、SLng和SLat字段分别整体向上平移一行后作为ETime、ELng和ELat,S代表每条行程的开始量,E代表结束量,然后取车牌号相等且passengerchange值为1的所有记录,得到所有出租车的载客行程

#进一步提取OD信息
oddata=oddata[['id','datetime','longitude','latitude','passengerchange']]
oddata.columns=['id','Stime','SLng','SLat','passengerchange']
oddata['Etime']=oddata['Stime'].shift(-1)
oddata['ELng']=oddata['SLng'].shift(-1)
oddata['ELat']=oddata['SLat'].shift(-1)
oddata=oddata[(oddata['id']==oddata['id'].shift(-1))&(oddata['passengerchange']==1)]
oddata=oddata.drop('passengerchange',axis=1)

在这里插入图片描述
选取其中一天的数据对不同时间段出租车行程进行统计

#不同时间段出租车行程统计
df=oddata.copy()
# 筛选2017-03-01的数据
df_20170301 = df[df['Stime'].dt.date == pd.to_datetime('2017-03-01').date()]

date_str='2017-03-01 '
# 定义时间段
time_periods = {
    '0:00-6:00': (pd.to_datetime(date_str+'00:00:00'), pd.to_datetime(date_str+'06:00:00')),
    '6:00-9:00': (pd.to_datetime(date_str+'06:00:00'), pd.to_datetime(date_str+'09:00:00')),
    '9:00-12:00': (pd.to_datetime(date_str+'09:00:00'), pd.to_datetime(date_str+'12:00:00')),
    '12:00-15:00': (pd.to_datetime(date_str+'12:00:00'), pd.to_datetime(date_str+'15:00:00')),
    '15:00-18:00': (pd.to_datetime(date_str+'15:00:00'), pd.to_datetime(date_str+'18:00:00')),
    '18:00-21:00': (pd.to_datetime(date_str+'18:00:00'), pd.to_datetime(date_str+'21:00:00')),
    '21:00-24:00': (pd.to_datetime(date_str+'21:00:00'), pd.to_datetime(date_str+'23:59:59'))
}
# 对每个时间段进行统计
for period_name, (start_time, end_time) in time_periods.items():
    print(start_time,end_time)
    # 筛选当前时间段的数据
    period_df = df_20170301[(df_20170301['Stime'] >= start_time) & 
                            (df_20170301['Stime'] < end_time)]
    # 统计行程数量
    trip_count = period_df.shape[0]
    print(f"时间段 {period_name} 的行程数量: {trip_count}")

在这里插入图片描述
可以根据上面表格初步看出高峰出行时间段

随意选取其中十条行程进行百度地图可视化,蓝色代表出发点,红色代表目的地
在这里插入图片描述
在这里插入图片描述

2.网格划分交通小区

采用网格划分的方式对所有行程区域进行交通小区划分,在这里选择将交通小区的经度均分成6份,将纬度均分成5份(根据范围大小和实际情况综合考虑划分),然后对交通小区从01L01到06L05进行编号,前两位表示经度位置,后两位表示纬度位置。

max_start_lng = oddata['SLng'].max()
max_end_lng = oddata['ELng'].max()
# 比较两个最大值,找出真正的最大经度坐标
lngmax = max_start_lng if max_start_lng > max_end_lng else max_end_lng

max_start_lat = oddata['SLat'].max()
max_end_lat = oddata['ELat'].max()
# 比较两个最大值,找出真正的最大纬度坐标
latmax= max_start_lat if max_start_lat > max_end_lat else max_end_lat

min_start_lng=oddata['SLng'].min()
min_end_lng=oddata['ELng'].min()
lngmin=min_start_lng if min_start_lng<min_end_lng else min_end_lng

min_start_lat=oddata['SLat'].min()
min_end_lat=oddata['ELat'].min()
latmin=min_start_lat if min_start_lat<min_end_lat else min_end_lat
# 网格划分参数
grid_count_lat = 6  # 纬度方向网格数量
grid_count_lng = 5  # 经度方向网格数量

# 计算每个网格的纬度和经度大小
lat_interval = (latmax - latmin) / grid_count_lat
lng_interval = (lngmax - lngmin) / grid_count_lng

# 构建网格编号和边界信息的字典
zone_boundaries = {}
for lat_index in range(grid_count_lat):
    for lng_index in range(grid_count_lng):
        # 计算当前网格的边界
        lat_start = latmin + lat_index * lat_interval
        lat_end = lat_start + lat_interval
        lng_start = lngmin + lng_index * lng_interval
        lng_end = lng_start + lng_interval
        
        # 创建网格编号,格式为 "XXLYY",其中XX是纬度索引,YY是经度索引
        grid_id = f"{lat_index + 1:02d}L{lng_index + 1:02d}"
        
        # 将网格的边界信息添加到zone_boundaries字典中
        zone_boundaries[grid_id] = {
            'lat_min': lat_start,
            'lat_max': lat_end,
            'lng_min': lng_start,
            'lng_max': lng_end,
        }

# 打印zone_boundaries字典以检查结果
for grid_id, boundaries in zone_boundaries.items():
    print(f"Grid ID: {grid_id}, Boundaries: {boundaries}")

在这里插入图片描述
然后将出租车载客行程分配到各个交通小区中

#出租车行程分配到各交通小区
import pandas as pd

# 定义一个函数来检查一个点是否在某个小区内
def check_zone(lat, lng, zone_boundary):
    return (zone_boundary['lat_min'] <= lat <= zone_boundary['lat_max']) and \
           (zone_boundary['lng_min'] <= lng <= zone_boundary['lng_max'])

# 遍历oddata DataFrame,为每条记录分配交通小区
oddata['origin_zone'] = None
oddata['destination_zone'] = None

for index, row in oddata.iterrows():
    origin_zone = next((zone for zone in zone_boundaries if check_zone(row['SLat'], row['SLng'], zone_boundaries[zone])), None)
    destination_zone = next((zone for zone in zone_boundaries if check_zone(row['ELat'], row['ELng'], zone_boundaries[zone])), None)
    
    oddata.at[index, 'origin_zone'] = origin_zone
    oddata.at[index, 'destination_zone'] = destination_zone

在这里插入图片描述

3.计算出行OD表

这里选取一天进行OD表的计算
利用pandas中的DataFrame结构存储OD表,列名和行名分别是各交通小区的编号,每个单元格对应一个交通小区对。
初始化所有单元格值为0,每条出租车行程的上车点和下车点对应一个交通小区对,读取一条行程数据,并找到其所对应的交通小区对,在表中对应交通小区对的数值上加1,对所有行程执行该操作,得到一天内所有行程的OD表,将其存储为csv文件。

import pandas as pd

#提取交通小区的编码(即字典zone_boundaries的键)
zone_ids = list(zone_boundaries.keys())

# 初始化OD表,所有值都设为0
od_table = pd.DataFrame(0, index=zone_ids, columns=zone_ids, dtype=int)

# 筛选出 'Stime' 在三月二号的数据
date_filter = oddata['Stime'].dt.month == 3
date_filter &= oddata['Stime'].dt.day == 2

# 使用筛选条件来选择对应的行
oddata_march_2st = oddata[date_filter]


# 遍历oddata_march_2st DataFrame,更新OD表
for index, row in oddata_march_2st.iterrows():
    origin_zone = row['origin_zone']
    dest_zone = row['destination_zone']
    od_table.at[origin_zone, dest_zone] += 1        

# 将OD表保存为CSV文件
csv_filename = 'D:/交通大数据/taxiod/taxi_od_table02.csv'
od_table.to_csv(csv_filename)

在这里插入图片描述
至此,出租车载客OD表提取结束。可以看到很多单元格为空,所以推断出租车载客起始-目的地对十分集中。

4.城市热点区域挖掘

上面的出租车载客OD表已经从一方面展示了热点交通区域,数值集较大的单元格即为热点交通小区对。为了进一步挖掘城市热点区域,在这里选取出租车载客行程目的地作为研究目标,采用DBSCAN算法对目的地坐标进行聚类,得到目的热点簇

from sklearn.cluster import DBSCAN
import pandas as pd

# 假设taxi_df是您的DataFrame
coords = oddata[['ELat', 'ELng']].values  # 提取上车点坐标

# 使用DBSCAN进行聚类
dbscan = DBSCAN(eps=0.01, min_samples=10)  # 根据需要调整eps和min_samples参数
oddata['cluster_label'] = dbscan.fit_predict(coords)

df=oddata.copy()
# 计算每个标签的个数
label_counts = df['cluster_label'].value_counts().reset_index()

# 给标签计数DataFrame添加列名
label_counts.columns = ['cluster_label', 'count']

# 将标签计数合并回原始DataFrame
# 这里我们使用left join,基于'label'列将计数信息合并回去
df_with_counts= df.merge(label_counts, on='cluster_label', how='left')
df_with_counts

在这里插入图片描述
聚类结果如下
在这里插入图片描述
标签为-1的是噪声点,不被归到任意簇中。
接下来调用百度地图API进行热点区域可视化。提取出租车行程表中的下车点经纬度坐标和所属簇的下车点总数count,count值大的簇下车点密集,为高密度区,count值小的点为低密度区,设定点的颜色由高密度到低密度用红色-橙色-绿色颜色过渡显示在百度地图上。

#将每个点和它的count写入到js文件中,绘制热力图
import json

# 选择需要的列
selected_data = df_with_counts[['ELng', 'ELat', 'count']]

# 构造JavaScript对象格式
points = []
for index, row in selected_data.iterrows():
    point = {
        "lng": row['ELng'],
        "lat": row['ELat'],
        "count": row['count']
    }
    points.append(point)

# 将对象数组转换为JSON字符串
json_points = json.dumps(points, ensure_ascii=False)

# 写入到.js文件中
js_content = f"var points = {json_points}\n"
with open('D:/交通大数据/points.js', 'w', encoding='utf-8') as js_file:
    js_file.write(js_content)

print("数据已写入points.js文件。")

编写html,调用百度地图API(可自行取百度地图控制台申请APK),引入上面的json文件对热点进行绘制

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=Jc3MjtqKpJpRNynmm5DnUR3wqxvQUwCj"></script>
    <script type="text/javascript" src="http://api.map.baidu.com/library/Heatmap/2.0/src/Heatmap_min.js"></script>
    <script type="text/javascript" src="D:/交通大数据/points.js"></script>
    <title>热力图功能示例</title>
    <style type="text/css">
		ul,li{list-style: none;margin:0;padding:0;float:left;}
		html{height:100%}
		body{height:100%;margin:0px;padding:0px;font-family:"微软雅黑";}
		#container{height:96%;width:100%;}
		#r-result{width:100%;}
    </style>	
</head>
<body>
	<div id="container"></div>
	<div id="r-result">
		<input type="button"  onclick="openHeatmap();" value="显示热力图"/><input type="button"  onclick="closeHeatmap();" value="关闭热力图"/>
	</div>
</body>
</html>
<script type="text/javascript">
    var map = new BMap.Map("container");          // 创建地图实例

    var point = new BMap.Point(106.538768,29.586074);
    map.centerAndZoom(point, 15);             // 初始化地图,设置中心点坐标和地图级别
    map.enableScrollWheelZoom(); // 允许滚轮缩放
        if(!isSupportCanvas()){
    	alert('热力图目前只支持有canvas支持的浏览器,您所使用的浏览器不能使用热力图功能~')
    }
	//详细的参数,可以查看heatmap.js的文档 https://github.com/pa7/heatmap.js/blob/master/README.md
	//参数说明如下:
	/* visible 热力图是否显示,默认为true
     * opacity 热力的透明度,1-100
     * radius 势力图的每个点的半径大小   
     * gradient  {JSON} 热力图的渐变区间 . gradient如下所示
     *	{
			.2:'rgb(0, 255, 255)',
			.5:'rgb(0, 110, 255)',
			.8:'rgb(100, 0, 255)'
		}
		其中 key 表示插值的位置, 0~1. 
		    value 为颜色值. 
     */
	heatmapOverlay = new BMapLib.HeatmapOverlay({"radius":5});
	map.addOverlay(heatmapOverlay);
	heatmapOverlay.setDataSet({data:points,max:184811});
	//var myarray=new Array();
	//myarray.push(new BMap.Point(106.542,29.590));
	//myarray.push(new BMap.Point(106.529,29.590));
	//myarray.push(new BMap.Point(106.529,29.577));
	//myarray.push(new BMap.Point(106.542,29.577));
	//myarray.push(new BMap.Point(106.542,29.590));
	//var polyline = new BMap.Polyline( myarray, {strokeColor:"blue", strokeWeight:5, strokeOpacity:0.8});   //创建折线		
	//map.addOverlay(polyline);
	//是否显示热力图
    function openHeatmap(){
        heatmapOverlay.show();
    }
	function closeHeatmap(){
        heatmapOverlay.hide();
    }
	closeHeatmap();
    function setGradient() {
    // 定义渐变色对象
    var gradient = {
        0: 'rgb(0, 255, 0)',  // 低密度区域为绿色
        0.5: 'rgb(255, 165, 0)', // 中等密度区域为橙色
        1: 'rgb(255, 0, 0)'     // 高密度区域为红色
    };
    
    // 应用渐变色配置到热力图
    heatmapOverlay.setOptions({"gradient": gradient});
}
	//判断浏览区是否支持canvas
    function isSupportCanvas(){
        var elem = document.createElement('canvas');
        return !!(elem.getContext && elem.getContext('2d'));
    }
</script>

可视化效果图如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到重庆市目的地热点区域主要集中在渝中区、渝北区、江北区、沙坪坝区、九龙坡区、南岸区和巴南区的部分沿江区域,北碚区的火车站和缙云山景区也较为密集。

总结

该项目涉及数据采集、清洗、提取转换、可视化、聚类挖掘等一系列数据处理和分析过程,是一个比较完整的大规模数据分析实例。
实验发现,城市中存在明显的出行热点区域,这些区域通常与商业中心、景点分布、交通枢纽等地理位置相吻合。通过聚类分析和百度地图可视化展示,能够识别出具有高出行到达密度的区域,为城市规划和交通管理提供了数据支持。
实际上,还可以以该数据为基础,对重庆市进行合理的路段划,采用合适的机器学习算法对全天24时隙不同路段的速度情况进行预测,对城市交通管制和交通规划也具有较强的实际意义。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值