ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python出租车GPS轨迹分析实战:从数据清洗到OD提取与可视化

Python出租车GPS轨迹分析实战:从数据清洗到OD提取与可视化 简介这是一份面向计算机相关专业学生与项目实战学习者的出租车轨迹数据分析与可视化高分项目源码评审分98分可作为课程设计、期末大作业或简历项目参考。资源以Python为核心结合transbigdata等工具围绕上海、深圳两地的出租车GPS数据展开涵盖数据清洗、轨迹处理、时空分析与可视化呈现等完整流程帮助读者理解真实出行数据的分析思路与实现方法。压缩包共26个文件约13.49MB其中15个py脚本承载核心分析与绘图逻辑2个ipynb提供交互式实验过程2个csv为原始或处理后的轨迹数据另有json配置、png结果图与README说明文档结构清晰、便于按模块学习。目前已有83人学习下载。读者可从中获得一套可复现的轨迹分析方案、数据处理脚本与可视化代码并借鉴项目组织方式与排错思路快速迁移到自己的数据分析任务中。1. 出租车 GPS 轨迹分析一份能跑通的 Python 实战项目长什么样打开招聘 JD 搜「Python 数据分析」十有八九会看到「熟悉轨迹数据处理」「有时空数据经验优先」。但真到动手时很多人卡在第一步手里只有一份出租车 GPS 点表字段是taxi_id, lat, lon, time接下来该干嘛这份基于 Python 的出租车轨迹数据分析与可视化项目就是冲着这个断层来的。它用transbigdata这个专门做交通时空数据的库把上海和深圳两城的出租车 GPS 原始记录一路处理成能看的轨迹图、能算的 OD 矩阵、能讲的出行特征。评审 98 分不是重点重点是它把「数据清洗 → 轨迹重建 → 可视化 → 指标提取」这条链路完整跑通了适合课程设计、期末大作业也适合想补一段真实时空数据经验的从业者。2. 环境搭建与数据摸底先让 transbigdata 跑起来2.1 为什么选 transbigdata 而不是手写 pandas出租车 GPS 数据的麻烦在于单辆车一天可能产生几千个点全城几万辆车就是千万级记录。用 pandas 硬做分组、排序、计算相邻点距离代码能写但慢且容易在时间排序上翻车。transbigdata是专门针对交通 GPS 数据设计的库内置了轨迹清洗、地图匹配辅助、OD 提取、网格聚合这些高频操作底层用 numpy 向量化比手写循环快一个量级。常见做法是原始点表先用tbd.clean_taxi_data做一次清洗把漂移点、重复点、时间乱序处理掉再进入分析。选它的另一个理由是文档里直接给了出租车数据的标准处理流程省去自己试参数的时间。2.2 环境配置与依赖安装项目是.ipynb笔记本核心依赖不多但版本要对。我一般会先建一个干净环境避免和系统里的旧版 geopandas 打架。# 创建独立环境Python 3.9 以上 conda create -n taxi_traj python3.9 -y conda activate taxi_traj # 核心依赖transbigdata 会带出 pandas、geopandas、matplotlib pip install transbigdata pip install jupyter notebook # 如果 geopandas 安装报错先装 GDAL 再装它 # conda install -c conda-forge geopandas逻辑说明transbigdata依赖geopandas做空间操作而geopandas在 Windows 上直接 pip 装经常因为 GDAL 编译问题失败。稳妥做法是用 conda 走conda-forge渠道。参数上Python 版本建议 3.9 或 3.10太高版本部分地理库轮子还没跟上。装完后在 notebook 里import transbigdata as tbd不报错就算过了第一关。2.3 数据字段核对与预处理项目data目录下放的是上海和深圳的出租车 GPS 样本。打开code_shanghai_taxi_gps.ipynb之前先确认字段名。transbigdata 对列名有约定常见的是vehiclenum车辆 ID、time时间戳、lon、lat。如果字段名不对后面所有函数都会报 KeyError。import pandas as pd import transbigdata as tbd # 读取原始数据先看前几行和字段类型 df pd.read_csv(data/shanghai_taxi_sample.csv) print(df.dtypes) print(df.head()) # 统一列名transbigdata 默认认这套 df df.rename(columns{ taxi_id: vehiclenum, lng: lon, latitude: lat }) # 时间列转 datetime否则排序和差值计算会出错 df[time] pd.to_datetime(df[time]) df df.sort_values([vehiclenum, time]).reset_index(dropTrue)逻辑说明sort_values这步不能省。GPS 点表经常是按采集顺序存的不是按车辆和时间排的。transbigdata 计算相邻点距离时默认数据已按车辆和时间排序不排的话距离会算成跨车、跨时间的乱值。参数上reset_index(dropTrue)是为了后续用位置索引切片时不带旧索引。做完这步用df.groupby(vehiclenum).size().describe()看一眼每辆车的点数分布如果中位数只有几十说明数据被抽样过轨迹重建时要降低期望。3. 轨迹清洗与重建把漂移点和时间乱序处理干净3.1 清洗规则与参数含义原始 GPS 数据有三类典型脏点定位漂移相邻点距离突然几百公里、重复上报同一秒多个点、时间倒挂后一个点时间早于前一个。transbigdata 的clean_taxi_data把这些规则打包了但参数得按数据实际情况调。# 清洗速度阈值、距离阈值、时间窗口 df_clean tbd.clean_taxi_data( df, col[vehiclenum, time, lon, lat], dislimit1000, # 相邻点距离超过 1000 米视为漂移 speedlimit80, # 速度超过 80 km/h 视为异常 timegap60 # 相邻点时间差超过 60 秒不参与距离计算 )逻辑说明dislimit设 1000 米是因为城市出租车在采样间隔内正常位移不会超过这个数超过基本是漂移。speedlimit设 80 是城市道路经验值高架或高速可以放宽到 100但设太高会漏掉漂移点。timegap是关键参数如果两个点间隔半小时中间车可能熄火或数据缺失硬算距离会得到荒谬的速度。清洗后行数通常会掉 5% 到 15%掉太多说明阈值太严要回头调。3.2 轨迹重建与停留点识别清洗完的点还是散点要变成「轨迹」得按车辆分组、按时间排序再计算每段的距离和速度。transbigdata 提供tbd.traj_segment做分段但更常用的是直接算相邻点差值。# 计算相邻点距离米和时间差秒 df_clean[dist] tbd.getdistance( df_clean[lon].shift(1), df_clean[lat].shift(1), df_clean[lon], df_clean[lat] ) df_clean[time_diff] df_clean[time].diff().dt.total_seconds() # 只在同一辆车内有效跨车的第一行要置空 df_clean.loc[df_clean[vehiclenum] ! df_clean[vehiclenum].shift(1), [dist, time_diff]] None # 速度 km/h df_clean[speed] df_clean[dist] / df_clean[time_diff] * 3.6逻辑说明shift(1)取上一行配合车辆 ID 变化判断避免把 A 车最后一点和 B 车第一点连起来算距离。getdistance返回的是米乘 3.6 转 km/h。算完后用df_clean[speed].describe()看分布正常城市出租车平均速度在 20 到 40 km/h如果均值超过 60大概率是清洗没做干净。停留点识别可以用速度阈值连续多个点速度低于 5 km/h 且持续超过 3 分钟标记为一次停留对应上下客或等客。3.3 可视化把轨迹画到地图上项目里可视化部分用 matplotlib 加底图。transbigdata 有tbd.plot_map和tbd.plot_traj辅助函数但底图需要自己准备或在线拉取。import matplotlib.pyplot as plt # 设置绘图范围上海大致经纬度 bounds [121.0, 31.0, 121.8, 31.5] # [lon_min, lat_min, lon_max, lat_max] fig plt.figure(figsize(12, 10)) ax fig.add_subplot(111) # 画单辆车轨迹 one_car df_clean[df_clean[vehiclenum] df_clean[vehiclenum].iloc[0]] ax.plot(one_car[lon], one_car[lat], linewidth0.8, alpha0.7) ax.set_xlim(bounds[0], bounds[2]) ax.set_ylim(bounds[1], bounds[3]) ax.set_xlabel(Longitude) ax.set_ylabel(Latitude) plt.show()逻辑说明bounds按数据实际范围调上海用上面这组深圳换成[113.8, 22.4, 114.3, 22.7]。alpha0.7让重叠轨迹能看出密度。如果要画全城所有车直接循环 plot 会卡常见做法是先用tbd.gridagg把轨迹聚合成网格热力图再画网格速度快很多。项目里两个城市的 notebook 分别对应不同 bounds跑之前先确认数据文件路径和城市匹配。4. 避坑与排查轨迹分析里最容易翻车的五件事4.1 时间列没转 datetime 导致排序失效现象清洗后轨迹图乱成一团速度出现负值或几万 km/h。原因time列是字符串sort_values按字典序排10:00排在9:00前面。解决读数据后立刻pd.to_datetime并检查df[time].dtype是不是datetime64[ns]。4.2 经纬度写反导致点全跑到南极现象地图上什么都看不到或者点集中在(0,0)附近。原因原始数据里lat和lon列名标错或者读取时列顺序错位。解决上海纬度约 31、经度约 121深圳纬度约 22、经度约 114用df[lat].mean()和df[lon].mean()快速判断数值明显不对就交换列。4.3 transbigdata 版本不匹配导致函数参数报错现象clean_taxi_data报unexpected keyword argument。原因不同版本参数名有变化比如旧版用dislimit新版可能改名。解决pip show transbigdata看版本对照官方文档确认参数名或者用help(tbd.clean_taxi_data)查当前签名。4.4 内存不够导致 notebook 崩溃现象跑全量数据时 kernel 直接挂掉。原因千万级点表一次性读入加上中间列内存爆了。解决分块读pd.read_csv(..., chunksize500000)或者先按车辆抽样比如只取 500 辆车做分析验证流程后再上全量。项目里的样本数据量已经控制过但自己换数据时要留意。4.5 底图坐标系不匹配导致轨迹偏移现象轨迹画出来和地图底图对不上整体偏移几百米。原因GPS 原始坐标是 WGS84而某些在线底图用 GCJ02火星坐标直接叠加会偏。解决要么统一转成同一坐标系要么只用散点图不叠底图。transbigdata 有tbd.gcj02towgs84之类的转换函数按需调用。5. 从轨迹到指标OD 提取与出行特征计算5.1 OD 矩阵的构建逻辑ODOrigin-Destination是交通分析的核心产出把城市划成网格统计每个网格有多少次出行起点和终点。transbigdata 的tbd.gridagg先把点映射到网格再用tbd.traj_to_od或手动分组提取。# 划分 500 米网格 df_clean[grid_lon], df_clean[grid_lat] tbd.gridid( df_clean[lon], df_clean[lat], 500 ) # 每辆车每天取第一条和最后一条作为 OD df_clean[date] df_clean[time].dt.date od df_clean.groupby([vehiclenum, date]).agg( o_lon(grid_lon, first), o_lat(grid_lat, first), d_lon(grid_lon, last), d_lat(grid_lat, last) ).reset_index() # 统计每个 OD 对的出行量 od_count od.groupby([o_lon, o_lat, d_lon, d_lat]).size().reset_index(nametrips)逻辑说明gridid的第二个参数是网格边长米500 米是城市交通分析的常用粒度太细会稀疏太粗会丢失空间特征。first和last取每天首末点作为 OD 是简化做法更严谨的是结合停留点识别把停留超过阈值的位置作为真实起终点。od_count就是后续画 OD 弧线图或热力图的数据基础。5.2 出行特征指标计算有了清洗后的轨迹可以算几类常用指标日均出行次数、平均出行距离、出行时间分布。这些是课程设计里最好写的分析章节。# 每辆车每天出行次数按停留点分段 daily_trips od.groupby([vehiclenum, date]).size().reset_index(nametrip_count) print(daily_trips[trip_count].describe()) # 平均出行距离OD 直线距离 od[dist_km] tbd.getdistance( od[o_lon], od[o_lat], od[d_lon], od[d_lat] ) / 1000 print(od[dist_km].describe()) # 出行时间分布按小时统计 df_clean[hour] df_clean[time].dt.hour hour_dist df_clean.groupby(hour).size() hour_dist.plot(kindbar)逻辑说明trip_count的分布能看出数据里每辆车被记录了多少天如果大部分车只有 1 天数据日均出行次数就没意义。dist_km用直线距离代替实际路径距离会偏小但作为相对指标够用。hour_dist画出来通常能看到早晚高峰双峰如果只有一个峰检查时间列时区是否正确。5.3 可视化进阶热力图与 OD 弧线项目里可视化部分除了轨迹线还涉及网格热力图。用tbd.plot_map配合tbd.gridagg的聚合结果可以画出出行密度。# 网格聚合出行量 grid_agg tbd.gridagg( df_clean[lon], df_clean[lat], df_clean[vehiclenum], # 按车辆计数 bounds[121.0, 31.0, 121.8, 31.5], accuracy500 ) # 热力图 fig plt.figure(figsize(12, 10)) ax fig.add_subplot(111) tbd.plot_map(ax, bounds[121.0, 31.0, 121.8, 31.5]) tbd.plot_grid(ax, grid_agg, cmapReds, alpha0.7) plt.show()逻辑说明gridagg的accuracy要和前面gridid的网格边长一致否则对不上。plot_map负责画底图框架plot_grid叠加网格颜色。如果底图拉取失败可以跳过plot_map直接画网格矩形。OD 弧线图更复杂常见做法是用matplotlib的annotate画箭头或者用plotly做交互项目里两个 notebook 各有侧重上海偏轨迹深圳偏 OD 统计。6. 两个 notebook 的差异与二次开发切入点6.1 上海与深圳代码的对照项目里code_shanghai_taxi_gps.ipynb和code_shenzhen_taxi_gps.ipynb不是简单复制城市边界、数据量、分析侧重都有区别。上海数据点更密适合做轨迹重建和速度分析深圳数据里 OD 特征更明显适合做出行矩阵。跑之前先看每个 notebook 开头的bounds和文件路径别拿上海的范围去套深圳数据。对比项上海 notebook深圳 notebook经纬度范围121.0–121.8, 31.0–31.5113.8–114.3, 22.4–22.7分析侧重轨迹清洗、速度分布OD 提取、出行量统计网格粒度500 米500 米可视化重点单轨迹线、热力图OD 弧线、小时分布6.2 二次开发可以往哪走这份源码的价值在于流程完整但每个环节都能替换成更复杂的方案。比如清洗环节可以引入卡尔曼滤波做平滑OD 提取可以结合路网匹配把直线距离换成路径距离可视化可以换成folium或pyecharts做交互地图。我一般会先跑通原 notebook确认输出和预期一致再挑一个环节替换对比前后指标差异。这样既不会一上来就陷进调参也能清楚每个改动的实际影响。6.3 验证结果是否合理的几个检查点跑完两个 notebook 后别急着截图交差。先看几个数清洗后行数保留率是否在 85% 以上平均速度是否落在 20–40 km/h小时分布是否有早晚双峰OD 距离中位数是否在 3–8 公里。这几个数只要有一个明显偏离就回头查对应环节。血泪经验是有一次我忘了转 datetime速度均值算出来 300 多图看着还挺「密集」差点就交上去了。从那以后我每次跑完清洗都强制走一遍describe()和分布图确认没有玄学数值才继续。希望这份拆解帮到你源码包里的两个 notebook 和 data 目录直接跑就能复现上面所有步骤。本文还有配套的精品资源点击获取
返回列表