news 2026/9/25 1:59:48

深圳道路交通数据集实战:从数据清洗到随机森林分类全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深圳道路交通数据集实战:从数据清洗到随机森林分类全流程

简介:深圳道路交通数据集采自深圳市政府开放平台,覆盖深圳市各区的道路信息,可为机器学习项目提供城市级交通数据支撑。该数据集包含交通流量、车速、事故记录、天气条件等多维字段,适合用于交通拥堵预测、流量规律挖掘、路线规划与智能交通系统开发等场景,尤其适合数据科学研究者、算法工程师及高校相关专业学生实践使用。包体共2个文件,压缩包大小仅2.35MB,其中traffic.sql为数据库结构化文件,内含建表、索引等SQL定义,便于数据查询与管理;获取来源.txt则记录了数据采集的时间、地点与更新方式等背景信息,有助于理解数据适用范围与局限性。目前已有500人浏览学习,数据集体量轻便、字段结构化程度较高,上手门槛低,可作为交通类机器学习项目的高质量实验样本,便于快速导入数据库开展分析与建模。

1. 深圳道路交通数据集适合哪些机器学习项目:先看标签再谈下载

想用深圳道路交通数据集做机器学习项目,先别急着下载。这份来自深圳市政府开放平台的数据是道路级的路网信息,覆盖深圳各区,字段以道路属性为主——名称、等级、长度、起止点、行政区、坐标,适合做道路分类、路网聚类等任务。

它的优点是官方渠道公开、可复现、更新周期固定;局限是粒度到道路级,没有实时车流量和路况序列,做流量预测得再找时序数据。所以我一般把它当特征底表,而不是唯一数据源。

适合的人群是交通方向毕设的学生、做路网分析 PoC 的工程师,以及机器学习入门练手的数据开发。项目开发的第一步是数据体检,下面按取数、清洗、特征、建模、避坑五步展开。

2. 从深圳市政府开放平台取数:检索道路数据集、识别字段与首轮统计

深圳道路交通数据集的下载入口是深圳市政府数据开放平台。平台上的数据集通常以 CSV、JSON 或 Excel 格式发布,页面会标注更新频率、数据条数和数据字典。真正决定项目进度的不是下载速度,而是你有没有在下载前看懂元数据。我一般先做三项体检:更新频率是否在一年内、有没有数据字典、字段名是不是稳定的中文命名。这三项过关,数据集才值得进入你的机器学习项目。

2.1 在开放平台用"道路"关键词定位数据集:先做元数据体检

平台检索框输入"道路""路网""道路信息",通常会返回多个相关数据集,名称接近但发布部门不同。不要只看名字就下载,先打开数据字典,核对字段清单和样例值。政府开放数据的字段名会随批次调整,比如"道路等级"和"道路级别"可能指同一个东西,也可能不是。以平台当前发布的数据字典为准,下面这张表是我处理这类道路数据时最常见的字段清单,用途列直接对应后续机器学习特征的来源:

常见字段类型说明机器学习用途
道路名称字符串道路的中文名称去重和分组
道路编码字符串平台内部编码主键、去重
道路等级字符串高速/快速/主干/次干/支路分类标签或有序特征
所属区字符串福田、南山等行政区类别特征
起止点字符串起点和终点路段名可选文本特征
长度数值单位通常是米连续特征
坐标字符串经纬度或投影坐标空间特征
更新时间时间记录最后更新日期增量过滤

注意:字段名以平台数据字典当前版本为准,不同批次可能叫"道路等级"或"道路级别"。

只看样例文件前几行是不够的,样例无法暴露缺失、别名和单位不一致的问题。把数据字典和样例各看一遍,确认"长度"的单位到底是米还是公里,确认"坐标"是经纬度还是投影坐标,这些误判到清洗阶段才发现,返工成本很高。

2.2 把下载好的 CSV 读成 DataFrame:编码与读取参数的一次性设置

平台下载接口通常需要登录 token,自动化脚本容易随接口改版失效,所以我一般手动下载 CSV 到本地,再用 pandas 读取。读取参数有两个是常年固定的:encoding="utf-8" 和 dtype=str。前者解决中文乱码,后者防止长度这类数值字段被中间环节转成科学计数法后精度丢失。

import pandas as pd # 手动下载到本地后读取,先统一按字符串读入 df = pd.read_csv( "shenzhen_road.csv", encoding="utf-8", dtype=str, keep_default_na=False, ) print("行数:", df.shape[0]) print("列名:", df.columns.tolist())

这段代码的逻辑是把所有字段先读成字符串,交给后续清洗脚本统一做类型转换。keep_default_na=False 的意义在于,"暂无""无"这类占位文本不会被 pandas 自动当成空值,缺失规则由自己定义,而不是被解析器替你做决定。参数说明:encoding 只有两种候选,utf-8 不行就试 gbk,政府平台导出的 CSV 常见这两者;dtype=str 会让数值计算报错,所以只用于读取原始文件这一步,不要用在已清洗的数据上。

2.3 首轮统计:按行政区核对覆盖度,先扫一眼缺失率

拿到 DataFrame 后第一件事不是画图,也不是建模,而是确认覆盖面。深圳的行政区包括福田、罗湖、南山、盐田、宝安、龙岗、龙华、坪山、光明、大鹏,正常的数据集应该都能覆盖到。用 value_counts 看行政区分布,再对关键字段做缺失率统计。

# 行政区覆盖度核对,正常应覆盖全部10个区 print(df["所属区"].value_counts()) # 关键字段缺失率,超过5%建议进入清洗流程 missing = df[["道路等级", "长度", "坐标", "更新时间"]].isna().mean() print(missing)

value_counts 会暴露两个问题:一是某个区完全没有记录,二是"福田区"和"深圳市福田区"这种别名并存,导致同一个区被拆成两行。缺失率统计是给清洗脚本定工作量的,5% 是个经验阈值——低于 5% 的字段直接删行影响不大,高于 5% 就要考虑保留缺失标记做特征,而不是粗暴删除。这一步花五分钟,能省掉清洗阶段至少一半的返工。

3. 清洗深圳道路数据:把政府原始表变成机器学习可用的样本

政府开放数据不是为机器学习设计的,清洗顺序错了后面会很别扭。我一般按"类型校正 → 坐标处理 → 别名与去重"推进,每一步都在上一步的干净结果上操作,防止前面造出来的脏数据污染后面的判断。

3.1 类型校正与中文列名归一化:长度和时间字段不再拖后腿

原始 CSV 全部按字符串读入后,第一件事就是类型转换。长度字段要转成 float,更新时间要转成 datetime,这两步不做,后续所有数值特征和增量过滤都会出问题。

# 长度转数值,乱码统一变成 NaN df["长度"] = pd.to_numeric(df["长度"], errors="coerce") # 更新时间转时间类型 df["更新时间"] = pd.to_datetime(df["更新时间"], errors="coerce") # 列名归一成统一风格,后续特征代码少踩坑 df = df.rename(columns={ "所属区": "area", "道路等级": "road_level", "长度": "length_m", "坐标": "coord", "道路编码": "road_id", "道路名称": "road_name", })

errors="coerce" 的作用是把无法解析的文本变成 NaN,这样整列的 dtype 才能统一成 float。如果不加这个参数,一行脏数据会中断整个转换。列名归一化看起来是洁癖,实际是为了后面写特征代码时少打几个中文字段名,也避免多个 CSV 拼接时字段名不一致。转换完成后建议马上看一眼 dtypes 和各列空值数量,确认转换没有把整列变成 NaN。

3.2 坐标解析与越界过滤:用深圳的经纬度区间做第一道闸

坐标字段是这类数据最容易翻车的地方。平台给的格式可能是"113.123,22.456",也可能是投影坐标,先看数据字典确认。如果是经纬度,写一个兼容英文逗号和中文逗号的解析函数,再做越界过滤。深圳的经纬度大致范围是东经 113.0 到 115.0、北纬 22.0 到 23.5,明显越界的记录要么是坐标错位,要么是解析失败。

import pandas as pd import numpy as np import re def parse_coord(s): # 兼容 "113.123,22.456" 和 "113.123,22.456" if not isinstance(s, str): return pd.NA, pd.NA parts = re.split(r"[,,]", s.strip()) if len(parts) != 2: return pd.NA, pd.NA try: lon, lat = float(parts[0]), float(parts[1]) except ValueError: return pd.NA, pd.NA return lon, lat df["lon"], df["lat"] = zip(*df["coord"].map(parse_coord)) # 深圳经纬度大致范围,越界的直接过滤 valid = (df["lon"].between(113.0, 115.0)) & (df["lat"].between(22.0, 23.5)) df = df[valid] print("坐标有效占比:", round(valid.mean(), 4))

解析函数先用正则拆分隔符,再转 float,任何一步失败都返回空值,而不是抛出异常中断整条流水线。范围过滤用的是 between,闭区间方便调整边界。注意:如果数据字典写明是投影坐标,这套逻辑不能用,要按平台提供的投影参数转成经纬度,或者直接用投影坐标算距离特征。这里最核心的教训是,坐标字段的格式判断比解析本身更重要。

3.3 去重与行政区别名归一:顺序反了会误删有效记录

政府平台的数据经常同一条道路分多条记录发布,比如按路段拆分。去重前先想清楚主键:有道路编码的按编码去重,没有的按"道路名称+所属区+长度"组合判断。别名归一必须放在去重前面,否则"福田区"和"深圳市福田区"会被当成两条不同的记录,去重后留下哪条都是随机的。

# 先做别名归一,再去重,顺序不能反 alias_map = { "深圳市福田区": "福田区", "福田": "福田区", "深圳市南山区": "南山区", "南山": "南山区", } df["area"] = df["area"].replace(alias_map) # 按道路编码去重,保留最新更新的一条 df = df.sort_values("更新时间").drop_duplicates( subset=["road_id"], keep="last" )

replace 传入字典,把别名统一到标准区名。drop_duplicates 前先 sort_values,保证按时间排序后 keep="last" 保留的是最新记录;如果不排序,保留哪条由文件内部顺序决定,结果不可控。做完这步,再用前面的 value_counts 核对一遍行政区分布,应该能看到一个干净的十区结构。去重后的行数会明显小于原始行数,这是正常的,不代表数据丢了。

4. 特征工程与基线模型:用深圳道路数据跑通道路等级分类

原始数据没有标签,这是这类"道路信息表"最常见的问题。我的做法是给自己造一个有监督任务:用长度、行政区、坐标等属性预测道路等级。道路等级本身就是政府维护的权威标注,把它当目标,既验证了数据质量,也拿到一个能跑通的机器学习模型基线,后续真要换任务,特征工程成果可以复用。

4.1 从道路长度构造连续特征和分箱特征:让模型拿到可解释的尺度

长度是这份数据里最直接的连续特征,但原始数值跨度大,从几百米的支路到几十公里的快速路都有,直接喂给模型会被长尾带偏。常见做法是同时保留对数变换和分箱特征。

import numpy as np # 千米化,让数值量级更直观 df["length_km"] = df["length_m"] / 1000.0 # 对数变换压缩长尾分布 df["length_log"] = np.log1p(df["length_m"]) # 分箱特征,边界按道路常识设定 df["length_bin"] = pd.cut( df["length_km"], bins=[0, 1, 3, 10, 100], labels=["短", "中", "长", "超长"], )

log1p 在长度为 0 时不会报错,比 log 更适合含零数据,这是它作为默认选择的原因。分箱边界 1/3/10/100 是经验值:1 公里以下是支路和小区路常见尺度,3 公里是短连接路阈值,10 公里以上通常是跨区干道。用 pd.cut 生成的类别特征可以直接进模型,也可以再 one-hot。想省事就只用 length_log,分箱特征主要是给树模型增加非线性切分点,实际提升有限,但可解释性好。

4.2 类别特征编码:道路等级做有序映射,行政区走 one-hot

道路等级本身有顺序:高速公路、快速路、主干路、次干路、支路,从高到低。对这种有序类别,用字典映射成整数比 one-hot 更合理,可以保留顺序信息。行政区没有顺序关系,属于名义变量,用 one-hot 编码。基线阶段不需要做太多区划交叉特征,one-hot 够用。

level_order = { "高速公路": 4, "快速路": 3, "主干路": 2, "次干路": 1, "支路": 0, } df["level_code"] = df["road_level"].map(level_order) # 把没映射上的未知等级过滤掉,避免脏标签进模型 df = df[df["level_code"].notna()] # 行政区 one-hot area_dummies = pd.get_dummies(df["area"], prefix="area") df = pd.concat([df, area_dummies], axis=1)

map 返回空值表示未知等级,这里选择直接过滤,因为等级是你要预测的目标,标签不干净,模型学到的东西不可信。get_dummies 默认会把所有类别展开,如果某个区在测试集出现而训练集没有,预测时会列数不一致,这就是为什么后续要用字段清单做兼容性检查。等级映射的数字大小只表示相对顺序,不表示间距,树模型不受影响,线性模型要小心。

4.3 随机森林基线:参数怎么设,先看分类报告而不是只盯准确率

特征不多、样本量中等的情况下,随机森林是比 XGBoost 更稳妥的机器学习模型基线,不用调太多参数就能给出合理结果。样本量上了几十万再换 XGBoost 或 LightGBM。切分时用 stratify 按等级比例分层,防止测试集里某类等级一个都没有。

from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report feature_cols = ["length_log"] + [c for c in df.columns if c.startswith("area_")] X = df[feature_cols] y = df["level_code"] # 分层切分,保证测试集里每个等级都有样本 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) clf = RandomForestClassifier( n_estimators=300, max_depth=10, min_samples_leaf=5, class_weight="balanced", random_state=42, ) clf.fit(X_train, y_train) print(classification_report(y_test, clf.predict(X_test)))

参数说明:n_estimators 从 100 加到 300 收益递减,超过 500 只增加训练时间;max_depth 限制在 10 以内,防止对道路样本过拟合;min_samples_leaf=5 让每个叶子至少有 5 个样本,预测更稳;class_weight="balanced" 是针对支路占比过高的问题,如果不设,模型会倾向把所有样本预测成支路,准确率虚高但小类全错。评估指标看宏平均宏 F1 或每个等级的分类报告,不要只看 accuracy。

一个容易忽略的点:这里用的是简单随机切分,只适合基线验证。同一条道路的多条记录如果同时出现在训练集和测试集,会发生数据泄漏,测试指标虚高。严格做法是按道路名称分组切分,我在第五章的踩坑记录里专门展开。

4.4 特征重要性检查:判断长度、区划哪个在真正起作用

随机森林不是黑匣子,训练完把 feature_importances_ 打出来看一眼,能判断模型到底在学什么规律,也能发现特征构造是否有问题。

importances = pd.Series( clf.feature_importances_, index=feature_cols ).sort_values(ascending=False) print(importances.head(10))

如果 length_log 排第一,说明道路规模在决定等级,符合常识;如果某几个区划特征异军突起,说明等级分布有明显地域差异,可以往"各区道路建设标准不同"的方向做业务解读。特征重要性还有一个用途:验证特征是否传对了。如果某个你认为很重要的特征重要性为 0,先检查是不是全列空值或编码错误,而不是怀疑模型。

5. 避坑:深圳道路交通数据集落地会踩的 5 个实际问题

下面五条是我用这类政府道路数据踩过的坑,每一条都按现象、原因、解决展开。这些问题在深圳道路交通数据集上出现的概率很高,提前看过再看后续代码,能少走弯路,尤其最后一条数据泄漏,属于隐蔽性最强、返工代价最大的一类。

5.1 行政区字段出现别名和缺失,value_counts 直接少了一区

现象:首轮统计时发现只出来 9 个区,某区记录数莫名少了一半。 原因:同一行政区分批录入,存在"福田区""深圳市福田区""福田"三种写法,value_counts 把它们当成三个类别,真实分布被拆散。 解决:做一张别名映射表统一区名,再做一次分布核对。注意别名表要覆盖平台历史版本,只覆盖当前版本,下次更新数据又会出现新写法。别名映射表建议单独存成 JSON 或放到配置模块,便于增量补充。

5.2 道路等级是中文文本,没编码就塞进模型直接报错

现象:训练时报错 "could not convert string to float",或者树模型把每个中文等级当成独立类别,效果奇差。 原因:pandas 读进来是字符串,sklearn 的树模型不接受字符串特征,而最容易忘记的就是先做标签编码。 解决:按等级有序映射成整数,再检查 map 之后是否存在空值。顺序映射表写成模块级常量,训练和推理共用同一份,避免两处维护两份映射导致不一致。映射表里留一个"unknown"分支比直接报错更好排查。

5.3 用 Excel 打开过 CSV 再另存,坐标精度被悄悄截断

现象:解析坐标后,有效过滤掉了四分之一的数据,检查发现经纬度小数位从 6 位变成 3 位。 原因:Excel 默认显示精度,另存为 CSV 时把小数位截断,原来能区分两条相邻道路的坐标变成完全相同的两个点。 解决:下载后直接用 pandas 读取原始 CSV,不要用 Excel 打开再另存。如果必须用 Excel 查看,只看不存,或者复制到新文件处理,不要覆盖原文件。这个坑的隐蔽之处在于文件能正常打开,肉眼也看不出精度变化,只有统计唯一坐标数量时才会发现。

5.4 支路占比过半,模型全体预测支路但你还觉得准确率不错

现象:准确率 0.7 上下,看起来不错,打开分类报告发现高速公路和快速路的精确率几乎为 0。 原因:数据里支路、次干路占比太高,模型学到的捷径是把所有样本都预测成支路,整体准确率依然好看。 解决:训练时加 class_weight="balanced",评估指标改用宏平均宏 F1。如果还是不均衡,对训练集做分层抽样,或者按等级设置采样权重。不要用准确率作为这类数据的最终指标,这是机器学习项目里最容易自我误导的地方,准确率只配当辅助参考。

5.5 同一条道路分了多条分段记录,按行切分造成数据泄漏

现象:验证集效果很好,上线换新数据效果大幅下滑。 原因:同一条道路按路段拆成多条记录,随机切分后同一条道路的记录同时落在训练集和测试集,模型其实是在背道路,而不是学规律。 解决:按道路编码或道路名称分组切分,用 GroupShuffleSplit 代替 train_test_split。实现上只差一个参数,但决定了这个模型到底有没有泛化能力。

from sklearn.model_selection import GroupShuffleSplit gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, test_idx = next( gss.split(X, y, groups=df["road_id"]) ) X_train, X_test = X.iloc[train_idx], X.iloc[test_idx] y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]

GroupShuffleSplit 的 groups 参数传入每条样本所属的道路编码,切分时保证同一个道路编码的所有分段记录只出现在同一侧。n_splits=1 表示只生成一组切分,test_size 保持 0.2 不变。如果平台数据没有道路编码,退而求其次用"道路名称+行政区"组合当 group 键,效果接近。

6. 版本化与增量验证:重新取数后如何判断该不该重训

政府开放平台会周期性更新道路数据,新文件的字段顺序和取值可能变化。我第一次拿新文件直接推理,模型输出乱套,追查才发现缺了一列特征。之后养成习惯:训练结束把字段清单、等级映射、模型参数一起存成 json。

# 把训练时的字段清单和等级映射存下来 import json meta = { "feature_columns": feature_cols, "level_map": level_order, } with open("model_meta.json", "w", encoding="utf-8") as f: json.dump(meta, f, ensure_ascii=False, indent=2)

推理脚本先读这份 json,对比新数据的列集,缺列或列顺序不一致就停下来报警,而不是带着错的特征硬跑。ensure_ascii=False 让中文映射表直接可读,indent=2 方便 git diff 查看改动。这个习惯成本极低,但能挡住大部分"换数据后悄悄变差"的问题。

第二个快检是分布漂移。把新数据的行政区占比和训练时存下来的占比对比,如果某个区掉了 10 个百分点以上,说明取数口径或道路覆盖变了,先查平台更新说明再决定要不要重训。判断逻辑很简单:占比差异小,沿用旧模型;差异大,重训并复查清洗脚本。

我现在每次重新取数,第一件事跑字段体检脚本,第二件事看分布对比,两项都过了才谈重训。这个习惯帮我挡住好几次线上翻车,比任何调参都值。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 1:59:47

ESP32-S3+LVGL打造实时音乐频谱可视化装置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 1:59:27

Turtlebot2 ROS自主导航实战:建图、定位、路径规划与参数调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 1:59:18

微信小程序+SSM+Mysql景区导览系统毕设实战:环境搭建到避坑指南

简介:这份资源是面向计算机专业学生与Java初学者的一套完整毕业设计/期末大作业方案,主题为乌鲁木齐景区导览系统,采用微信小程序前端搭配SSM框架与MySQL数据库实现。包内包含论文、PPT、需求分析文档及演示视频,覆盖从选题立项到…

作者头像 李华
网站建设 2026/9/25 1:56:40

150款CSS3 loading动画拆解:从实现原理到组件化落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 1:55:34

Anaconda零基础安装配置教程:从conda环境到Jupyter Notebook实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华