我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

城市轨道交通数据可视化:Python大作业从数据到页面全攻略

城市轨道交通数据可视化:Python大作业从数据到页面全攻略 简介这是一份以中国城市轨道交通数据为主题的Python数据可视化大作业源码适合计算机相关专业学生用作期末大作业、课程设计或数据可视化实战练习。项目为大三学生高分设计经导师指导认可评审分达99分代码完整可运行零基础也能快速上手。压缩包共35个文件、约3.43MB其中包含3个Python脚本爬虫、数据分析、检索模块subway.csv与city_line.db等原始数据文件20张PNG图表和4个HTML交互页面覆盖各城市线路站点数量、换乘站分布、地铁站命名风格、大学数量与站点关系等多个维度并配有README说明文档帮助理解项目结构与运行流程。目前已有381人学习下载。通过该项目可掌握从数据爬取、清洗、统计到图表呈现的完整数据可视化流程学习饼图、散点图、趋势图、双变量图等图形的绘制方法并可直接在此基础上扩展新城市数据或调整分析维度用于完善自身课程作业。1. 为什么中国城市轨道交通数据可视化是性价比最高的 Python 大作业选题如果期末只剩两周又想拿 95 分以上选题比写代码重要得多。中国城市轨道交通数据可视化分析这个方向数据好找、维度丰富、图表类型能撑起一个完整的 Web 页面而且主题自带“城市发展”的现实意义答辩时天然有的聊。相比电商爬虫、疫情数据这类被写烂的选题轨道交通数据既不会太冷门到没数据也不会太热门到跟同学撞车。这个选题真正考察的不是画图而是三层能力数据清洗干不干净、图表选得合不合理、页面有没有形成一个完整的分析闭环。大作业拿高分的本质是让老师一眼看出你做了“数据分析”而不是“画了几张图”。下面从数据、工程、图表、避坑四条线展开全程给可复现的步骤。2. 数据是可视化的地基中国城市轨道交通数据集从哪找、怎么清洗2.1 数据来源和字段设计先想清楚要回答什么问题动手爬数据之前先列清楚要回答的问题。常见做法是围绕四个维度建数据表城市线路统计、站点分布、客流强度、开通时间线。这四个维度能支撑起“中国城市轨道交通发展现状”这个核心命题也恰好对应答辩时老师最爱问的“你的分析维度是什么”。数据的获取渠道常见的有三个方向。第一是各城市地铁官网或轨道交通集团年报能拿到线路里程、站点数量、年客运量这类权威数据缺点是格式不统一PDF 和网页混杂。第二是百科类网站的城市轨道交通词条有整理好的线路列表和开通年份适合批量处理。第三是地图服务的 POI 查询能拿到站点经纬度坐标但需要处理配额和返回结果的分页问题。我的建议是不要只依赖一个渠道用年报数据做底数用百科数据补全线路开通年份用 POI 数据补坐标三份数据交叉验证。字段设计上先定义四张表字段清晰后面所有图表都不用返工。线路表放城市、线路名称、开通年份、线路长度、站点数量、运营状态站点表放站点名称、所属线路、城市、经纬度、换乘线路数客流表放城市、年份、年客运量、日均客运量、客流强度城市表放城市名称、省份、城市等级、GDP可选。这样分表的直接好处是画地图用城市表画趋势用客流表画排名用线路表职责单一后期加图表不用改原有逻辑。爬取过程我一般会做一层缓存把每次请求结果存成 JSON 文件一方面避免重复请求被限流另一方面大作业需要提交源码和数据文件本地留一份完整数据是加分项。常见做法是加一个带重试的函数超时重试三次每次间隔两秒实测对大多数公开数据源都够用。2.2 数据清洗的四个标准动作类型、缺失、重复、口径数据拿到手后最花时间的不是写代码而是清洗。轨道交通数据里典型的脏数据有三类里程单位不统一有的写“公里”有的写“km”开通年份格式混乱“2010年12月”和“2010/12”并存还有换乘站被不同线路重复计数。这些问题在清洗阶段不解决后面做图表时全部会翻车。以下是清洗的核心片段pandas 直接处理import pandas as pd # 读取原始线路数据 df pd.read_csv(data/raw_lines.csv) # 1. 统一里程单位把 km、公里、千米 统一转为数值型 def parse_length(x): if isinstance(x, str): num .join(ch for ch in x if ch.isdigit() or ch .) return float(num) if num else None return x df[length_clean] df[length].apply(parse_length) # 2. 解析开通年份从 2010年12月28日 这类文本里提取年份 df[year] df[opening_date].str.extract(r(20\d{2})).astype(Int64) # 3. 工程口径只保留运营中的线路过滤在建和规划线路 df df[df[status] 运营中] # 4. 去重同一线路名在不同数据源可能重复出现 df df.drop_duplicates(subset[city, line_name], keepfirst)逻辑说明第一步把字符串型的里程统一成浮点数Python 的isdigit()对中文数字和字母不生效所以先过滤非数字字符第二步用正则抽取四位年份.astype(Int64)是 pandas 的可空整型能保留缺失值而不是转成浮点第三步工程口径必须做年报数据里常混入“在建”“规划”状态的线路不筛掉会把总里程数算错第四步去重要指定subset因为不同城市可能有一条同名线路比如“1号线”在十几个城市都有只按线路名去重会误删。参数说明keepfirst表示保留第一条记录如果两个数据源有冲突建议先在前面把权威数据源排在前面再执行去重。extract(r(20\d{2}))这个正则只匹配 20 开头的四位年份目前所有开通的地铁线路都满足但如果未来有 19xx 年的老线路需要把正则在 2 后面加一个(?:0|1|9)的兼容。过滤运营状态那一步建议先输出一步 value_counts 看看状态字段到底有几种取值不同数据源可能叫“已运营”“运营中”“Open”要先归一化再过滤。站点坐标的补全通常单独处理因为 POI 数据拿过来是 JSON 嵌套结构。核心操作是用 pandas 的json_normalize展开然后按站点名称去重再跟线路表做关联。站点表和线路表是一对多的关系一个换乘站出现在多条线路里这是正常现象但画散点图时要先去重再画否则同一个站点坐标会被画好几次导致图上有重叠点。3. 技术栈选型与最小工程pyecharts Flask 的组合为什么最稳3.1 选型理由ECharts 图表生态和 Python 工程能力的平衡点可视化大作业的技术栈常见选择有三条路纯 matplotlib 静态图、pyecharts 生成 HTML、前后端分离的 Web 工程。纯 matplotlib 的硬伤是默认样式太“科研风”跟“数据可视化分析”的名字不匹配而且交互能力几乎为零做不出下钻和联动。前后端分离对两周工期来说太重Vue 或 React 的学习成本会吃掉写分析的时间。pyecharts 加 Flask 是这条路线上最稳的组合。pyecharts 底层是百度的 ECharts图表类型覆盖地图、散点、漏斗、桑基图而且代码是链式调用跟写 Python 的习惯一致不需要碰 JavaScript 一行。Flask 只承担两个职责把 pyecharts 生成的 HTML 拼装进模板以及启动一个本地服务方便答辩演示。整个工程只有 Python 一种语言老师检查源码时不用面对一堆 node_modules。版本上有一点要提醒pyecharts 1.x 和 0.5.x 的 API 差异很大网上很多旧教程用的是 0.5 的add()写法新版本已经改成add_coord这种新接口。装的时候直接pip install pyecharts会拿到 1.x 以上版本代码写法以官方文档为准别照抄老博客。地图数据包要单独装用pip install echarts-countries-pypkg这类 pypkg 包补充中国地图和城市地图不装的话地图组件只会渲染出空白网格。3.2 跑通最小工程从数据文件到网页展示的一条龙脚本直接给一个能跑的最小工程骨架。项目目录结构按“数据、图表、服务”三层切分data 放清洗后的 CSVcharts 放生成图表的脚本app.py 是 Flask 入口。这个结构本身就是大作业的好看门面老师打开目录就明白作者的工程意识。先写一个生成图表的模块把地图和趋势图都封装成函数# charts/china_map.py from pyecharts import options as opts from pyecharts.charts import Map def china_total_map(df_city): 绘制中国城市地铁里程分布图 data [(row[city], row[total_length]) for _, row in df_city.iterrows()] chart ( Map(init_optsopts.InitOpts(width100%, height600px)) .add( series_name运营里程(km), data_pairdata, maptypechina, is_roamTrue, label_optsopts.LabelOpts(is_showFalse), ) .set_global_opts( title_optsopts.TitleOpts(title中国城市轨道交通运营里程分布), visualmap_optsopts.VisualMapOpts(min_0, max_800, is_piecewiseFalse), ) ) return chart def save_chart(chart, filename): 渲染为 HTML 文件 chart.render(ftemplates/{filename})逻辑说明data_pair接收的是(城市名, 值)的元组列表城市名必须跟地图组件内置的省份名一致才能对上地理坐标这是地图组件最常踩的坑之一。is_roamTrue开启缩放和平移答辩演示时能放大地图看细节这个小交互是加分项。visualmap_opts的max_参数决定了颜色映射上限建议先查看数据的最大值如果最大值是 800 但max_设成 1000整个地图的颜色都会偏浅视觉上区分度很差。Flask 入口的写法# app.py from flask import Flask, render_template import pandas as pd from charts.china_map import china_total_map, save_chart app Flask(__name__) app.route(/) def index(): # 读取清洗后的数据 df_city pd.read_csv(data/city_lines_summary.csv) # 生成图表 HTML首次访问时生成一次后续直接用缓存文件 save_chart(china_total_map(df_city), china_map.html) return render_template(index.html) if __name__ __main__: app.run(debugFalse, port5000)逻辑说明这里把图表生成放在请求处理函数里是为了保证每次启动服务都是最新数据。debugFalse是答辩演示时的稳妥设置debugTrue虽然改了代码能自动重载但一旦代码里有异常页面会直接抛出一个带调用栈的报错页面演示现场很尴尬。port5000是 Flask 默认端口如果本地被占用改成 5001 或 8080 都行。模板页面把图表和文字结论拼在一起。用一个最简单的 iframe 嵌图表文件再配一段分析结论形成“图 文”的完整分析块避免整页全是图没有观点。实际运行时建议启动后先自己完整访问一遍四个页面确认没报错再保存截图答辩 PPT 的演示环节直接切浏览器不要用录屏。4. 可视化落地中国城市轨道交通分析页面的图表组合与分析深度4.1 图表选型逻辑每种图都对应一个具体问题不搞装饰图很多大作业翻车在“堆图表”——一个页面放了十几个图每个图都在展示不同信息但串不成一条分析线。高分页面的标准是每个图回答一个问题所有问题连起来支撑一个核心结论。轨道交通数据可视化适合的核心结论可以定为“中国城市轨道交通发展的空间不均衡性与时间阶段性”围绕这个结论至少需要四类图来支撑。地图类图表回答“空间上哪里强哪里弱”。用中国地图展示各城市运营里程和站点数量颜色深浅一眼看出东部沿海和成渝地区的集中度。pyecharts 的Map组件对数值型数据有视觉映射不需要额外处理就能看到梯度。排名类图表回答“谁在前谁在后”。横向条形图展示 TOP10 城市线路里程用Bar组件的reversal_axis()方法把坐标轴反转让数据从高到低纵向排列更符合阅读习惯。时间类图表回答“发展节奏怎么变化”。折线图展示全国每年新增线路里程可以看到几个明显的跃升节点这条曲线是整个项目里最有“分析感”的图。比例类图表回答“线网结构怎么样”。饼图或环形图展示已开通线路的制式分布地铁、轻轨、有轨电车、市域快线或者换乘站占比这类数据很容易从线路表里聚合出来。选图的核心不是“好看”而是“这个信息用什么编码方式最直观”。城市名和里程的对应关系用地图的位置编码比用表格强得多排名关系用条形图的长度编码比饼图强得多因为人眼对长度比对角度敏感。答辩时如果老师问“为什么这里用条形图不用饼图”能答出这条就说明真的理解了可视化原理。4.2 组合联动让图表之间形成分析闭环而不是孤立拼盘图表之间不能是孤岛。两个维度让页面“活”起来一个是筛选联动一个是叙事顺序。筛选联动用 Flask 的查询参数就能实现不需要上 JavaScript 框架。点击城市跳转到该城市的详情页详情页展示该城市各条线路的长度对比和开通时间轴这是最直观的下钻。叙事顺序按“总分总”的结构组织页面。首页放全国总览地图加全国累计里程数、城市数量等几个关键指标卡片让老师一眼看到结论。中间页放分维度分析TOP 城市排名、年度新增里程趋势、线网制式结构。末页放城市详情选一个代表性城市比如成都或武汉近十年地铁发展最快的城市做案例拆解。这样页面结构本身就是一份分析报告老师往下滚动时思路是连贯的不会被东一张西一张的图表打乱。以下是条形图和折线图的生成代码注意看两个图的参数差异# charts/trend_charts.py from pyecharts import options as opts from pyecharts.charts import Bar, Line def top10_bar(df_top10): TOP10 城市运营里程横向条形图 cities df_top10[city].tolist() lengths df_top10[total_length].tolist() chart ( Bar(init_optsopts.InitOpts(width100%, height500px)) .add_xaxis(cities) .add_yaxis( series_name运营里程(km), y_axislengths, label_optsopts.LabelOpts(positionright), itemstyle_optsopts.ItemStyleOpts(color#337ecc), ) .reversal_axis() .set_global_opts( title_optsopts.TitleOpts(title中国城市轨道交通运营里程 TOP10), yaxis_optsopts.AxisOpts(name城市), xaxis_optsopts.AxisOpts(name里程(km)), ) ) return chart def yearly_trend_line(df_yearly): 全国年度新增里程折线图 years df_yearly[year].astype(str).tolist() added df_yearly[added_length].tolist() chart ( Line(init_optsopts.InitOpts(width100%, height450px)) .add_xaxis(years) .add_yaxis( series_name新增里程(km), y_axisadded, is_smoothTrue, label_optsopts.LabelOpts(is_showTrue), markpoint_optsopts.MarkPointOpts( data[opts.MarkPointItem(type_max, name峰值)] ), ) .set_global_opts( title_optsopts.TitleOpts(title全国城市轨道交通年度新增里程趋势), yaxis_optsopts.AxisOpts(name新增里程(km)), ) ) return chart逻辑说明条形图里reversal_axis()必须放在add_yaxis之后调用它交换的是 X 和 Y 轴的角色数据本身不用动。label_optsopts.LabelOpts(positionright)在反转后才会显示在条形右侧如果忘记设置默认标签显示在条形内部数字大时会被遮住。折线图的markpoint_opts是最直观的“分析痕迹”——自动标记出新增里程最高的年份这个细节老师在答辩时常会指着问回答“自动找最大值并标注”就能体现对组件的熟练度。is_smoothTrue把折线变成平滑曲线数据本身没有精度损失只是视觉更连贯。细节上图表的标题不要只写“折线图”三个字要写成“全国城市轨道交通年度新增里程趋势”这个标题本身就是分析结论的浓缩。X 轴的年份建议转成字符串再传如果传整数而年份跨度太大ECharts 默认按连续刻度显示可能出现没有数据的空档年份。所有图表的width设置成百分比而不是像素这样浏览器缩放时图表自适应投影仪上演示也不会变形。5. 大作业避坑城市轨道交通数据可视化最常见的 5 个翻车点5.1 地图组件白屏或城市名对不上现象地图渲染出来是完整中国轮廓但所有省份和城市都是灰色或者某些城市的数据没有显示。原因两类情况居多一是 pyecharts 的地图数据包没装完整只装了核心包没装中国地图包二是 CSV 里的城市名和地图组件内置的名称不一致比如数据里写“成都”但地图包收录的是“成都市”或者写“台北”而地图组件里是“台湾”。解决先确认地图包安装完整再执行一步名称映射常见做法是在预处理时给数据加一列“地图名”用字典手动对应。检查城市名最快的方法是先打印地图包支持的区域名列表跟数据里的城市名单做一次集合运算差异一眼就看出来。5.2 CSV 里的数字被 pandas 读成字符串现象图表画出来数值轴上一片空白或者柱子的高度都一样。原因从网页或 PDF 复制数据时混入了全角字符、空格、单位pandas 读取时识别成字符串。这个问题在里程字段上尤其常见因为源数据写着“28.5公里”而不是“28.5”。解决在清洗阶段统一做类型转换pd.to_numeric(series, errorscoerce)可以把非法字符串转成 NaN再做一次dropna()。这里注意errorscoerce会把“28.5公里”转成 NaN 而不是 28.5所以要先做正则提取数字再做类型转换顺序不能反。5.3 图表渲染后中文全部变成方块现象浏览器打开 HTML所有标题和图例的中文显示为豆腐块。原因绝大多数情况是 HTML 文件缺charset声明或者 pyecharts 生成模板的编码跟浏览器默认编码不一致。解决在 HTML 的 head 里手动加meta charsetutf-8如果用的是 Flask 的render_template确保模板文件本身以 UTF-8 保存。这个问题在 Windows 上格外高发因为本地文件可能被编辑器默认存成了 GBK用 VS Code 右下角把编码切换成 UTF-8 重新保存一次就行。5.4 数据口径不统一导致结论自相矛盾现象首页写“全国累计运营里程 1 万公里”趋势图里加总却是 9000 公里答辩时被老师当场发现。原因不同数据源对“运营里程”的定义不同有的算地铁制式有的包含有轨电车和市域快线有的统计到去年年底有的统计到今年年中。解决在数据文件旁边放一个 README写明每条数据的统计时点和口径说明这里不需要写多复杂两句话讲清楚即可。图表上也要做标注比如“数据截至 2023 年 12 月”这个细节能把严谨性拉高一个档次。数据的一致性检查要写进清洗流程里做完每张表都sum加总核对一遍。5.5 大作业只交一个 HTML 文件没有任何工程结构现象源码目录打开只有index.html和一个data.csv图表全部是静态文件。原因图省事直接用 pyecharts 生成 HTML 后就没再管工程结构。这类作业即便图表再漂亮也大概率被归到“低分档”因为大作业考察的是“用 Python 做数据分析处理的能力”而不是“调出一个图表的能力”。解决按上一章的目录结构组织工程——data/放原始数据和清洗后数据scripts/放清洗脚本charts/放图表生成代码app.py放服务入口templates/放页面模板。哪怕整个工程一共只有五个.py文件这个结构也会让老师觉得“这是一个完整的分析项目”。6. 从 85 到 95答辩口径、叙事串联与最后的验证方法高分和低分的分界线往往不在图表数量在“分析有没有闭环”。最后一步做两件事一是把页面叙事按“数据—图表—结论”三层串讲一遍二是把隐藏的得分点挖出来展示给老师看不要等着老师自己发现。具体做一个“关键指标卡片”区块放在地图上方全国开通城市数量、累计运营里程、年度最高新增量、最大换乘站深度。这四个数字从四张数据表里聚合而来每个卡片配一句结论式描述比如“截至 2023 年底全国共有 xx 个城市开通轨道交通”。这一屏不用任何图表但它是整个页面最像“分析报告”的部分答辩时先讲这四个数字再进入图表逐个展开节奏会非常清晰。二级页面加一个“城市详情对比”功能选两个城市做并排对比——比如北京和成都一个老牌、一个新兴对比线路总长、站点密度、年均新增里程三个维度。两个城市的并排对比比单看排名更有说服力因为展示了“这个数据跟什么比才有意义”。答辩时主动讲这条很容易把话题引向城市发展差异这个方向老师觉得你是在做数据分析不是在交作业。最后验证环节我自己吃过一次亏所有图表在 Chrome 上正常答辩现场的投影仪分辨率是 1024 x 768页面直接横向溢出右侧图表看不全。后来在代码里统一加了响应式配置——width100%加上 Flask 模板里的 viewport 设置。交作业前养成一个习惯用浏览器开发者工具模拟 1280 和 1024 两种分辨率各截图一次确认没有布局错乱再交源码包。这一步的成本只有五分钟却是最值得花的五分钟。整个项目做完你手里会留下一套可复用的分析工程一份清洗干净的数据表、一张覆盖地图/条形图/折线图/饼图的图表库、一个 Flask 服务骨架。这套东西以后做其他选题餐饮数据、房价数据、空气质量数据都能直接换数据复用不是一次性的“作业代码”。我当年做完这个项目后把图表库的函数改改就接入了另一个课程设计省了至少三个晚上的工作量。数据可视化大作业真正的收获不是说会调用 pyecharts而是建立了“先想结论、再选图表、最后编码”的分析习惯——这套思路在任何数据岗面试的案例分析题里都是实打实的加分项。希望这个方案能帮到你少走一点我走过的弯路。本文还有配套的精品资源点击获取
返回列表