news 2026/10/8 4:57:14

电动汽车数据集清洗与特征工程:从电池规格到价格预测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
电动汽车数据集清洗与特征工程:从电池规格到价格预测

简介:这是一份面向数据分析、市场研究及电动汽车行业爱好者的真实电动汽车数据集,围绕2025年销售的3K+条记录,覆盖特斯拉、宝马、日产等品牌的电池规格、续航里程、充电方式、价格、产地、安全等级及销量等属性,可用于车型对比、电池技术趋势和价格区间分析。资源打包为zip压缩包,共3个文件,分别提供xlsx表格便于Excel透视、csv通用格式适合编程建模、json结构适合Web应用集成,体积仅492KB,数据规整、便于上手。目前已有204人学习下载,适合需要快速获取结构化电动车数据的学习者或研究者使用。数据中还包含多个年份、插电混动车型及部分缺失字段,使用者可借此练习数据清洗和单位统一,也可观察全球主流车企在2025年的产品布局,并进一步开展安全评级与续航表现等交叉分析。

1. 电动汽车数据集:3K+真实记录撑得起哪些分析

2025年做电动汽车相关的数据项目,最不缺的是模型,最缺的是一份能直接落地的真实车辆数据。这份电动汽车数据集把特斯拉、宝马、日产的电池规格和2025年销售数据收成了3K+条记录,覆盖了二手残值评估、电池衰减回归、区域市场价差这些常见的从业场景,也是新车价格预测模型能起步的最小样本量。适合正在做新能源行情分析的工程师、准备用真实车辆数据训练价格与续航分类模型、又不想从零写爬虫的团队,拿到之后可以直接验证想法、跑通基线,不必从抓取和清洗的泥潭里起步。

2. 先读懂这批数据:电池规格与销售字段到底怎么对齐

2.1 电池规格字段要拆开看:容量、电压、测试标准各说各话

拿到这个数据集先别急着做特征工程,先把“电池规格”四个字拆开。真实来源往往是官网参数表、论坛、第三方数据库混在一起,字段叫法很乱。我一般会强制要求至少保留五列:电池类型(三元锂/磷酸铁锂)、标称总容量(kWh)、可用容量(kWh)、标称电压(V)、续航里程(km)。缺任何一个,后面换算都容易出问题。

这里有个特别容易翻车的地方:同一款车,NEDC、WLTP、EPA三种测试标准测出来的续航差距很大。欧洲2020年前的车喜欢标NEDC,美国车标EPA,中方厂商不少拿CLTC。比如特斯拉Model 3长续航,EPA大约在570-600km,换WLTP能到629km,NEDC甚至敢标到668km。所以看到range_km字段时,必须同时确认test_std字段,否则别直接拿它建续航回归模型。你可以在后续处理里新建一列test_std,把“NEDC/WLTP/EPA/CLTC”四个值归一,缺掉的置NaN。

日产Leaf的电池规格字段尤其要小心。Leaf在官方文档里同时会出现电池包kWh值和电芯Ah值,第三方采集员经常把“40Ah电芯”直接当“40kWh电池包”写进去。所幸电池包标称电压通常在同一行里,标准的换算公式是kWh = 电压(V) × 电流(Ah) / 1000。比如一组标称360V、66Ah的电池包,算出来是23.76kWh,对应早期24kWh的Leaf,而不是66kWh。这个坑在二手欧洲车的规格表里最常见,单位不统一,一不注意就把整个分布的右尾拉歪。

宝马i3也是典型的演进样本。早期i3用60Ah电芯,电池包约22kWh;中期改款用94Ah,约33kWh;末期用120Ah,约42kWh。三款电池包外观尺寸几乎一样,但BMS软件版本、充电功率、可用容量都不一样。如果你只按“BMW i3”去合并销售表,必然出错。反过来,如果销售表里恰好有电池容量字段,你就可以反推它是哪一代电池,从而补全年款信息。这就是为什么我坚持把电池容量作为合并主键的一部分,而不是辅助信息。

2.2 销售数据字段还有一套口径:价格、地区、时间

销售数据比电池规格更脏。首先是价格:同一款Model Y,美国、欧洲、中国、东南亚的价格不一样,而且有些是含税指导价,有些是补贴后地价,有些是二手车成交价。要做跨市场对比,必须先归一化币种和税务口径。我一般新建price_usd、price_includes_tax两列,原始价格字段永远保留不动,这是血泪教训:改原始列会丢掉下游复盘的依据。

时间字段是另一个坑。2025年销售数据不是一个时间点,而是月度或季度记录。特斯拉每个季度会对长续航版做临时降价促销,宝马i系列经销商折扣甚至能到15%,如果按“2025年全年平均价”建模,实际是在用一个不存在的价格。正确做法是保留sale_year、sale_month两列,把价格当成时变特征,别把2025年的12个月合成一列全年均价。很多研究残值的模型会忽略这一点,结果就是对促销月的价格预测系统性偏高。

字段对齐策略上,我的经验是“品牌+车型+年款”只能做辅助键,真正的合并主键要带电池规格。原因很简单:Model 3在2021款之后同时卖过标准续航(约55-62kWh)和长续航(约75-82kWh),单看车型名,销售表里根本分不出来是哪个配置。把电池容量作为主键一部分之后,两条数据才能落到同一行。这里还藏着一个多对多问题:同一车型同一个容量,可能覆盖两个年款,所以我的merge_key通常是“品牌_车型_电池容量_年款”。

最后建议把字段字典固定下来,我倾向于在进模型之前把表格规范成19到20列。这个步骤不复杂但特别重要:字段没从一开始统一,后面每做一次合并就多一次脏数据。字段字典设计好之后,无论原始数据叫“Battery Capacity”还是“电池容量”,都映射到同一列,后续特征工程和模型验证才不用反复返工。

标准字段类型示例说明
brandstrtesla厂商统一小写
modelstrmodel_3车型归一化
yearint2024年款
battery_kwh_rawfloat82.0厂商标称总容量
battery_kwh_usablefloat79.0BMS可用容量
voltagefloat400标称平台电压
range_kmfloat629续航,必须配test_std
test_stdstrwltpwltp/epa/nedc/cltc
price_usdfloat42990归一化美元价格
price_typestrmsrpmsrp/dealer/used/after_incentive
sale_monthint2025032025年3月

3. 把3K+记录落进本地:数据整理与字段标准化的最小流程

3.1 先做数据勘察:不要跳过shape、dtypes和缺失值

拿到数据后第一件事不是读文件内容,而是跑一个标准的勘察脚本。很多从报表系统导出的CSV带BOM头,Excel打开过再存一次,编码和分隔符都会变化,所以read_csv要用utf-8-sig并显式声明分隔符。勘察脚本的核心是三件事:看形状、看字段类型、看缺失分布。

import pandas as pd import numpy as np df = pd.read_csv("ev_dataset_2025.csv", encoding="utf-8-sig") print("shape:", df.shape) print("columns:", df.columns.tolist()) print(df.dtypes) missing = df.isna().sum() print(missing[missing > 0]) print(df["brand"].value_counts(dropna=False))

这段代码里,df.shape告诉你记录数和字段数是否和标题描述一致,如果少了超过10%,先怀疑读取问题,再怀疑原始文件被截断。df.dtypes是找“数字被读成字符串”的最快路径,比如price列是object而不是float64,说明里面有逗号或货币符号。最后一行value_counts可以快速看品牌分布是否真的集中在特斯拉、宝马、日产三家。

参数说明:encoding="utf-8-sig"是为了吃掉UTF-8 BOM头,Windows下导出的CSV几乎都有;dropna=False能把缺失值单独显示成NaN一行。我习惯在前三步不drop任何行,纯粹摸清底数,避免在没看清分布的情况下误删数据。

3.2 电池规格字段统一成数值型kWh

电池规格往往是字符串,比如“82 kWh (usable 79 kWh)”“66 Ah 350V”“600 Wh”。一步到位做单位归一化的脚本长这样:

import re def parse_capacity(text): if pd.isna(text): return np.nan s = str(text).lower().replace(" ", "").replace(",", "") m = re.search(r"(\d+(?:\.\d+)?)\s*(kwh|wh|ah)", s) if not m: return np.nan val = float(m.group(1)) unit = m.group(2) if unit == "wh": return round(val / 1000, 2) if unit == "kwh": return round(val, 2) vm = re.search(r"(\d+(?:\.\d+)?)\s*v", s) if not vm: return np.nan return round(val * float(vm.group(1)) / 1000, 2) df["battery_kwh_parsed"] = df["battery_spec_raw"].apply(parse_capacity)

逻辑说明:先把字符串里的空格和逗号全部去掉,让“82 kWh”变成“82kwh”,再用正则优先匹配数字加单位。匹配到Wh就除以1000,匹配到Ah则必须在同一字符串里找到电压V,否则直接返回NaN。这个设计是故意的:Ah没电压就是不可换算的脏数据,猜一个值会让后面整个回归都不稳。

参数说明:正则中的“\d+(?:.\d+)?”同时覆盖了“82”和“82.1”两种写法;“\s*”是保险,虽然前面已经去过空格,但万一换行符没被replace掉也能兜住。这里有个易错点:字符串“79kwh”和“82kwh”可能同时出现在同一行里,regex默认只匹配第一处,所以我会在解析前先去截最短的数值段,或者直接用findall加min,避免把“usable 79”当成总容量。

提示:解析结果出来后,直接对比battery_kwh_parsed和原始列的分布。如果原始列有“总容量/可用容量”两种语义,务必拆成battery_kwh_raw和battery_kwh_usable两列,别混在一个字段里。

3.3 2025年销售时间与价格口径清洗

时间字段和价格字段是清洗的重头戏。sale_month如果以整数形式存在,比如“202501”,直接用pd.to_datetime解析;如果带中划线“2025-01”,就把format参数改掉。价格的脏数据通常是逗号、货币符号、空格混着来。

def clean_price(price_str): s = str(price_str).replace(",", "").replace("$", "").replace(" ", "").strip() try: return float(s) except ValueError: return np.nan df["sale_month_dt"] = pd.to_datetime(df["sale_month"].astype(str), format="%Y%m") df["price_usd"] = df["price"].apply(clean_price)

逻辑说明:clean_price把美元符号、千分位逗号、中间空格全部去掉后再转float,转不动说明原值可能是“约45万”这类非结构化文本,直接置NaN比猜一个数更安全。sale_month先转字符串再指定format="%Y%m",是为了兼容“202501”和“20251”这种Excel把前导零吞掉的情况。

参数说明:astype(str)是把整型时间列转字符串,pd.to_datetime遇到“20251”这种非法格式会报错,所以实际项目中我一般加errors="coerce",这样解析失败会变成NaT而不是中断流程。价格清洗后一定要再画一次describe(),如果price_usd的最小值小于1000,基本可以断定单位不是美元,而是人民币标价被误判成美元了。

4. 多源合并与特征工程:让3K+记录能喂给模型

4.1 合并主键:品牌、车型、电池容量三段式

现在数据集里有电池规格表、销售表,可能还有一份年款配置表。直接按品牌和车型合并一定会撞车,因为同一车型在不同年款有不同电池容量。我的做法是构造三段式merge_key。

def norm_key(x): s = str(x).lower() return re.sub(r"[^a-z0-9]+", "_", s).strip("_") df["brand_key"] = df["brand"].map(norm_key) df["model_key"] = df["model"].map(norm_key) df["pack_key"] = df["battery_kwh_parsed"].round(1) df["merge_key"] = df["brand_key"] + "__" + df["model_key"] + "__" + df["pack_key"].astype(str)

逻辑说明:norm_key先把“BMW”转成“bmw”,把“Model 3”转成“model_3”,统一大小写和分隔符。pack_key把容量四舍五入到一位小数,兼容“82.0”和“82”因为数据结构不同导致的微小误差。

参数说明:round(1)并不是万能药,如果两份数据一份用总容量、一份用可用容量,四舍五入也救不回来,必须先靠battery_kwh_raw/usable两列统一到同一种语义再做round。还有一个细节:nm口品牌的“i3”会保留数字,不会有问题;“M3”在特斯拉语境里是Model 3,但在宝马语境是M3,所以merge_key前面加了brand_key,用品牌做第一段隔离。

合并时我还会检查merge_key的重复率。正常情况下每个key对应一条记录,如果某个key对应多条,大概率是两个年款共用同一组电池规格,那就必须把year加进merge_key。价差模型里,年款这个变量太重要了,不能省。

4.2 构造可用特征:能耗、充电倍率、单位容量价格

合并完字段后,数据集已经具备进模型的基本形状。但原始字段不适合直接喂给线性模型,因为续航、容量、价格之间的量纲差异太大。我会先构造三个解释性强的特征。

df["energy_eff"] = df["range_km"] / df["battery_kwh_parsed"] # km/kWh df["c_rate"] = df["fast_charge_kw"] / df["battery_kwh_parsed"] # 近似充电倍率 df["price_per_kwh"] = df["price_usd"] / df["battery_kwh_parsed"]

逻辑说明:energy_eff是每度电能跑多少公里,数值越大越省电,国产车通常在6-8km/kWh,老款Leaf能到5左右,高性能车反而可能低于4。c_rate是最大快充功率除以容量,800V平台车型通常能到2C以上,400V车型多在1-2C之间,这个特征可以帮你快速识别平台电压等级。price_per_kwh是把价格除以容量,得到“每度电多少钱”,用来横向比较不同容量的定价合理性,这在残值分析里是个很敏感的指标。

参数说明:如果原始数据没有fast_charge_kw字段,c_rate这一列就直接不构造,不要用快充功率缺失的行硬算。另外,价格在不同地区差异很大,price_per_kwh最好分地区、分品牌来看,不然特斯拉和日产的对比会被汇率和税费政策带偏。

做完特征之后,我建议再做一轮相关性检查。把energy_eff、c_rate、price_per_kwh、price_usd、battery_kwh_parsed放进corr(),如果price_per_kwh和battery_kwh_parsed的相关系数超过0.9,说明价格基本由容量决定,这个数据集很适合做回归;如果相关系数接近0,则说明市场定价被品牌和年份主导,容量只是辅助变量。3K+条记录做相关性检查是秒出的,完全跑得动。

4.3 切分训练验证:按时间切,别随机切

销售数据天然带时间属性,做价格预测时绝对不能把2025年全年混在一起随机切。我见过太多次这种翻车:训练集里包含了月底降价记录,验证集里出现了月初记录,模型指标好得反常,一上线就崩。正确做法是排序后按月份切。

from sklearn.model_selection import train_test_split df = df.sort_values("sale_month_dt") valid = df[df["sale_month_dt"] >= "2025-10-01"] train = df[df["sale_month_dt"] < "2025-10-01"] X_train = pd.get_dummies(train[["brand", "model", "battery_kwh_parsed"]], columns=["brand", "model"]) y_train = train["price_usd"] X_valid = pd.get_dummies(valid[["brand", "model", "battery_kwh_parsed"]], columns=["brand", "model"]) X_valid = X_valid.reindex(columns=X_train.columns, fill_value=0)

逻辑说明:先把数据按时间升序排好,取2025年10月之后做验证集,之前做训练集。get_dummies把品牌和车型展开成哑变量,这样特斯拉、宝马、日产的品牌效应会被模型显式学习。X_valid reindex到X_train的列集,是为了防止验证集里出现训练集没有见到的新车型导致报错。

参数说明:10月这个切分点你可以按数据量调整,一般是最后2-3个月做验证。stratify只在随机切分时有意义,时间切分没有必要加。如果你要预测的是“未来价格”,那么训练集里绝不能出现晚于验证集日期的任何记录,哪怕那些记录价格更“正确”。时序泄漏是所有销售数据建模里最隐蔽的问题,四舍五入的merge_key坑是数据质量,时间顺序坑是方法论,两个都要单独检查。

5. 实名避坑:整理这类电动车数据集最容易翻车的五个细节

5.1 电池容量单位混用

现象:battery_spec_raw里同时出现“66 Ah”“58 kWh”“600 Wh”,一个字段三项三种单位。

原因:数据源混合了电芯参数、电池包参数和第三方二手网站的自定义字段。欧洲二手车网站偏爱Ah,国内网站几乎清一色kWh,抓取合并时没人统一。

解决:先按3.2里的parse_capacity统一到kWh。Ah必须带电压换算,如果同一行里找不到电压字段,直接置NaN,不要用同车型中位数去填Ah类缺失。单位混用的数据一旦混进训练集,容量特征会平白多出几倍的噪声。

5.2 总容量与可用容量被当成一列

现象:同一车型两个来源,一个标82kWh,一个标79kWh,两个都对。

原因:厂商对外宣传往往标电池包总容量,BMS系统实际可用的部分会锁掉3-5kWh。不同数据源一个抓官网总容量,一个抓车主App里的可用容量。

解决:先建battery_kwh_raw和battery_kwh_usable两列。做残值分析和续航预测全用usable,因为价格和续航是由用户可用的电量决定的;做充电时间粗算和能耗标称对比才用raw。建模前在数据字典里写明你用哪一列,否则模型系数没有可解释性。

5.3 2025年销售价格口径不一致

现象:同样一台Model Y,一条记录42990美元,另一条折合下来38900美元。

原因:前者是含税落地价,后者是不含税裸车指导价;更隐蔽的是有人把“补贴后价格”混了进来,导致同配置同时间出现两个相差悬殊的价位。

解决:增加price_type列,值域设为msrp/dealer/used/after_incentive,并在建模时只保留其中一种。做新车价格分析用msrp,做二手车残值用used,千万不要混着训练。这个过程靠脚本检查可能发现不了,得直接看同一merge_key下的价格极差,大于15%就要手工核对。

5.4 电池规格缺失率过高

现象:3K+条记录里20%的电池容量为空,直接drop后,长续航、高配车型被删掉一大半,样本分布完全偏移。

原因:老款Leaf和宝马i3早期发布时官方参数不完整,第三方数据源不采集电池规格;越老的车型缺失越严重。

解决:先用官方配置表做一次补全,剩下的按brand+model+year分组取中位数插补,同时加is_imputed标记列。中位数计算时要先剔除本组内的NaN行,如果一组全是NaN,就别硬补,直接保留缺失并交给树模型处理。

5.5 时序泄漏

现象:价格模型在验证集上R²高得离谱,接近0.95,一放到真实业务里预测就整体偏移。

原因:把2025年全年数据混在一起后随机切分,训练集包含了月底的降价记录,而验证集用了月初的价格,模型等于提前看到了“未来”。

解决:永远按sale_month排序后切分,验证集在时间上严格晚于训练集。对销售数据做随机切分的唯一后果就是指标失真,没有例外。代码层面可以加一个断言:assert valid["sale_month_dt"].min() > train["sale_month_dt"].max(),把这条写进流程,谁改切分逻辑都会立刻被发现。

6. 基线验证:用价格-容量回归检验这批数据值不值得投入

数据整理完,先不要急着上XGBoost或深度学习。我习惯先跑一个价格对容量的简单线性回归,这一步能花两分钟告诉你数据质量到底行不行。

import statsmodels.api as sm sub = df.dropna(subset=["price_usd", "battery_kwh_parsed"]).copy() X = sm.add_constant(sub["battery_kwh_parsed"]) y = sub["price_usd"] ols = sm.OLS(y, X).fit() print(ols.summary().tables[1])

逻辑说明:battery_kwh_parsed的系数应当显著为正,p值远小于0.05。如果系数为负,基本可以断定前面某个环节出了问题,最可能是容量单位混用或价格口径不一致。R²在0.2到0.5之间都算正常,因为价格还被品牌、年款、地区强烈影响,单靠容量解释不了全部。接下来你可以把brand、range_km加进特征再看R²,如果从0.3涨到0.7,说明数据集的结构是健康的,品牌效应被模型成功捕捉。

这一版的教训我记忆很深。以前拿到一批“2024纯电车型数据”,没有做单位统一,直接喂回归,容量系数居然为负。排查了两个小时,发现约两成容量字段是Ah没换算,约三成价格混了汇率。那种翻车最伤的不是数据本身,而是时间:你永远不知道是代码错了还是市场疯了。从那以后,无论数据多急,我都先跑一遍这个基线回归,R²异常低就直接拦下整个流程,不带着脏数据往下走。3K+条记录跑这个检查完全够用,算下来也就一眨眼的功夫。希望这套整理流程能帮你在自己的数据集上少踩同样的坑。

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

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

VC6.0 MFC计算器开发全攻略:从对话框到消息映射的完整实践

简介&#xff1a;面向VC6.0与MFC初学者的完整计算器程序源码包&#xff0c;基于MFC对话框类实现基础四则运算&#xff0c;适合正在学习Windows窗口程序设计、进行课后实践或希望掌握MFC类库应用的开发者参考。压缩包内共有31个文件&#xff0c;涵盖C源文件与头文件、资源脚本、…

作者头像 李华
网站建设 2026/10/8 4:56:33

千人集团10家主体财务智能体落地实践:六大流程Agent拆解与效率提升

1. 千人集团十家主体的财务困局&#xff1a;为什么必须上智能体一家1000人规模、旗下有10家独立法人主体的集团&#xff0c;财务团队通常维持在25到40人之间。这个体量听起来不算小&#xff0c;但真正做过集团财务的人都知道&#xff0c;人再多也架不住主体多、流程碎、口径乱。…

作者头像 李华
网站建设 2026/10/8 4:56:03

WorkBuddy 六大跨行业实战:MCP 接入飞书多维表格与科研数据清洗

1. 从六个真实场景看 WorkBuddy 的落地逻辑第一次听到 WorkBuddy 这个名字&#xff0c;很多人会下意识把它归类成"又一个 AI 聊天工具"。但真正把它用起来的人会发现&#xff0c;它更像是一个能挂载各种能力、能接入不同数据源、能替你把重复劳动吃掉的工作台。我接触…

作者头像 李华
网站建设 2026/10/8 4:55:48

Agent Skills实战:从技能封装到调度机制,打造稳定可靠的AI Agent

1. Agent为什需要“Skills”&#xff0c;而不是一堆零散的工具函数这两年“Agent”这个词快被说烂了&#xff0c;但真正跑过生产环境的人心里都清楚&#xff1a;一个Agent能不能干活&#xff0c;很多时候不取决于模型有多聪明&#xff0c;而取决于它手里有没有一套沉淀好的方法…

作者头像 李华
网站建设 2026/10/8 4:55:26

AWD攻防赛脚本集合:从批量提交到应急恢复的自动化实战指南

简介&#xff1a;面向AWD/CTF网络安全竞赛的攻防脚本合集&#xff0c;专门为参赛者、安全爱好者和蓝红队人员提供赛场上所需的工具支持&#xff0c;覆盖信息收集、漏洞扫描、渗透测试、Web漏洞检测、日志分析与防御加固等常见环节&#xff0c;帮助快速定位对手弱点并建立自身防…

作者头像 李华
网站建设 2026/10/8 4:54:54

RK3588 GPU开源方案:panthor内核驱动与Mesa编译落地指南

简介&#xff1a;针对RK3588平台的开源GPU驱动与mesa库整合资源&#xff0c;以panthor驱动为核心&#xff0c;并配套用户态mesa图形库&#xff0c;已在Ubuntu 22.04和内核6.1.75环境实测通过。面向需要为Mali-G610启用开源图形能力的嵌入式Linux开发者、驱动移植工程师及图形栈…

作者头像 李华