
简介数据分析是交通工程与城市计算中的核心技能而GPS轨迹数据正是理解城市交通运行状态的黄金素材。从原始出租车定位记录中提取有效信息通常需要经过数据清洗、坐标转换、路网匹配等预处理环节再通过平均速度、车流密度与拥堵指数等指标量化道路通行状况最终借助可视化工具呈现时空规律。这类完整的数据分析流程不仅适用于课程设计与期末大作业也是智能交通系统、出行服务与城市规划中常见的工程实践。本文以出租车GPS轨迹数据为例梳理了从数据预处理到指标计算、可视化展示再到实验报告撰写的全链路方法并提供了开源工具链下的可复现方案为处理类似时空轨迹数据或完成交通数据分析项目提供了一份可直接参考的技术模板。 前几天帮学弟整理文件翻到一个之前期末大作业交通数据分析与应用课程的作业包里面是完整程序源码加实验报告压缩成zip格式躺在硬盘角落里。这个学期的课程已经结题正好趁着热乎劲把整个项目从数据清洗到报告撰写的完整链路拆给你看。如果你正在做类似的交通大数据课程设计、期末大作业或者刚接触数据分析想找一个能落地练手的项目这篇文章可以直接当作参考模板来用。先说清楚这份作业干了什么用公开的出租车GPS轨迹数据结合路网信息完成区域交通状态分析、拥堵时段识别和可视化展示最后产出一份结构完整的实验报告。整个项目从拿到原始数据到最终交付大概花了两周业余时间中间踩了不少坑尤其是数据分析前的预处理阶段光清洗数据就占了三分之一的时间。下面按照我做这个项目的顺序把每个环节的思路、代码和复盘一起写出来。1. 期末大作业的任务拆解与选型思路1.1 这个作业到底要解决什么问题很多同学拿到“交通数据分析与应用”这种课程作业的第一反应就是慌因为题目太开放了老师只给了一个方向没有明确指定数据集和分析指标。我的理解是这类作业考察的核心不是你会多少模型而是你能不能完成一个“数据获取—数据清洗—特征分析—可视化—结论输出”的完整闭环。所以第一步不是急着写代码而是把作业要求拆成几个可量化的小目标。我当时给自己定的任务是基于城市某区域一周的出租车轨迹数据统计不同时段的路网平均速度、车流密度和拥堵指数识别早晚高峰的具体时间区间并用图表直观呈现。这个目标听起来不大但足以覆盖交通数据分析的核心流程数据预处理、地图匹配、指标计算、可视化、结论总结。拆完之后你心里就有底了每一步都有明确产出不会做着做着迷失方向。1.2 数据从哪来公开数据集与自采数据的取舍搞交通数据分析数据是最大的门槛。选项有三个一是用老师提供的数据二是自己爬取公交/地铁API数据三是找公开数据集。我的建议是优先用老师给的或者公开的经典数据集不要一上来就自己爬因为课程作业的时间成本很紧张爬虫本身就是一个大坑。我当时用的是某公开的出租车GPS轨迹数据集字段包括车辆ID、时间戳、经度、纬度、载客状态、速度等。这个数据集的好处是字段规范、时间连续坏处是数据量很大一天的数据解压出来就有几个GB跑起来很吃内存。如果你的机器配置一般建议先做采样比如只取其中一天的某个区域数据或者每隔10辆车抽一辆这样既能跑通流程又不至于被数据处理卡死。还有一个重要决策数据格式。我拿到的是csv文件但因为原始文件很大直接用Excel是打不开的必须用pandas处理。如果你拿到的是shp路网数据或者osm.pbf格式还得先转坐标、提取路网建议选数据源之前先确认格式避免后期做大量格式转换工作。1.3 技术栈选型Python为主Excel为辅这个项目我全部用Python完成的核心工具是pandas、numpy、matplotlib、folium。数据处理量在千万级以下pandas完全能扛住没必要上Spark。地图可视化用了folium可以直接生成交互式HTML地图展示轨迹和热力分布效果很好实验报告插图档次一下就上去了。为什么不用Excel或者TableauExcel处理几万行数据还行到了几十万行以上的轨迹数据就明显卡顿而且还不能做复杂的地理计算。Tableau虽然可视化强但学校不一定会安装而且老师对Tableau生成的分析结果认可度没有代码高。用Python写代码的好处是分析过程可复现老师能通过代码看到你的思路答辩时也方便讲清楚每个步骤。环境配置上我建议用Anaconda创建独立环境避免系统Python环境被搞乱。这里补充一个实战经验不要把项目依赖全装在base环境里我的实验报告里专门加了一条环境说明告诉别人用conda创建python3.9环境运行pip install -r requirements.txt就能复现这样不管谁拿到你的zip压缩包都能快速跑通。2. 数据预处理整个项目的地基2.1 拿到原始数据后的第一步不是建模而是清洗这是所有数据分析项目里最容易翻车也最容易被忽视的一步。我拿到的原始csv字段里明显有脏数据时间戳格式不统一有的精确到秒有的带毫秒经纬度字段有小数位数不一致的情况更严重的是出现了“经纬度倒置”问题——有几条记录的纬度值大于经度值明显是录入时把经纬度写反了。清洗策略分三层第一步是格式统一时间字段全部用pd.to_datetime()转成标准格式经纬度如果是字符串类型要先转float第二步是范围过滤根据城市经纬度范围剔除超出市界的记录比如我研究的城市中心区域经度范围约在116.0到117.0之间纬度范围约在39.6到41.0之间超出这个范围的直接丢弃第三步是重复值处理同一辆车同一时刻如果出现重复轨迹点保留第一条记录。举个例子原始数据clean的过程大概这样import pandas as pd df pd.read_csv(raw_taxi.csv) # 时间字段统一 df[timestamp] pd.to_datetime(df[timestamp], errorscoerce) # 过滤无效时间 df df.dropna(subset[timestamp]) # 经纬度合法性过滤 df df[(df[lng] 116.0) (df[lng] 117.0)] df df[(df[lat] 39.6) (df[lat] 41.0)] # 去重 df df.drop_duplicates(subset[vehicle_id, timestamp])这个清洗过程写进实验报告后老师能明确看到你的数据是由脏到净的转变过程这本身就是加分项。很多同学直接把原始数据丢进模型跑写报告时根本没法解释结果为什么异常问题往往就出在漏了数据清洗这一步。2.2 坐标转换与路网匹配的实操细节交通轨迹数据常遇到坐标系问题原始GPS数据多是WGS84坐标系而路网数据可能是GCJ02或者国测局坐标两者直接叠加会导致轨迹偏移几十米甚至上百米可视化时轨迹会跑到路网之外直接导致分析结果失真。我当时就踩了这个坑用folium画轨迹时发现轨迹点明显偏移到建筑物上后来一查是坐标系不一致。解决办法是写一个坐标转换函数网上有成熟的WGS84转GCJ02代码直接集成到预处理流程里。这里要注意不要自己推导公式直接引用公开的转换算法就好。地图匹配是更进阶的处理把GPS轨迹点匹配到实际路网上这一步在课程作业里不一定要求但如果你能用简单的方式实现会加分。最简单的做法是计算轨迹点到最近路段端点的距离把点投影到最近路段上。我当时用了一个比较取巧的方式预处理时直接把轨迹点与路网shape文件做空间连接用geopandas的sjoin操作按最近的roa_id把点关联到路段上这样就得到了每个轨迹点对应的路网编号后面统计不同路段的交通状态就很方便了。import geopandas as gpd from shapely.geometry import Point # 轨迹点转GeoDataFrame gdf_points gpd.GeoDataFrame(df, geometry[Point(x, y) for x, y in zip(df[lng], df[lat])]) gdf_points.crs EPSG:4326 # 路网数据 gdf_roads gpd.read_file(road_network.shp) gdf_roads gdf_roads.to_crs(EPSG:4326) # 空间连接 merged gpd.sjoin(gdf_points, gdf_roads, howleft, predicatewithin)这段代码跑起来比较费内存建议按天分片处理把匹配结果保存成parquet文件后面分析的时候直接读取匹配好的数据不用每次都重新做空间计算。我当时就是没经验一次性跑全量数据直接内存溢出后来改成按小时循环处理才跑通。2.3 缺失值和离群点的处理原则缺失值处理要分情况时间戳缺失只能删速度字段如果是NaN可以用相邻轨迹点之间的距离除以时间差计算一个推算速度来填载客状态缺失可以使用前向填充因为载客状态一般持续一段时间才变化。离群点的处理要更谨慎。我遇到过速度字段明显不合理的情况比如出租车在城市道路的速度显示180km/h这明显是GPS漂移导致的异常。处理策略不是直接删除而是先算出速度的95%分位数超过某个阈值的先标记为“疑似异常”再判断前后轨迹点是否有大幅度跳跃。如果前后两点距离超过5公里且时间间隔小于1分钟那基本可以判定是GPS漂移点直接剔除。如果只是速度超限但前后轨迹正常可能是在高速路上保留并单独标注。这个处理原则我会写进报告的方法部分因为老师会关心你是否意识到了异常数据的存在以及你如何处理这些异常。盲目删数据会丢失真实信息完全不处理又会污染结果所以要在报告里说清楚你的过滤逻辑和判断依据。3. 指标计算与可视化让数据“开口说话”3.1 流量、速度、延误三类核心指标的公式与Python实现预处理完成后真正体现交通数据分析“应用”价值的环节就是指标计算。我当时选了三个核心指标路段平均速度、车流密度、拥堵指数。路段平均速度的计算方式是对于每个路段把一天内的轨迹点按车辆ID分组计算每辆车在该路段的行驶距离除以行驶时间再对多辆车取加权平均。这个指标能直观反映道路通行状态。车流密度这里用的是“单位路段长度上的车辆数”先按15分钟时间窗口统计每个路段出现的不同车辆数再除以路段长度得到时空分布的热度指标。拥堵指数是衡量交通状态的关键参数。我采用的简化定义是拥堵指数 自由流速度 / 实际平均速度自由流速度我取该路段一天内95%分位数的速度作为近似值。指数大于1表示实际速度低于自由流数值越大越拥堵。按这个公式将拥堵指数划分为四个等级畅通1.2、基本畅通1.2-1.5、拥堵1.5-2.0、严重拥堵2.0。这个逻辑简单清晰写报告时非常好解释答辩老师也比较认可这种有依据的指标定义。下面是一段按15分钟窗口计算平均速度和拥堵指数的核心代码# 按路段和时间窗口分组 df[time_window] df[timestamp].dt.floor(15min) speed_stats df.groupby([road_id, time_window]).apply( lambda x: (x[distance].sum() / x[duration].sum()) ).rename(avg_speed).reset_index() # 计算自由流速度取95%分位数 free_flow_speed df.groupby(road_id)[speed].quantile(0.95).to_dict() speed_stats[free_flow_speed] speed_stats[road_id].map(free_flow_speed) speed_stats[congestion_index] speed_stats[free_flow_speed] / speed_stats[avg_speed]这段逻辑用时约半小时就写完了但调试时间花了一天多因为发现部分路段的平均速度出现极端值。后来定位到原因某些路段被多辆车长时间停留比如出租车在路边等客导致里程除以时间的结果非常小。针对这个问题我加了一步过滤剔除停留时间超过5分钟的轨迹点效果立竿见影。3.2 时段特征分析与拥堵识别有了基础指标后就可以做时段特征分析了。我按小时维度统计了全区域的平均速度和拥堵指数用折线图观察一天的变化趋势。结果非常典型早高峰出现在7点到9点时段内平均速度降到全天最低值晚高峰出现在17点到19点拥堵指数明显上升中午12点到14点有一个小幅回升但不如早晚高峰明显。这些结论和常识一致说明指标计算没有大问题。再往深一层我按路段做聚类分析把不同路段的拥堵模式分为“早晚双峰型”“单晚峰型”“全天平稳型”然后在地图上用不同颜色标注。这个分析在报告里属于亮点部分因为不是简单堆砌指标而是找到了交通运行状态的时空规律。聚类我用的是KMeans输入特征是每路段的24小时拥堵指数序列聚类数取3。为让结果可解释我对聚类后的每类求平均拥堵时变曲线再结合路段地理位置判断模式名称。如果你也想复现注意点是聚类前需要对特征做标准化不然数值范围大的路段会主导距离计算导致聚类结果失真。我自己一开始没有做标准化聚类出来的结果几乎把高速路和城市道路分开后来加了StandardScaler才正常。3.3 可视化图表选型与配色经验可视化是课程作业里最能直观体现“水平”的部分。我的原则是能用热力图不用散点图能用折线图不用柱状图能用交互地图不用静态地图。因为交通数据有明显的时空属性静态图很难呈现完整的趋势。我实际采用的图表有四种一是区域拥堵指数24小时折线图用matplotlib绘制用来展示整体变化趋势二是路段拥堵等级地图用folium绘制每个路段根据拥堵指数着色颜色从绿色渐变到红色展示空间分布三是交通流量热力图用folium的HeatMap插件把车辆位置密度叠加到底图上直观展示热点区域四是拥堵模式聚类结果图用不同颜色表示三类路段配合简短文字说明。这里有一个非常实用的配色经验连续型指标用渐变色时一定要避开红绿搭配因为红绿色盲群体看不出来我用的方案是绿—黄—红渐变同时通过图例加注文字说明。另外所有图表一定要加标题、坐标轴标签和图例这是基本盘丢了会很影响印象分。folium热力图的核心代码很简单import folium from folium.plugins import HeatMap m folium.Map(location[39.9, 116.4], zoom_start11) heat_data [[row[lat], row[lng]] for _, row in df_sample.iterrows()] HeatMap(heat_data, radius15, blur20, max_zoom1).add_to(m) m.save(traffic_heatmap.html)生成的HTML文件可以直接在浏览器打开截图插入实验报告非常方便。如果你论文里需要动态展示还可以直接把HTML文件打包进zip压缩包作为附件老师打开就能交互查看体验感完全不一样。4. 实验报告的高分写法4.1 报告结构问题—方法—结果—结论的逻辑闭环实验报告不是代码注释的堆砌也不是把源代码粘贴一遍就完事。我做这个项目时前后改了四版报告最终总结出一个比较稳妥的结构第一部分是问题定义说明你分析了什么问题为什么选这个问题数据来源是什么第二部分是方法介绍讲清楚你用了哪些预处理方法、指标如何定义、算法如何选择第三部分是实验过程展示关键代码和数据处理的中间结果这部分要控制篇幅不要每个函数都贴代码只放核心步骤第四部分是结果分析给出你发现的交通规律并配上图表最后是结论与反思总结项目价值说明目前方案的不足和可改进方向。这个结构对应的是数据分析的标准化流程逻辑闭环很完整。我见过很多低分报告通篇都在贴代码、贴图但看不到“为什么这样做”这是最致命的。4.2 图表编号与结论呼应的技巧图表是实验报告最重要的支撑材料但图表不是插上就行必须和结论形成呼应。我的做法是每个图表都配一段“图表解读”文字重点说明读者能从图中看到什么现象这个现象说明了什么问题。例如早高峰折线图出来之后我这样写“由图3可知区域平均速度在7:00-8:30之间呈陡降趋势最低值出现在8:00左右平均速度约为22.6 km/h表明该时段道路通行压力明显上升。这与该区域早晚通勤出行特征基本吻合。”另外图表编号要在正文里先出现再做图不要报告写完了才想起来插图。我习惯在写报告文字时先预留好图表位置比如在结论旁边写“见图X”最后统一插入图片和编号这样既不会漏图也方便调整顺序。4.3 老师们最反感的几种报告写法这里说点得罪人的实话。作为经历过答辩和帮老师打过分的人我总结几个常见的低分写法一是“结果导向造假”为了得到理想结论而人为筛选数据只挑好看的结果展示一旦老师追问就很被动二是“有图无解读”通篇堆了十几张图但没有任何解释文字老师根本看不明白你在表达什么三是“内容架构混乱”没有引言、没有方法、没有结论就像一份代码注释的打印版四是“结论过于空泛”比如“该区域交通状况不佳建议加强管理”这种没有任何数据支撑的废话。这些坑都要规避报告的字数是次要的逻辑和实证才是核心。5. 源码组织与zip交付规范5.1 代码目录要能让助教十分钟跑通期末作业如果只提交一个“实验报告.doc”加一个“code.py”会被扣分的。完整的工程化交付应该是一个结构清晰的目录尤其当你打包成zip传上去后助教或者老师打开压缩包的第一印象就是你的代码组织能力。我当时整理的目录结构是这样的traffic-analysis/ ├── data/ │ ├── raw/ # 原始数据如果太大放README里的下载链接 │ └── processed/ # 清洗后的数据文件 ├── notebooks/ │ ├── 01_eda.ipynb # 探索性分析 │ ├── 02_clean.ipynb # 清洗代码 │ └── 03_analysis.ipynb # 指标计算与可视化 ├── reports/ │ └── 实验报告.pdf ├── src/ │ ├── preprocess.py │ ├── analyze.py │ └── visualize.py ├── README.md └── requirements.txt这样的结构拿到zip包后助教按README一步步执行就能复现。很多同学喜欢把所有代码堆在一个main.py里最后几千行代码谁看了都头疼。模块化拆分虽然前期费点功夫但后面排查问题和写报告会省很多力。5.2 requirements.txt和README的写法requirements.txt是复现流程里最重要但最容易被忽略的文件。如果老师运行你的代码时发现缺少numpy、pandas、folium等库第一反应就是去安装而不是认为你的代码有问题。但如果你在README里写清楚了依赖版本和安装命令老师会认为你考虑得很周全。我的requirements.txt内容如下pandas2.0.3 numpy1.24.3 matplotlib3.7.2 geopandas0.14.0 folium0.15.0 scikit-learn1.3.0注意版本号一定要固定不能只写“pandas”不写版本。因为不同版本接口有差异写死版本号可以确保复现时不出兼容性问题。README虽然不是正课内容但它是你工程能力的体现。我一般会写四个部分项目简介、文件说明、运行步骤、结果截图。运行步骤要写清楚先执行哪条命令再执行哪条命令比如conda create -n traffic python3.9 conda activate traffic pip install -r requirements.txt python src/preprocess.py python src/analyze.py python src/visualize.py写出每一步的运行时间预期也很有用比如“预处理约需15分钟请耐心等待”避免别人以为程序死机了。5.3 打包zip时版本号、命名与压缩细节最后交付就是用zip把整个目录打包。这里有几个细节值得注意压缩包命名要规范不要叫“新建文件夹(3).zip”这种建议命名成“学号姓名交通数据分析期末作业.zip”。我见过很多同学用班级群发送的作业压缩包下载下来直接叫“作业.zip”助教存档时文件名就乱了。打包的时候还要注意不要把原始大文件重复打成两份。如果数据文件太大建议在README里提供数据下载链接而不要硬塞进zip否则zip会超过邮箱或者学习平台的附件限制。我当时的原始csv有2GB最后放进zip的只是一小份抽样数据约50MB全部代码实验报告。在README里说明完整数据下载方式这样既保证了作业完整性又避开了附件体积限制。如果是Windows上做压缩右键“发送到压缩文件”就行但要注意选择zip格式不要用rar因为很多在线学习平台对rar支持不好。如果在Linux服务器上做打包用unzip和zip命令非常方便下面这一节专门讲一下。6. 常见问题速查解压、乱码与环境配置6.1 提示“file is not a zip file”怎么办这个报错很多人都遇到过包括我自己。期末作业的zip包从平台下载下来后解压时提示file is not a zip file第一反应是文件损坏了但很多时候并不是。可能的原因有三个下载不完整、文件实际是rar或7z但后缀名被改成了zip、以及用记事本等程序打开过文件导致文件头损坏。排查方式很简单Linux下用file命令看一眼file received.zip它会直接告诉你是Zip archive data还是RAR archive data还是纯文本。如果是RAR改后缀名或者用对应工具解压。如果显示data说明下载过程出错重新下载一次。这个报错在用户留言里出现的频率很高我写实验报告时专门在“运行环境说明”里加了一条提示老师解压时如果遇到该报错可以先检查文件后缀名或者重新下载意外地显得很贴心。6.2 Linux下zip/unzip命令实操很多同学在Windows下用图形界面解压zip到了服务器上就不知道怎么操作了。期末作业如果要在服务器跑数据分析掌握几个Linux压缩解压命令是刚需。最常用的是这几条# 解压 unzip project.zip # 指定解压到目录 unzip project.zip -d /home/user/projects # 压缩目录 zip -r project.zip traffic-analysis/ # 排除某个文件或目录 zip -r project.zip traffic-analysis/ -x *.ipynb_checkpoints*有几个细节容易踩坑解压中文文件名会乱码通常是因为zip包的编码是GBK而Linux默认按UTF-8解压。解决办法是用unzip -O GBK指定编码unzip -O GBK project.zip如果不支持-O参数可以用python脚本解压用zipfile库指定编码处理或者先在Windows上解压再重新压缩。另外打包时尽量用zip -r这样会保留原有目录结构对方解压后不会把一堆文件散落一地。6.3 中文文件名乱码与编码处理乱码问题在打包交给老师或助教时经常发生。Windows压缩时默认编码是GBKLinux或Mac解压时按UTF-8解析文件名就成了乱码。这个问题不算复杂但对评审体验影响很大老师看到一堆乱码文件名第一印象会很差。最省事的方案是在Windows上打包前先把所有文件名改成英文或拼音比如实验报告_final.pdf改成lab_report.pdf。这样无论在哪解压都不会乱码。如果已经打包好了可以用unzip -O把乱码文件解压后重新命名再用zip打包一次。如果遇到压缩包里混入了“锟斤拷”这类乱码字符这类问题是编码转换失败后出现的典型符号处理方法同上先解压再用批量重命名脚本修正文件名代码很简单import os import re for name in os.listdir(.): new_name name.encode(gbk, errorsignore).decode(gbk, errorsignore) if new_name ! name: os.rename(name, new_name)6.4 Python环境冲突与依赖安装失败代码明明在自己的电脑上跑得好好的换台机器就报错这是期末作业传递过程中最常见的问题。通常有两类原因一是系统Python版本和Anaconda环境混用导致pip装到了错误的环境二是requirements.txt里的包版本和当前Python版本不兼容比如pandas 2.0以上需要Python 3.8以上。环境冲突的解决办法是永远用虚拟环境跑项目。不要直接pip install而是先用conda创建环境conda create -n traffic python3.9 conda activate traffic pip install -r requirements.txt如果安装geopandas失败多半是底层依赖问题可以先安装conda版geopandasconda install -c conda-forge geopandas再装其他的。这类坑我在实验报告里专门写了一条“常见环境问题排查”把我和室友遇到过的几个典型问题以及解决方案都列进去了也算是一个小差异化亮点。最后再分享一点我自己的体会。做这个交通数据分析与应用的期末大作业最大的收获不是那几个图表和结论而是完整走了一遍“拿到一个开放问题—自己做决策—处理脏数据—写代码验证—形成报告”的全流程。这个过程里踩过的坑比老师上课讲的内容印象深得多。你如果也在做类似的课程作业建议尽早动手尤其别在数据清洗上恋战把更多精力放到指标定义和结果解释上这两块才是拉开分差的地方。后续如果想扩展这个项目还可以加实时数据接入、OD分析、信号灯优化等方向每一步都能沉淀出新的课题。希望这次的源码和实验报告压缩包整理思路能帮到你。本文还有配套的精品资源点击获取