☰
Python用transbigdata实现出租车GPS轨迹可视化OD图
2026/10/3 9:11:09 网站建设 项目流程

简介:这是一份基于Python和transbigdata的出租车轨迹数据可视化分析源码包,面向数据科学初学者、交通领域研究者及城市规划人员,用于在Jupyter Notebook环境中对出租车GPS轨迹数据进行读取、清洗、可视化与探索性分析,以观察车辆运行规律和区域热点。压缩包共26个文件,涵盖15个Python脚本、2个Jupyter Notebook、2个CSV与2个JSON数据文件,另含说明文档、许可协议、Git忽略规则等,整体大小约70.26MB。目前已有471人浏览学习,适合希望快速上手transbigdata库并掌握出租车轨迹分析流程的读者。通过这套源码,可了解从数据预处理、轨迹网格化、OD分析到地图绘制的完整链路;内置上海、深圳两套出租车GPS示例数据,借助交互式图表和动态地图,能直观呈现出租车运行模式与热点区域,为城市交通管理和智能调度提供参考,也为进一步扩展数据分析思路提供了可运行的样板。

1. 出租车轨迹数据可视化:transbigdata 帮你把 10 万行 GPS 变成一张 OD 图

拿 Python 处理出租车轨迹数据,最烦的不是算法,而是数据从原始 GPS 到能画图的中间环节——清洗、切分、起讫点提取、栅格化,样样都要写样板代码。transbigdata 这个库正好把这些步骤收敛成十几个可复用函数,配合 pandas 和 matplotlib,一套基于 Python 的出租车轨迹数据可视化分析源码,能把百万行点表压成一张 OD 图。这篇文章不讲虚的,从装上依赖到画出热力图和起讫点弧线,再到把流程封装成可复用模块,全程都是可复制可调整的代码,并标出最容易翻车的几个参数。适合刚拿到出租车数据集、想快速出第一版分析成果的从业者,也适合已经被数据清洗磨掉耐心的老手。

2. 先把环境与数据伺候好:安装、字段解读与异常清洗

任何轨迹分析项目,真正费时间的永远不是画图,而是环境装不完、数据清不干净。这一章先把两个最常见的坑填平:一个是怎么把这套依赖装到开箱即用,另一个是拿到原始出租车数据之后第一时间该检查什么。后面所有绘图和分析都建立在这一步之上,跳过这两步直接读 CSV 的后果就是,等代码跑到中间才被坐标偏移和脏数据卡住,来回返工一整天。

2.1 装好 transbigdata:依赖项和最容易翻车的 geopandas

pip install transbigdata

如果是在干净的 Python 环境里执行,这一行大多数情况下能直接成功,但我必须提醒你:transbigdata 不是孤立的一个包,它需要 pandas、numpy、geopandas、shapely、matplotlib、pyproj 这一串依赖协同工作。其中最容易出问题的就是 geopandas,它底层依赖 GEOS/GDAL 这些本地编译库,在 Windows 上经常因为缺编译器导致 wheel 构建失败。我一般建议先单独把 geopandas 装好,确认import geopandas不报错,再装 transbigdata,这样排查问题的时候不用同时面对两个包的错误。

import transbigdata as tbd import geopandas as gpd print(tbd.__version__) print(gpd.__version__)

这段验证代码的作用很简单:能在当前内核里同时 import 到 transbigdata 和 geopandas,就说明底层的空间计算依赖已经打通。如果卡在 import geopandas 这一步,先不要去碰 transbigdata,把 geopandas 的安装问题解决掉再说。在 VSCode 里写代码的话,记得先把解释器切到新建的虚拟环境,再执行 pip,避免出现包已经装了但 import 仍然失败的环境混淆问题。

还有一个细节值得单独说:transbigdata 的 API 在不同小版本之间有调整,函数签名和返回字段偶尔会变。如果你照着某篇博客的代码跑不通,第一步永远是help(tbd.函数名)看当前版本的实际签名,而不是怀疑自己写错。我自己的习惯是把项目依赖的版本范围写进 requirements.txt,固定一套环境,避免换机器后同样的代码出不同结果。

2.2 出租车 GPS 字段:先搞清楚这四列再动手

出租车轨迹数据虽然来源不同,但核心字段基本一致:车辆编号(VehicleNum)、采样时间(Time)、经度(Lng)、纬度(Lat)。有些供应商还会附带载客状态(Status,1 载客 / 0 空驶)、瞬时速度、航向角,这些字段在后续分析里很有用,但第一眼一定要先确认四列主字段的类型和覆盖率。很多 Python 入门者拿到 CSV 之后直接df.describe()就想画图,结果时间列被当成字符串,经纬度列里有脏数据,画出来的图完全没法看。

import pandas as pd df = pd.read_csv('taxi.csv', parse_dates=['Time']) print(df.dtypes) print(df['Time'].min(), df['Time'].max()) print(df['VehicleNum'].nunique()) print(df.isna().sum())

这段代码一次回答四个问题:时间列是否已经被解析成 datetime64、数据的时间覆盖范围、有多少辆车、有没有缺失值。如果 parse_dates 没有生效,说明原始时间格式不是 pandas 默认能认出的 ISO 格式,需要回退到先读字符串,再pd.to_datetime(..., format='%Y-%m-%d %H:%M:%S')显式指定格式。到了这一步,时间字段的格式混乱是最大隐患,后面切分行程全靠它,解析错了后面所有排序和分组都会跟着错。

另外一个非常常见的坑是列名不统一:有的数据源用大写 Lng/Lat,有的用 Longitude/Latitude,还有的列名里带空格或全角字符。transbigdata 的大部分函数都要求你传入列名,所以拿到数据后的第一个操作应该是统一列名,我一般直接用 rename 把经纬度列改成全小写命名,后续传参的时候不用反复确认大小写和空格:

df = df.rename(columns={'Lng': 'lng', 'Lat': 'lat'})

这里多说一句,统一列名不仅仅是方便自己写代码,也是为了让函数调用的报错信息更直观。你用 lng/lat 这种约定名,即使版本升级导致默认参数变化,报错也更容易一眼看出来是哪一步的问题。

2.3 用 clean_outliers 做第一道清洗:经纬度越界与速度异常

出租车 GPS 设备在隧道和高架桥下经常掉链子,产生两类典型的脏数据:一类是经纬度直接跳到城市边界之外,另一类是短时间内位移过大导致速度离谱。transbigdata 对这两类情况分别提供了辅助函数,最常用的就是 clean_outliers,它按一个矩形边界把研究区域外的点过滤掉。

# 城市矩形边界:经度最小、纬度最小、经度最大、纬度最大 bounds = [113.5, 22.2, 114.5, 22.8] df_clean = tbd.clean_outliers( df, col=['lng', 'lat'], method='rect', bounds=bounds )

clean_outliers 的 method 参数选 rect,表示按矩形过滤;bounds 里的四个数字是有顺序要求的,先经度最小值,再纬度最小值,然后经度最大值、纬度最大值,这个顺序写反的后果是过滤掉所有正常点,画出来的图区域里只剩零星几根毛刺。过滤完记得看一眼删除了多少行。如果删除比例超过 1%,先怀疑 bounds 本身是不是设错了,而不是真的数据有问题。这个方法不是黑匣子,想确认它到底按什么规则过滤,直接读一下函数源码最保险,十几行就能看完。

还有一类异常是时间顺序乱掉。车载设备重连后,可能出现时间戳回跳或同一辆车在同一条记录上重复上报。处理这类问题没有专门函数,我的习惯是按车辆和时间排序后做 diff,把时间差为负的行直接丢掉,因为它们在轨迹切分阶段会把时间间隙计算搞乱。速度异常可以暂时不清理,只要后面做停留点或 OD 提取时用的是经纬度序列而不是速度,影响就有限。

清洗动作做完后,先跑一次简单的轨迹点数统计,看看剩余数据量级是否正常,比如单辆车单日几千条记录,再进入下一章的轨迹切分。数据这一步伺候好了,后面所有步骤都不会因为脏数据返工。

3. 轨迹切分与 OD 提取:让 10 万行点变成可分析的行程

原始 GPS 表每天可能有上百万行,但一次出行的起点和终点才是分析对象。这一章解决三个核心问题:怎么把一行一个点的数据变成一趟一趟的行程,怎么把行程压缩成 OD 表,最后怎么落到网格上做空间统计。三个动作分别对应 traj_split、traj_od 和 GPS_to_grid,可以单独用,也可以串成一条流水线,最终拿到一张干净的 OD 表,后面画图就简单了。

3.1 用 traj_split 按车辆和时间间隙切出 TripID

轨迹数据的最小单位是一次行程,但 GPS 文件本身没有行程字段。切分行程最常见的时间间隙法:同一辆车相邻两条记录的时间差,如果超过某个阈值,就认为是两段独立行程。出租车在路口等灯一般不会超过 5 分钟,而司机熄火揽客可能长达几十分钟,所以阈值选 300 秒作为起点,是比较稳妥的做法。

切分之前必须确认数据已经排序,否则前后的间隙比较毫无意义。先按车辆编号和时间排序,再用 traj_split 生成行程编号,输出的主键是“车辆编号 + 行程编号”,两者一起才能唯一确定一次行程。

df_clean = df_clean.sort_values(['VehicleNum', 'Time']).reset_index(drop=True) df_clean['TripID'] = tbd.traj_split( df_clean['VehicleNum'].values, df_clean['Time'].values, splitgap=300 )

这里的 traj_split 接受两个 numpy 数组,返回一个和输入等长的数组。函数内部逻辑是:时间差大于 splitgap 就生成一个新的 TripID,否则沿用上一个。splitgap 的单位是秒,出租车场景建议从 300 秒起步;如果你的数据采样间隔就是 60 秒,那么 300 秒意味着中间最多能容忍 4 个采样缺失,超过就断开。采样间隔更稀的数据,比如 2 分钟一条,splitgap 就得放宽到 600 秒以上,否则一次正常行程会被拦腰截成几段。

TripID 这个字段建议尽量保留在 DataFrame 里,越早生成越好。后面统计每趟行程的持续时间、里程、OD 点,全都依赖这个字段。如果你在切分之前发现数据量特别大,可以先按天拆分,分块跑完再拼回去,但排序和切分逻辑保持一致,避免各块之间同一辆车的行程编号冲突。

3.2 用 traj_od 提取行程起讫点:载客状态与时间窗口的取舍

有了 TripID 之后,OD 提取就变得很机械:每个 TripID 对应的第一条记录是起点 O,最后一条记录是终点 D。transbigdata 的 traj_od 做的就是这件事,它还会顺便带上每段行程的开始时间和结束时间,省去自己 groupby 后再取 head/tail 的样板代码。

od = tbd.traj_od( df_clean, col=['VehicleNum', 'Time', 'lng', 'lat'], tripID='TripID' ) print(od.columns.tolist())

traj_od 返回的列名在不同版本里略有差别,所以不要假设列名,先打印出来看。常见情况下它会包含车辆编号、TripID、起止时间和起止经纬度。接下来把所有字段改成统一命名,后面画图才顺手。

od = od.rename(columns={ 'O_Lng': 'O_lng', 'O_Lat': 'O_lat', 'D_Lng': 'D_lng', 'D_Lat': 'D_lat' })

如果打印 od.columns 发现字段名不是 O_Lng 这种形式,就按实际名字改。把列名打印当成常驻习惯,能避免在不同版本间切换时被 KeyError 反复折腾。

还有一个业务层面的取舍必须讲清楚:traj_od 按 TripID 切分,不区分载客还是空驶。如果你的目标是分析乘客出行需求,而数据里有明确的载客状态字段,那正确的姿势是先只保留 Status == 1 的记录,再做切分和 OD 提取。如果不管载客状态,最后 OD 表里会有大量空驶巡游的短途段,热力图看着热热闹闹,实际表达的是车辆运行强度而不是乘客需求。反过来说,如果研究的是道路运行状态和拥堵,保持全量轨迹反而更有价值。所以在跑 od 之前,先想清楚这次分析的服务对象是谁。

3.3 栅格化与网格聚合:把经纬度变成可统计的格子

OD 表还是连续坐标,做热力聚合需要先栅格化。transbigdata 的栅格化把研究区域划分成边长相等的方格,每个经纬度落到一个格子,用一对整数索引表示,这两列索引就能直接拿来 groupby。

area = [113.5, 22.2, 114.5, 22.8] cell_size = 500 grid = tbd.grid_params(area, accuracy=cell_size) od['O_LON_INDEX'], od['O_LAT_INDEX'] = tbd.GPS_to_grid( od['O_lng'], od['O_lat'], grid ) od['D_LON_INDEX'], od['D_LAT_INDEX'] = tbd.GPS_to_grid( od['D_lng'], od['D_lat'], grid )

grid_params 根据 area 矩形范围和 accuracy 生成网格参数,里面保存的是网格原点和坐标缩放系数。GPS_to_grid 的作用是把经纬度换算成该网格的行列号。注意这里我没有直接对原始点表栅格化,而是对 OD 表做栅格化,这样得到的行列号直接挂在 OD 表上,后续做起终点热点分析或者通勤流量统计都可以直接 groupby,不用再回到原始点表去查,内存开销也小很多。

聚合的写法很简单,按行列号分组计数:

dest_hot = od.groupby(['D_LON_INDEX', 'D_LAT_INDEX']).size().reset_index(name='count') dest_hot = dest_hot.sort_values('count', ascending=False)

一次性 groupby 就得到每个落客格子的订单量,排序后取前 N 个格子,结合城市地图就能直接定位出最热的落客区。这也是出租车轨迹可视化分析里最常被问的产出——热点在哪、流量往哪儿走。栅格大小在这个环节直接决定了热点是“区级”还是“街道级”:500 米适合看城市整体,200 米适合看商圈细节,网格切得越小空格子越多,聚合结果越碎。所以我的习惯是先跑一个 500 米版本看大局,确认分析区域没问题,再针对感兴趣区域加密到 200 米,这样效率高也省得反复调参数。

4. 可视化输出:从热力栅格到 OD 弧线的完整画法

前面拿到的 OD 表和栅格索引,本质上还是数据,这一章把它们画成能直接放进报告里的图。这里用的全部是 matplotlib 和可选的 keplergl,不需要额外引入重量级 GIS 前端工具。做完这一步,Python 数据分析与可视化的完整链路就通了:清洗 → 切分 → 提取 → 聚合 → 出图。

4.1 轨迹热力图:用 matplotlib 把栅格计数画出来

热力图是最直观的第一步可视化。先把原始轨迹点栅格化并统计每个网格的计数,再把网格索引还原成中心经纬度,用散点图的大小和颜色同时映射数量,一张覆盖全城的轨迹热度图就出来了。

import matplotlib.pyplot as plt df_clean['LON_INDEX'], df_clean['LAT_INDEX'] = tbd.GPS_to_grid( df_clean['lng'], df_clean['lat'], grid ) grouped = df_clean.groupby(['LON_INDEX', 'LAT_INDEX']).size().reset_index(name='count') grouped['lng'], grouped['lat'] = tbd.grid_to_center( grouped['LON_INDEX'], grouped['LAT_INDEX'], grid ) fig, ax = plt.subplots(figsize=(10, 8)) sc = ax.scatter( grouped['lng'], grouped['lat'], c=grouped['count'], s=grouped['count'] / grouped['count'].max() * 30, cmap='OrRd', alpha=0.7 ) plt.colorbar(sc, label='轨迹点数量') ax.set_xlabel('经度') ax.set_ylabel('纬度') plt.show()

这里分两步:先 groupby 网格索引得到每个格子里的轨迹点数,再用 grid_to_center 把行列号还原成该网格中心的经纬度,得到“中心点坐标-数量”的映射表。散点图中 size 和 color 同时代表 count 值,数量越大的格子圈越大颜色越深,视觉上就是一张连续的热力分布。如果想把热力过渡得更平滑,可以用 scipy 的 gaussian_kde 对经纬度点做二维密度估计,再画 contourf,效果更像真正意义上的热力图,但计算量会随点数增长明显上升,建议先在抽样数据上试效果再全量跑。

这个图是过程产物,不是终稿。看到热力分布整体形状符合城市结构,坐标没有偏移,说明前面的清洗、切分、栅格化链路是通的,可以放心进入 OD 图的环节。如果热力图上热点在河中间或者大片空白,说明不是坐标系问题就是 bounds 设错了,回头检查比继续往下画 OD 更省时间。

4.2 OD 弧线图:从 OD 表到空间连线

OD 表里每一行是一段行程。画 OD 弧线图的常见做法是把每段行程的起点和终点连成一条线,线的密度代表出行强度。matplotlib 里用 LineCollection 来批量画线,性能比一条一条 plot 高很多。

from matplotlib.collections import LineCollection od_sample = od.head(2000) lines = [] for _, row in od_sample.iterrows(): lines.append([ (row['O_lng'], row['O_lat']), (row['D_lng'], row['D_lat']) ]) fig, ax = plt.subplots(figsize=(12, 9)) lc = LineCollection(lines, linewidths=0.3, colors='blue', alpha=0.3) ax.add_collection(lc) ax.set_xlim([113.5, 114.5]) ax.set_ylim([22.2, 22.8]) ax.set_xlabel('经度') ax.set_ylabel('纬度') plt.show()

真实项目里一次全量 OD 表几千上万行,直接全画线会糊成一片。我一般先按起终点网格做聚合,统计每条 OD 对的流量,再按流量从大到小排序,只画前 20% 的高频 OD,图面干净,还能一眼看出主要出行走廊。LineCollection 的 linewidths 和 alpha 是控制观感的关键参数:0.3 的线宽加 0.3 透明度适合看整体流向,如果换成 1.0 线宽、0.8 透明度,就变成强调少数几条主线的高对比风格。具体调多少取决于底图风格和输出用途,没有标准答案,这个调整某种程度上是玄学,多试几次就找到顺眼的组合了。

OD 图的价值不只是好看,它直接回答“从哪来、到哪去”的问题。比如早高峰时段的高频 OD 集中在居住区到 CBD,晚高峰正好反向,这个现象用 OD 弧线图一眼就能看出来,比堆一堆统计数字有说服力得多。

4.3 交互式地图:用 keplergl 验证热力效果和空间位置

静态图适合出报告,但调试阶段我更愿意用 keplergl 加载结果直接看,因为鼠标能放大到街道级别,确认坐标偏移和栅格变形这些静态图上看不清的问题。keplergl 是独立安装的包,和 transbigdata 没有绑定关系,但组合起来非常好用。

import keplergl import pandas as pd # 抽样一部分轨迹点,避免浏览器卡死 sample = df_clean.sample(50000, random_state=42) map_plot = keplergl.KeplerGl(height=600) map_plot.add_data(sample, name='taxi_traj') map_plot.save_to_html(file_name='taxi_kepler.html')

add_data 传入 DataFrame 后,keplergl 会自动识别经纬度列并渲染成点图层,图层面板里还能切换成热力图模式,不需要写额外代码就能验证结果。注意要先抽样再传入,几十万点直接塞进浏览器会明显卡顿,抽样 5 万点左右看整体分布足够用了。保存成 html 之后可以发给同事在浏览器里打开,对方不需要装 Python 环境。

交互地图不是必须的环节,但它的定位很实用:静态图是给读者看的,交互地图是给自己排查问题用的。我几乎在每个项目里都用它做一遍坐标正确性验证,确认没有落海点、没有跨城跳点,再回头出静态图,能省掉大量反复渲染的时间。

5. 出租车轨迹分析中常见的五个翻车点:现象、原因与排查修复

这一章把最容易踩的坑单独拉出来写,每一条都有真实的现象描述、原因分析和修复手段。这些坑不是偶发问题,而是换一个数据源就大概率再犯一次的典型情况。遇到问题的时候直接对照现象查原因,比从头调试快得多。

5.1 现象:热力图数据画出来了,但整体位置和地图底图对不上

做热力图叠加到城市底图上,发现热点整体偏移了几百米甚至一公里,但热力分布的形状又是对的城市轮廓。这时候第一反应不应该是调图,而是去查坐标系。国内不少出租车历史数据使用的是 gcj02 坐标系,甚至部分早期数据是加密坐标,直接用 WGS84 底图叠加自然偏移。

原因:原始数据的坐标系不是 WGS84 经纬度。GPS 设备原始输出通常是 WGS84,但数据加工平台可能会做过坐标偏移。

解决:先查数据说明文档里是否声明了坐标系。如果确认是 gcj02,需要用公开的坐标转换算法把经纬度转成 WGS84 再做后续分析。注意 transbigdata 没有内置纠偏功能,不要指望它自动处理。转换完成后再跑一次热力图,热点位置对上了,后面的 OD 图才有意义。

5.2 现象:切分出的行程数明显异常,比订单数多几倍或少一个量级

用 traj_split 切分后统计 TripID 数量,发现结果和业务对不上。出租车正常情况下单日订单数大致是可预期的,如果行程数多出好几倍,说明轨迹被切碎了;如果少得太多,说明短途行程被合并了。

原因:splitgap 参数与数据采样频率不匹配。数据源如果是 10 秒一条,300 秒的间隙设置太松,正常等红灯都会切成多段,行程数暴涨;如果采样是 60 秒一条,300 秒间隙又可能把短途订单合并成一段。

解决:先统计相邻点之间的时间差分布,用中位数的 2 到 3 倍作为 splitgap 初始值,再和业务口径的订单数交叉校验。出租车场景通常从 300 秒起步,但必须结合采样间隔调整。同时确认排序逻辑正确,否则切出来的碎片没有任何可解释性。

5.3 现象:处理一整天全市数据时内存溢出或运行极慢

数据量一大,pandas 的内存占用肉眼可见地涨,跑一条 groupby 要几十秒,甚至直接 OOM 崩溃。

原因:读取时引入了大量无关列,比如方向角、原始状态码、信号强度等,这些列在分析阶段用不上却占内存。另一个原因是逐行遍历循环,比如自己在 for 循环里做轨迹切分,而不是用向量化函数。

解决:读取时用 usecols 只选必要字段。我一般在 read_csv 阶段就排除不需要的列,字段从十几个减到四五个,内存直接降一半。处理全量数据再按天或按车辆分批,跑完后 concat 汇总,不要在单机内存里硬扛全量全年数据。

df = pd.read_csv('taxi.csv', usecols=['VehicleNum', 'Time', 'lng', 'lat', 'Status'])

这一行看起来简单,但对大数据量的项目,它对运行时间和内存的影响比后面任何优化都明显。

5.4 现象:时间相关的分组统计曲线乱跳,跨零点数据总被当成异常

按小时或天做聚合时,曲线出现异常断层,比如凌晨 0 点附近订单量突然归零,或者某天的数据整体消失。

原因:原始时间戳有的是带时区的 ISO 字符串,有的是毫秒级时间戳,有的是 Excel 导出的文本日期。pandas 解析时没有统一成 datetime64 的话,排序就会出错,跨零点的行程被错误地分到了两天。

解决:在 read_csv 阶段显式传 parse_dates 和 format,统一解析格式。对于跨天数据切分,不要先按天过滤再跑切分,而是先按车辆和时间全局排序、切完 TripID,再按开始时间归入对应日期统计。这样跨零点的行程会保留完整,统计也不会丢一半。

5.5 现象:栅格热力图边缘出现大片空白,热点被明显截断

热力图中心区域效果不错,但研究区域边缘出现大量空网格,或者热点刚到一个区域边界就戛然而止,看起来不自然。

原因:研究区域范围取得太随意。有人直接用数据经纬度的 min/max 作为边界,导致边缘网格只覆盖到几个孤点,栅格化后插值出现大片空白。也有人把区域范围取得过大,网格数量暴增但点没变多,图面被空白格子稀释。

解决:研究区域建议取城市行政区边界或外扩 3 到 5 公里的矩形。先看数据经纬度的分布中心,再以中心向四周扩边,让栅格铺满有效区域。同时让 cell_size 和覆盖范围匹配,城市级分析 500 米足够,不需要 100 米。区域范围确定后最好固定下来,后续多次分析用同一套参数,结果才可对比。

6. 把分析流程封装成可复用的可视化分析模块:源码设计的最后一环

流程跑通之后,下一步是把脚本变成可复用的模块,让换一份数据时不用重写代码。一个好的封装原则是:参数集中在构造函数里,每个处理步骤拆成独立方法,中间结果可以随时取出来检查。

class TaxiTrajectoryAnalyzer: def __init__(self, area, cell_size=500, splitgap=300): self.area = area self.cell_size = cell_size self.splitgap = splitgap self.grid = tbd.grid_params(area, accuracy=cell_size) self.df = None self.od = None def load(self, path): self.df = pd.read_csv( path, parse_dates=['Time'], usecols=['VehicleNum', 'Time', 'lng', 'lat', 'Status'] ) return self def clean(self, bounds=None): bounds = bounds or self.area self.df = tbd.clean_outliers( self.df, col=['lng', 'lat'], method='rect', bounds=bounds ) return self def split_trips(self): self.df = self.df.sort_values(['VehicleNum', 'Time']).reset_index(drop=True) self.df['TripID'] = tbd.traj_split( self.df['VehicleNum'].values, self.df['Time'].values, splitgap=self.splitgap ) return self def extract_od(self): self.od = tbd.traj_od( self.df, col=['VehicleNum', 'Time', 'lng', 'lat'], tripID='TripID' ) return self

设计思路是这样的:load、clean、split_trips、extract_od 各管一段,每个方法返回 self 方便链式调用。核心参数 area、cell_size、splitgap 集中在init里,后续调整只改一个地方,不用去翻代码里的魔法数字。od 结果保存在 self.od 上,画图方法可以随时取用。

封装完成后一定要留一个最小验证流程,比如造一个 10 辆车、每辆 100 条记录的采样数据,跑一遍完整链路,确认输出 OD 数量和业务预期一致。这个验证脚本就是以后的后悔药,换数据源、升级库版本后,先跑一遍它,通过再交付,能避免大量“昨天还能出图今天全报错”的尴尬。我自己的血泪经验是,任何分析项目都要留一个这样的最小回归,否则每次环境变动都是在给未来挖坑。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询