ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python出租车GPS轨迹数据分析与可视化全流程实战

2026/8/31 21:15:09 拓冰建站 浏览量
Python出租车GPS轨迹数据分析与可视化全流程实战 简介本资源是一套面向数据分析初学者与地理信息可视化实践者的Python实战项目聚焦城市交通轨迹挖掘与动态呈现解决出租车GPS数据清洗、栅格化统计、OD路径分析及时空轨迹地图可视化等典型问题。压缩包共26个文件包含15个核心Python脚本含数据预处理、栅格计数、OD矩阵构建、2个实测CSV数据集上海与深圳出租车GPS轨迹、2个Jupyter Notebook含完整分析流程与交互式图表、2个JSON配置与地理边界文件以及README说明、LICENSE等辅助文档整体大小为13.13MB。已有820人学习下载项目基于成熟的transbigdata开源工具包开发代码结构清晰、模块职责分明提供从原始GPS点到热力栅格、OD流图、24小时载客轨迹动画的端到端实现方案并附带可直接运行的可视化示例与关键参数注释便于快速复现与二次拓展。 去年我接手了一个城市级出租车GPS数据分析的活儿手上是几千万条的原始轨迹记录第一反应确实被数据量震住了。但把整个链路理顺之后我发现这种项目的难度并不在于某个算法有多高深而在于你愿不愿意把数据清洗、轨迹分割、特征计算和可视化这一整套流程全走扎实。这篇文章完整复盘一次基于Python的出租车轨迹数据分析与可视化过程讲清楚从一堆原始GPS点怎么变成一张张能讲故事的地图以及那些文档里查不到的坑。这个项目对三类人特别有价值一是刚入门时空数据分析的Python学习者想找一个真实场景练手二是做城市计算、交通方向的学生需要从出租车数据里挖运营特征与出行规律三是业务侧的数据分析师想用可视化给业务方做决策展示。无论你是哪一类这篇文章里给的步骤、代码和排查思路都可以直接拿过去改改用。1. 项目整体分析与方案选型思路1.1 出租车轨迹数据是什么能分析出什么出租车车载GPS终端会按照固定频率回传定位信息一条典型的出租车轨迹数据包含以下字段字段名示例值说明vehicle_id沪A8T302车辆唯一标识timestamp2023-06-01 08:23:15回传时间精确到秒longitude121.4737车辆经度WGS84latitude31.2304车辆纬度speed36.5瞬时速度km/hdirection125行驶方向角0-359°occupancy1载客状态0为空车1为载客把这些字段组合起来能做的事非常多。最常规的分析方向包括城市居民出行OD起讫点分析、热点区域挖掘、道路拥堵时空分布、巡游车空驶率评估、司机接单行为优化。每一类分析背后都有明确的业务诉求不是为分析而分析。我个人的经验是出租车数据相对网约车数据的优势在于轨迹覆盖密度高、样本连续性强而且载客状态字段像是一个天然的行程分段器把一整天的轨迹切成一单又一单真实出行。这个特征对后续做OD分析帮助极大。1.2 项目目标与最终产出形态做之前先把目标定清楚。这个项目我给自己定下的交付标准是三个一份干净可复用的轨迹处理流水线、一组站得住脚的城市出行分析结论、一个能交给业务方看的交互式可视化页面。目标如果定成把所有数据都画出来或者把所有算法都跑一遍项目基本会烂尾。因为数据量太大车载GPS在城市里的回传频率从几秒到几十秒不等一天的单城数据很容易破亿条。面对这种规模盲目追求全家桶式分析反而会把自己拖垮。我建议任何人在动手前先花半小时想清楚决策者关心什么指标学者关心什么关联司机群体关心什么收益变化把这个坐标系建立起来后面每一步怎么取舍都会清晰很多。1.3 技术栈选型为什么是Python这套项目用Python几乎是唯一合理的选择不是因为Python性能最强而是生态成熟度太高。轨迹数据处理离不开pandas和numpy空间计算有geopandas和shapely聚类有scikit-learn可视化从matplotlib到folium再到kepler.gl一整套链路都是现成的。如果你非要固执地用Java或者C去从零搭这套分析系统光是把GPS点画到地图上这一件事就会消耗大量精力。另外Python在交互式分析上的体验无可替代。Jupyter Notebook里可以一边调试数据一边看中间结果这个试错效率对探索性分析来说特别关键。我见过很多用其他语言做数据分析的同行基本都绕不开中间结果反复落盘的问题在项目节奏快的时候非常难受。2. 数据准备与预处理决定分析质量的隐藏环节2.1 从原始GPS点到可分析数据集拿到手的数据往往没有文档里写的那样规整。原始数据导入后第一件事不是做清洗而是先做字段探查。用df.info()看字段类型、用df.describe()看数值分布、用df.head()看字段样例把这三个动作做完才能知道数据里到底藏着什么问题。常见的问题包括经纬度字段被读成了字符串类型、时间字段混合了多种格式、载客状态字段出现缺失或者异常值、坐标超过城市边界范围。这些问题如果不在这一阶段解决后续任何分析都会失真。以经纬度为例很多车载终端会对坐标进行偏移处理俗称火星坐标直接和标准底图叠加会出现几百米的错位。这种情况有两种处理办法一是用地图API提供的坐标转换接口做纠偏二是如果项目对绝对位置要求不高只分析相对关系比如聚类热点、统计OD流可以先用原始坐标出结果等结论成型后再统一纠偏汇报。我的建议是优先做一次纠偏因为后续和路网、行政区划叠加分析时偏移会直接影响空间连接的正确性。2.2 数据清洗的四个关键动作轨迹数据清洗相比普通表格数据多了一层空间上下文的判断不能只看字段值本身还要看点和点之间的关系。我按执行顺序拆成四步第一步是去重。因为GPS终端可能存在重传机制同一个车辆在同一秒回传了两条相同记录的情况并不少见。直接按车辆ID和时间戳去重即可这个操作通常能干掉0.5%到2%的数据量。第二步是坐标合理性检查。在一座城市做分析时先划定一个城市的经纬度边界矩形快速把明显出界的点过滤掉。比如分析目标在上海那经度范围大约在120.8到122.1之间纬度范围在30.6到31.9之间落在范围外的点直接丢掉。更精确的做法是使用行政区划的shapefile做空间裁剪不过工程量和耗时都会上去初级阶段用矩形过滤就够了。第三步是瞬时速度合理性检查。一辆出租车在城市道路上跑到200km/h是不现实的。把speed字段大于180的点过滤掉另外还可以计算相邻GPS点之间的距离再推算出实际平均速度如果计算速度和设备上报速度差距过大大概率是漂移点。第四步是GPS漂移点的空间上下文剔除。这是轨迹数据特有的问题。一辆车不可能在1秒内从A点跑到距离500米外的B点再在下一秒跳回A点附近。这种跳变点需要通过前后两个点之间的速度和方向角变化来识别我通常用滑窗法计算每个点和前后点的距离如果单段距离超过阈值且方向发生剧烈变化就标记为漂移并剔除。这一步对后续的路线可视化影响巨大不处理的话轨迹线会画出很多乱飞的毛刺。2.3 轨迹分割把点串变成一段段行程清洗完之后原始数据还只是按时间排列的点不是行程或者轨迹。要做出行分析必须先把属于同一辆车的GPS点按时间序列排好然后根据载客状态的变化把整天的轨迹切成一段一段的行程。经典的分割逻辑是一辆车从空车变为载客的瞬间是一个行程的起点从载客变为空车的瞬间是行程的终点。但也有一种情况车辆长时间没有回传数据比如进隧道、终端关机中间隔了超过15分钟即使载客状态没变也应该强制切断否则会把两个不连续的故事硬接成一条线。分割的代码逻辑大体是这样按车辆ID分组组内按时间排序判断每条记录和前一条记录之间是否发生载客状态变化或者时间间隔是否超过阈值。找到分割点之后把每一段连续的记录归类为一个trip_id这个字段是后续所有聚合分析的依据。做这一步时有个重要的细节不要把只包含一两个GPS点的微片段当成有效行程。一个行程至少要有5到10个定位点才能比较可靠地计算轨迹长度、平均速度等特征。我在实际项目中会把少于5个点的行程全部剔除否则后续OD分析会凭空多出一堆噪声。2.4 特征工程速度、加速度与行程指标行程分割完成后下一步就是计算每条行程的特征。这里面最基础的是三个特征里程、时长、平均速度。里程计算必须用球面距离公式Haversine不能直接用欧氏距离否则在经度纬度尺度差异大的城市会出现明显偏差。Haversine公式的代码实现不复杂我之前封装过一个向量化版本用numpy的三角函数一次性算出整列经纬度之间的距离效率比逐行循环高几百倍。一个千万级数据集跑一遍也只要几秒。除了基础的里程和速度我还会计算一个容易被忽略但很有价值的特征瞬时加速度。加速度可以通过前后两点的速度差值除以时间差得到。急加速、急减速的频次能反映出道路拥堵程度和司机驾驶行为是分析道路交通状态的一个不错的代理指标。另一个值得计算的特征是行程起终点所在的时间窗。把出发时间映射到早高峰、午间平峰、晚高峰、夜间等时段后续按时间段做分组分析就非常方便了。这个特征在OD分析里几乎是必用的维度。3. 核心分析从轨迹点到出行洞察3.1 热点区域挖掘与载客状态分析数据清洗和特征工程做完项目真正有意思的部分才开始。第一个必做的分析是热点区域挖掘。思路很简单把载客状态为1的GPS点也就是乘客在车上的位置提取出来用聚类算法找出空间上高度聚集的区域这些区域就是城市里打车需求最旺盛的地方。我优先推荐DBSCAN而不是K-Means做空间聚类。原因有两个一是DBSCAN不需要预先指定聚类簇数城市里到底有几个热点事先并不知道二是DBSCAN能自动识别离群点不会像K-Means那样把毫无关联的边界点强行拉进某个簇。在做DBSCAN之前把经纬度转成合适的投影坐标会好很多。直接使用经纬度计算距离时经度和纬度单位长度在不同纬度上差别很大会让聚类结果失真。一个简便方法是把经纬度乘以111320换算成米在纬度为0附近近似更精确的做法是使用pyproj做投影转换。聚完类之后把每个簇的中心点标出来再结合簇内点的数量排序就能得到前十大热点区域。这个结果对出租车调度、商业选址、城市规划都有直接参考价值。3.2 OD分析理解城市出行的起终点结构OD分析是出租车轨迹数据里信息量最大的分析之一。O就是上车点OriginD就是下车点Destination。每一笔订单都是一次OD记录把成千上万次OD聚合起来就能看到城市出行在空间上的流向图。OD分析的第一步是给每个行程标记起点和终点落在哪个区域。最简单的做法是行政区域粒度比如市区、各行政区更精细的做法是使用网格编码比如把城市切成500米×500米的方格每个行程都打上start_grid_id和end_grid_id。做完区域标记后可以构建OD矩阵矩阵的行是出发区域列是到达区域单元格里的数值是订单数量。这个矩阵是研究城市通勤结构、商业区联动关系、跨区出行强度的核心输入。OD矩阵的可视化通常有两种表现方式如果区域数量少用弦图或者桑基图展示流向关系很直观如果区域数量多直接在地图上画OD连线然后根据流量大小调整连线的粗细和透明度。我建议两种方式都做前者适合汇报后者适合探索分析。3.3 运营效率指标空驶率与接单特征分析对出租车运营方或者司机群体来说最关心的不是热点在哪而是怎么提高收入。从这个角度切入分析重点是空驶率和接单距离。空驶率的定义很简单空车状态行驶时间占全部运营时间的比例。在数据集里载客状态为0的GPS点所在的时间就是空驶时间。空驶率可以按车辆聚合做司机之间的对比也可以按时间段聚合看一天内哪些时段空驶率高还可以按区域聚合看哪个区域空车扎堆。接单距离的计算稍微复杂一点。每段行程的起点上一条GPS点空车状态和行程第一个载客点之间往往就是司机接到这一单之前的巡游路线。这段巡游距离的分布直接反映了一个区域的打车可达性。我做过一个有意思的分析把城市按路段划分计算每条路上空车的平均通过速度然后把速度慢、空车多的区域叠加在一起就能找到出租车扎堆但跑不起来的拥堵带。这个结果对交通管理部门调整信号配时有很强的参考价值。4. 可视化方案选型与落地实践4.1 可视化工具怎么选folium、kepler.gl、pyecharts、matplotlib可视化是这个项目的门面工具选型我踩过不少坑。先说结论如果只是出论文插图或者报告配图matplotlib和seaborn足够如果要做交互式地图放在网页里给业务人员点着看优先选folium或者kepler.gl如果想要视觉冲击力强的汇报大屏pyecharts是快速选项。folium是基于Leaflet的Python库它能生成自定义HTML地图支持marker、polyline、热力图等图层最香的是输出是单文件HTML发微信给同事就能直接打开看不需要部署服务器。kepler.gl是Uber开源的超大规模数据可视化工具Python接口叫kepler.gl的库或者通过Jupyter使用。它专为百万级点数据的可视化设计GPU加速渲染交互流畅度远超前几种。唯一的缺点是需要部署到jupyter或web服务器上配置稍重。pyecharts的优点是好看、配置方便适合快速出图表。但它的地图底图加载依赖在线资源大数据量渲染时会卡。我做汇报场景用得多分析场景用得少。工具适合场景大数据量表现部署难度matplotlib论文插图、静态分析图差需要降采样无folium交互式HTML地图中等需聚合或采样无kepler.gl百万级点交互探索好GPU加速中等pyecharts汇报图表、大屏中等偏下低4.2 轨迹热力图与密度分析怎么做轨迹热力图能直观回答城市里哪些地方车辆最密集这个问题。实现上有两种思路一种是直接调用folium的HeatMap插件把需要展示的经纬度点传进去它用Canvas在前端做核密度渲染另一种是用Python端先做核密度估计KDE把密度值算好再映射成颜色图层。我推荐第二种思路。原因是直接给前端传几百万个点浏览器光解析数据就会卡顿。正确的做法是先降采样甚至网格化聚合把点的数量变成格子的密度值然后只渲染聚合后的结果。这样浏览器压力小加载快而且还能用颜色分级来表达密度大小。一个实操细节是热力图分析最好分时段做不要全量一锅煮。早高峰和夜间的热点是完全不同的全量渲染反而模糊了规律。我把一天拆成4个时段早高峰、白天平峰、晚高峰、夜间分别生成热力图叠加在地图上用时间滑块切换展示效果比一张总图好得多。4.3 OD连线图与交互式仪表盘OD连线图制作时最忌讳的是把全部OD线都画出来。几十万条连线同时画在地图上视觉上就是一坨乱麻。解决方法是分层聚合先按流量排序只画出流量排名前200的OD对然后用线的粗细代表流量大小用颜色代表方向或者时间段。另一个技巧是使用三次贝塞尔曲线而不是直线画OD连线。直线在地图上会横穿大片无关区域让人误以为乘客真的走了这条路线贝塞尔曲线可以向一个方向拱起视觉上更接近实际出行路径的感觉也更美观。folium的PolyLine虽然不支持直接画贝塞尔曲线但可以手动计算曲线路径点来圆滑处理。交互式仪表盘方面我用过最顺手的方案是用folium生成主地图再用Flask封装一层轻量服务前端页面里嵌入多个iframe分别展示热力图、OD图和个人习惯用的统计图表。这样不用学前端框架纯Python就能搭出一个能用的分析页面。4.4 大数据量下的渲染性能优化这个部分是可视化环节我觉得最值钱的实战经验。数据量一旦超过十万点很多看似正常的操作就会卡到想砸电脑。核心解决思路无非四个字聚合、采样。聚合是把空间粒度放大。比如展示全市轨迹密度时不需要在500米网格级别展示改成1公里网格级别数据量能直接降一个数量级展示效果差异其实很小。采样是按比例抽稀比如每N条记录取一条或者按时间间隔采样每10秒保留一个点。抽样时务必使用随机抽样或系统抽样不要有明显的时间偏好否则会破坏轨迹的时间连续性。对于folium来说我还会用分块加载的思路先把地图按照zoom级别划分若干瓦片在低zoom级别显示聚合结果高zoom级别才加载原始点。这么做虽然增加了开发量但用户体验提升非常明显。5. 实操过程与完整复现步骤5.1 环境准备与依赖安装复现这个项目的环境依赖不算复杂核心库就这些pandas、numpy、matplotlib、folium、scikit-learn。如果要做空间计算再加geopandas、shapely、pyproj如果要用kepler.gl需要安装kepler-gl或delphi等额外包。我推荐先建一个独立的conda环境避免版本冲突。conda create -n taxi-analysis python3.10 conda activate taxi-analysis pip install pandas numpy matplotlib folium scikit-learn pip install geopandas shapely pyproj安装geopandas时如果遇到二进制依赖问题在Windows上建议直接用conda安装conda install geopandas会比pip省心很多。这个坑我在第一次配环境时踩过编译报错耗费了大半天。5.2 数据加载与预处理代码示例下面给出一段核心的预处理代码包含字段解析、去重、坐标过滤和漂移点剔除。这是整条流水线里最关键的代码片段我反复打磨过性能上对千万级数据也能在几分钟内跑完。import pandas as pd import numpy as np # 读取原始CSV数据 df pd.read_csv(taxi_gps.csv, parse_dates[timestamp], dtype{vehicle_id: str}) # 1. 按车辆ID和时间戳去重 df df.drop_duplicates(subset[vehicle_id, timestamp]) # 2. 坐标范围过滤以上海为例 lon_bounds (120.8, 122.1) lat_bounds (30.6, 31.9) df df[(df[longitude].between(*lon_bounds)) (df[latitude].between(*lat_bounds))] # 3. 速度异常值过滤 df df[df[speed] 180] # 4. 按车辆分组组内按时间排序 df df.sort_values([vehicle_id, timestamp]).reset_index(dropTrue)这段代码里有几个细节想说明parse_dates参数会让pandas自动解析时间列但前提是原始时间字段格式统一如果不统一就需要先用pd.to_datetime(..., format...)加宽松匹配参数处理drop_duplicates默认保留第一条记录但GPS重传场景下可能应该保留最后一条两边时间相近影响不大我一般默认保留第一条。5.3 行程分割与特征计算代码示例行程分割是整条分析链路的承上启下环节。我在下面代码里实现了基于载客状态和时间间隔的双重分割逻辑。# 载客状态发生变化或时间间隔超过阈值标记为新的行程 time_gap df.groupby(vehicle_id)[timestamp].diff() status_change df.groupby(vehicle_id)[occupancy].diff().fillna(0) ! 0 time_break time_gap pd.Timedelta(minutes15) # 分段标志只要有任一条件成立就开启新行程 df[segment_id] (status_change | time_break).astype(int) df[trip_flag] df.groupby(vehicle_id)[segment_id].cumsum() # 合并车辆ID和分段标志生成全局行程ID df[trip_id] df[vehicle_id] _ df[trip_flag].astype(str)这段代码用向量化操作代替循环在千万级数据集上速度极快。但有一个需要注意的点diff()在分组边界处会产生NaNfillna(0)处理后不会误判为新行程这是一个容易写错的细节。行程特征计算我一般用一个聚合函数把每个trip_id的信息汇总起来包括起终点经纬度、起止时间、GPS点数、总里程等。def calc_haversine_distance(lon1, lat1, lon2, lat2): R 6371.0 # 地球半径(km) lon1, lat1, lon2, lat2 map(np.radians, [lon1, lat1, lon2, lat2]) dlon lon2 - lon1 dlat lat2 - lat1 a np.sin(dlat/2)**2 np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2 return R * 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a)) # 按行程分组聚合 trip_stats df.groupby(trip_id).agg( vehicle_id(vehicle_id, first), start_time(timestamp, min), end_time(timestamp, max), start_lon(longitude, first), start_lat(latitude, first), end_lon(longitude, last), end_lat(latitude, last), point_count(longitude, count), ) # 计算里程和时长 trip_stats[distance_km] calc_haversine_distance( trip_stats[start_lon], trip_stats[start_lat], trip_stats[end_lon], trip_stats[end_lat]) trip_stats[duration_min] (trip_stats[end_time] - trip_stats[start_time]).dt.total_seconds() / 60顺手提醒一个容易踩的坑起点和终点的经纬度用first和last聚合时必须确保组内已经按时间排序否则取到的第一条和最后一条可能不是真正的起终点。建议在groupby之前先做一次全局排序然后按组累积顺序取用排序后的位置。5.4 DBSCAN热点聚类与结果验证热点聚类的代码不复杂但参数调优值得讲一下。DBSCAN的关键参数有两个eps邻域半径单位是坐标距离和min_samples一个核心点邻域内至少包含的样本数。from sklearn.cluster import DBSCAN # 先把经纬度转成适合计算距离的平面坐标近似米制 coords hotspots[[longitude, latitude]].values coords_meter np.column_stack([ coords[:, 0] * 111320, coords[:, 1] * 110540 ]) # DBSCAN聚类 clustering DBSCAN(eps250, min_samples30).fit(coords_meter) hotspots[cluster_id] clustering.labels_eps250表示半径250米在这个场景下大致对应一个街区的大小min_samples30表示在250米范围内至少有30个载客点才算得上一个热点。这组参数不是拍脑袋定的我一般先用k-distance图观察数据点之间距离的拐点来粗调再结合业务认知微调。例如上海的市中心热点区域250米半径里数量远高于30个载客点但在郊区这个参数可能就要放宽到50。聚类结果验证很重要不然可能就是一堆没有意义的簇。我会把每个簇的GPS点画到地图上人工核对是不是真的对应商圈、火车站、医院等需求密集场所。如果某个簇落在荒郊野外基本就是参数设置不合理导致的假热点。5.5 folium交互地图与轨迹热力图制作最后一步是可视化这部分代码比较直观但参数细节决定最终效果。import folium from folium.plugins import HeatMap # 创建底图初始中心设为城市中心 m folium.Map(location[31.2304, 121.4737], zoom_start12) # 加载聚合后的密度网格数据 heat_data [[row[lat], row[lon], row[density]] for _, row in density_grid.iterrows()] # 添加热力图图层 HeatMap(heat_data, radius15, blur10, max_zoom1).add_to(m) # 保存为HTML文件 m.save(taxi_heatmap.html)这里有个经验density字段可以填每个格子里GPS点的数量也可以填归一化后的密度值。如果直接传点数量数值差异过大会导致极端点颜色过深、其他区域完全看不见。我建议先对密度值做一次对数变换或者百分位归一化视觉呈现会更均匀。轨迹线可视化我用的是folium的PolyLine每条行程一条线颜色按时段或者平均速度映射。这里同样要警惕数据量问题几十万条行程同时画页面直接崩溃。我的对策是先按行程流量排序只挑出流量最高的几百条线展示或者对轨迹点做抽稀。6. 常见问题与排查技巧实录6.1 数据质量类问题时间解析失败是遇到最多的报错。pandas的parse_dates对混合时间格式的容忍度不高一旦出现2023/06/01 08:23:15和2023-06-01 08:23:15混在一起的情况就会抛异常。我的处理策略是先读取为字符串再统一用pd.to_datetime配多个候选格式尝试解析实在解析不了的行用errorscoerce置为NaT再过滤。经纬度字段被读成字符串也很常见尤其是从某些平台导出的CSV会在数字两侧自动加引号。判断方法很简单打印dtypes如果显示object就是字符串用pd.to_numeric(..., errorscoerce)转换即可但转换前先确认有没有null或NA这样的非数字文本否则会引入一堆NaN。漂移点的判断阈值也要根据数据的采样频率调整。30秒采样一次和5秒采样一次的数据相邻点之间的合理最大距离差别巨大。建议先用df.groupby(vehicle_id)[timestamp].diff().describe()看看采样间隔的中位数再据此设定速度阈值。6.2 性能与内存问题千万级数据在pandas里处理内存占用是个大问题。一辆车一天的数据量不大但整个城市所有车辆合在一起DataFrame的内存很容易冲到几个GB以上。我的做法是分块处理按车辆ID分块读取或者按日期分块处理最后再合并分析结果而不是一次性加载全量数据。另一个节省内存的技巧是降级数据类型。经纬度如果保留到6位小数用float64存储完全没必要改成float32就能省一半内存。车辆ID如果是定长字符串可以改成category类型内存占用会大幅下降。groupby操作在千万级数据上是必然瓶颈。我测试过如果不做任何优化对千万条记录做一次双重groupby加聚合耗时可能超过3分钟。优化思路是把需要聚合的列提前筛选出来减少数据拷贝另外如果经常按车辆ID聚合可以提前对vehicle_id排序让groupby的分组过程更高效。6.3 可视化显示问题folium地图加载慢或者加载不出来八成是网络问题。folium默认使用OpenStreetMap的在线瓦片在部分网络环境下访问不稳定。解决办法一是换用离线瓦片或者国内的地图瓦片服务二是用folium.Map(tilesCartoDB positron)切换瓦片源如果条件允许把瓦片缓存到本地配合离线部署方案彻底摆脱网络依赖。OD连线图上出现跨太平洋的线大概率是数据里有经纬度为0或者极端的脏数据没有清干净。这种错误线会让整个可视化显得特别不专业解决方案是在画图前对OD数据的起终点再做一次边界过滤并检查是否有经纬度恰好等于0的默认值。热力图颜色过曝难以辨认的问题解决方案和前面说的一致对密度值做对数变换或分位数归一化。另外一个技巧是给HeatMap设置radius和blur参数这两个参数控制热力点的扩散范围和柔和度我一般的经验值是radius取15到20blur取10到15数字越大越模糊需要根据地图缩放级别反复试。结尾一点个人体会做完这个出租车轨迹数据分析项目我最大的感受是这类空间轨迹分析的难点不在某个算法有多难而在整个链条太长每一步都会引入噪声每一步的决策都会影响最终结论。数据清洗少了漂移点剔除地图上就满是毛刺行程分割少了时间间隔判断OD分析就凭空多出无数假行程可视化少了降采样页面就卡到打不开。这些环节没有哪个是技术深水区但任何一个环节做得不扎实都会让你在最终汇报时被质疑结论的可靠性。另外一个实际建议是项目过程中尽量多保存中间结果。每个阶段处理完的数据都落一份parquet或者csv带上日期前缀既方便回溯也方便在后续发现分析有问题时快速定位是哪一步出的错。我后期把OD分析推翻重来的时候正是因为保留了各阶段的中间结果才没有把前面所有工作都白费。如果你也想拿出租车轨迹数据练手不要贪多先选一个城市、一个月份、一天或者一周的数据把全流程走通再逐步扩大范围。数据量从小到大扩张的过程中你对性能优化、参数调优和可视化取舍的理解会完全不一样。批完每一条轨迹点再回头看那些最初让你头疼的脏数据其实都在告诉你这个城市出行的真实规律。本文还有配套的精品资源点击获取