
简介针对2024年全电动汽车保有量数据的可视化分析案例这份资源面向数据分析初学者与新能源汽车行业观察者提供从数据清洗、分类汇总、统计探索到图表呈现的完整参考流程。压缩包共3个文件约3.8MB内含1个CSV原始数据集、1个Jupyter Notebook分析脚本与1个HTML可视化页面可帮助读者直接查看电动汽车品牌、型号、续航里程、充电次数等维度的分布特征。目前已有196人学习下载。通过学习Notebook中逐步编写的Pandas处理代码和可视化逻辑读者能掌握真实数据集的预处理技巧、缺失值与异常值处理方法、相关性分析思路以及如何使用Matplotlib或ECharts制作直观图表HTML页面则适合作为分析报告的交互展示模板便于快速共享与演示。整体内容紧贴环保与新能源热点既能练习数据分析技能也能为市场趋势研究提供数据参考适合实战练习与作品集积累。1. 2024 年全电动汽车数据集先跑通「清洗 → 可视化 → 结论」的最小闭环做数据分析的人手里通常不缺数据缺的是一个可视化分析练手场景字段别太脏、量级适中、能一口气跑通全流程。2024 年全电动汽车数据集正好是这一类资源——它把品牌、车型、电池容量、续航、价格这些直接可对比的字段收进一张横截面表配合附带的 Python 脚本能完整演示 pandas 清洗、matplotlib 制图、分组聚合这三个最高频的数据可视化动作。新手靠它能理解“数据要洗到什么程度才能画图”有经验的从业者也能把它当成验证统计口径的素材库。这份资源适合刚学数据分析的入门者也需要做周报分析的中级工程师。它给的不是一个孤立表格而是一条从 CSV 读到结论的可复跑流水线。下面按拆资源的方式把每个环节和踩过的坑逐步过一遍。2. 数据准备与清洗为什么先看字段类型再决定画图数据集下载回来后第一步不是急着画图而是先回答三个问题表里有哪些字段、每个字段的类型对不对、空值落在哪里。这三件事没确认后面所有图都可能指向错误结论。常见做法是先跑一遍 info() 看结构再查缺失值最后才开始清洗。下面的顺序是我处理这类横截面数据时固定使用的照着走能省掉大半返工。2.1 先读表头再动手字段结构核对第一步先把数据读进来看形状、字段名和类型import pandas as pd # 读入后先看结构再决定清洗策略 df pd.read_csv(ev_2024.csv, encodingutf-8) print(df.shape) print(df.columns.tolist()) print(df.dtypes)这段代码做三件事shape 返回的是行列数让你对数据量有个预期columns.tolist() 把所有字段名以列表形式打印出来方便和后续代码里的列名逐一对齐dtypes 显示每一列被 pandas 读成了什么类型。这里最关键的是确认数字列是否被读成 object——我见过太多次“价格列是字符串”导致排序报错的情况根源就是 CSV 里混入了$、逗号或空串。如果读取时直接抛 UnicodeDecodeError通常不是文件坏了而是编码不匹配。CSV 用 gbk 保存时encodingutf-8会报错改成encodinggbk或encodingutf-8-sig重试即可。utf-8-sig适合带 BOM 的 Excel 导出文件它能自动吃掉开头那几个不可见字符。以我接触过的 2024 年全电动汽车数据集版本为例核心字段基本逃不出下面这几类品牌、车型、年份、车身形式、驱动形式、电池容量kWh、续航里程英里、建议零售价美元、是否支持全美销售。年份是车型年而不是购买年续航来自 EPA 官方标称值价格是厂商建议零售价而非市场成交价——这三个口径差异决定了后续结论的边界。不同版本字段名会有些出入下表是通用的字段对照字段类别字段名示例分析用途常见脏数据标识类brand / model分组对比、品牌榜大小写不一致、首尾空格规格类battery_kwh / range_mi电池-续航相关性数值带单位、千分位逗号价格类price价格区间分层含货币符号、空值、混入其它币种状态类available_50_states可用性过滤yes/no 与 True/False 混用2.2 清洗三步类型、空值、单位统一字段确认后进入清洗。我的固定动作是先统一数字列的格式再处理空值最后统一品牌的大小写和空格。df[range_mi] pd.to_numeric(df[range_mi], errorscoerce) # 先把价格列里的美元符号和千分位逗号剥掉再转数值 df[price] ( df[price].astype(str) .str.replace(,, , regexFalse) .str.replace($, , regexFalse) ) df[price] pd.to_numeric(df[price], errorscoerce) # 仅按分析必需的三列过滤空值不整行全删 df df.dropna(subset[battery_kwh, range_mi, price]) # 统一品牌写法避免 groupby 时同一品牌被拆成两组 df[brand] df[brand].str.strip().str.title()第一行pd.to_numeric 把能转成数字的字符串转成数值errorscoerce的意思是转不动的值变成 NaN而不是直接抛异常。这样做的价值在于保留“脏数据位置”线索之后可以用df[df[range_mi].isna()]反查具体是哪几行出了问题。price 列的处理要单独说CSV 里的价格经常写成$45,000这种带货币符号和千分位逗号的格式直接 to_numeric 会全列失败。先把整列 cast 成 str用 replace 去掉逗号和$再转数字。regexFalse表示按字面量替换不启用正则——这里也可以防住某些价格字段里含正则特殊字符的情况。第二步再对 price 调一次 to_numeric覆盖“符号去掉后仍有空白字符”的情况。dropna 的subset参数只针对分析必需的三列过滤而不是把整张表含缺失值的行都删掉。比如“是否全美可售”这一列缺失不影响续航分析就不该因为它的空值丢弃整行数据。brand 的str.strip()去掉首尾空格.str.title()把tesla统一成Tesla——这一步不做groupby 时同一个品牌可能被拆成两个组。2.3 清洗后的复查describe 与缺失值清单清洗不是做完就算还要复核。我一般用两句代码完成检查# 复查清洗结果范围与缺失值 print(df[[battery_kwh, range_mi, price]].describe()) print(df.isna().sum())describe 输出均值、标准差、min、25%/50%/75% 分位和 max。这一步主要看两件事一是 min 和 max 是否在合理区间如果价格最小值是 1000 或最大值是 900000说明还有脏行漏网二是均值是否明显大于 50% 分位如果均值被高端车型拉高很多后续分组对比时要留意长尾影响。isna().sum() 打印每列剩余缺失值数量确保关键列已经清零。这里想多说一句「项目级清洗」和「报表级清洗」的差别。这份资源附带代码最好的地方是清洗逻辑集中放在脚本开头而不是每画一张图前重复 dropna 一次。集中清洗的好处是复跑时口径一致同一个“平均续航”在两张图里如果分别采用了不同的空值策略得到的数字可能不一样这就是报表级的坑。我一般把清洗代码单独放一个 cell 或脚本用注释分好区后续所有分析都基于同一个清洗后的 df。3. 单变量可视化从续航分布中读出的市场分层数据洗完后第一张值得画的图是续航分布。2024 年纯电市场并不是均匀的一边是低价短续航通勤小车一边是七八百英里续航的旗舰轿车和皮卡。一张直方图能读出的信息往往比后续复杂的二维图更直接。3.1 用 pandas matplotlib 看续航分布import matplotlib.pyplot as plt plt.rcParams[figure.figsize] (10, 6) # 续航分布直方图bins30 是适配几十款车型的经验值 df[range_mi].hist(bins30, edgecolorblack) plt.xlabel(Range (mile)) plt.ylabel(Count) plt.title(EV Range Distribution - 2024) plt.grid(alpha0.3) plt.show()写法说明pandas 的 Series.hist() 是对 matplotlib 的封装bins30控制分箱数量edgecolorblack给每个柱描边避免相邻柱体糊成一片。设置 figure.figsize 是因为默认尺寸在小屏幕上容易压扁grid(alpha0.3)让网格线淡一些不抢柱体视觉权重。读图时重点看形状。如果分布出现明显的右偏或长尾说明市场不是单峰结构——少数高续航车型把右端拉长主力车型集中在中间某个区间。如果 bins 取得太小比如 5双峰会被抹成单峰取到 100 又会因为某些区间没有车型而出现锯齿。对于几十款车型量级的数据30 是个比较稳的经验值。想进一步确认分布形状可以用sns.histplot(df[range_mi], kdeTrue)叠加核密度曲线曲线比直方图更平滑地反映概率密度走势。3.2 价格区间划分pd.cut 的分箱与边界续航分布只能看市场结构价格分层则直接影响“这车卖给谁”的判断。把连续价格转成离散档位最常用的是 pd.cut# 按业务口径切价格段labels 数量 bins 数量 - 1 bins [0, 30000, 45000, 60000, 120000] labels [economy, mid_range, premium, luxury] df[price_seg] pd.cut( df[price], binsbins, labelslabels, ) print(df[price_seg].value_counts().sort_index())pd.cut 按给定的 bins 把价格切成四段3 万美元以下经济型3 到 4.5 万中档4.5 到 6 万高端6 万以上豪华。这里必须强调分箱边界是业务口径不是统计最优解。你也可以用 qcut 按四分位切让每一档数量尽量均衡但那样分出来的边界往往没有解释意义——比如某次切分的“luxury”档可能包含一辆 3.5 万的小车结论就变得奇怪。参数上有两个容易翻车的点第一labels 的数量必须等于 bins 数量减 1多写或少写一个都会 ValueError第二pd.cut 默认区间是左开右闭即第一个区间的实际范围是 (0, 30000]价格正好等于 30000 的车型会落入第二档。如果你希望 30000 算作经济型上限需要传rightFalse并用include_lowestTrue保证极小值能进第一箱。value_counts 按价格段统计车型数量后.sort_index()的作用是让输出顺序与 bins 一致否则默认按计数从大到小排读起来很乱。3.3 子图对比一张画布看两条线索单变量的直方图和价格分箱都看完了把两者放到同一张画布上对比会发现一个有意思的错位fig, axes plt.subplots(1, 2, figsize(12, 5)) # 左右子图并排看电池-续航关系与价格段结构 df.plot.scatter( xbattery_kwh, yrange_mi, axaxes[0], alpha0.5, titleBattery vs Range, ) df[price_seg].value_counts().sort_index().plot.bar( axaxes[1], titlePrice Segment Count, ) plt.tight_layout() plt.show()左边散点把电池容量和续航的关系画出来右边柱状图呈现价格分层。两个图并排时最容易看到的结论是高端价格段的车型数量往往不多但电池容量普遍更大。散点上的alpha0.5是半透明参数数据点重叠时能看出密度分布而不是糊成一团。tight_layout()自动调整子图间距避免两个标题互相遮挡。到这里只用了单变量视角品牌和车身形式还没有参与进来。续航、电池、价格三者之间的真正关系需要进入分组和交叉分析才能讲清楚这也是下一章的内容。4. 多变量对比品牌、电池容量与效率的交叉分析单变量回答“市场长什么样”多变量回答“具体是谁在什么价位、靠什么打”。这一章的三个小工具——groupby、corr、pivot_table——几乎是这份资源里代码的核心部分。4.1 品牌聚合groupby agg 的统计口径品牌对比最常见的起点是按品牌聚合续航和电池均值# 按品牌聚合用 agg 同时计算均值与样本量 brand_stats ( df.groupby(brand) .agg( avg_range(range_mi, mean), avg_battery(battery_kwh, mean), model_count(model, count), ) .sort_values(avg_range, ascendingFalse) .head(10) ) print(brand_stats)groupby 按 brand 分组后agg 用命名元组的方式同时算三个指标avg_range 是续航均值avg_battery 是电池均值model_count 统计该品牌在售车型数量。第三列非常重要——如果某个品牌只有一款车型且恰好是旗舰它的 avg_range 会虚高看过 model_count 就不会被误导。我一般会在聚合前加一道过滤把只有一两款车型的品牌踢掉再对比# 只保留车型数不少于 3 款的品牌避免单款旗舰车拉高均值 df df.groupby(brand).filter(lambda g: len(g) 3)groupby().filter() 的 lambda 接收每个分组len(g) 3表示保留车型数不少于 3 的品牌返回过滤后的完整 DataFrame。这样对比的组内样本量至少有 3均值稳定性好一些。此处还有一个隐藏知识点groupby 默认把分组列放进 index如果想保留 brand 作为普通列继续参与后面的 merge要用groupby(brand, as_indexFalse)或者在聚合后手动reset_index()。4.2 相关性热力图验证电池与续航的假设纯电车型的一个通用认知是“电池越大续航越远”。用相关系数验证这个前提是否成立比直接看图更严谨# 皮尔逊相关系数低值时先查单位混用 corr df[[battery_kwh, range_mi, price]].corr() print(corr)corr() 默认计算皮尔逊相关系数。电池容量与续航的相关系数通常在 0.9 左右说明强线性正相关价格与续航的相关性会弱一些通常在 0.5 到 0.7因为价格还受品牌溢价和车身形式影响。把相关系数矩阵打印出来后可以再用热力图增强可读性import seaborn as sns import matplotlib.pyplot as plt sns.heatmap(corr, annotTrue, cmapcoolwarm, fmt.2f) plt.title(Correlation Matrix) plt.show()heatmap 的annotTrue在格子里标出数值fmt.2f控制显示两位小数cmapcoolwarm用冷暖色区分正负相关。这里要给一条判错经验如果电池容量和续航的相关系数明显低于 0.8先别急着怀疑车型技术水平回头查清洗——最常见原因是续航字段里混入了公里和英里两种单位单位不一致会直接稀释相关性。换句话说相关系数异常低时先假设是数据问题再认为是业务问题。4.3 交叉表与效率指标车身形式 × 价格段多变量的另一个常用工具是交叉表看车身形式和价格段的组合关系# index 是行维度columns 是列维度values 做计数 table pd.pivot_table( df, indexbody_style, columnsprice_seg, valuesmodel, aggfunccount, fill_value0, ) print(table)pivot_table 的 index 是行维度、columns 是列维度这里用车身形式作行、价格段作列values 选 model 并对它计数得到的是“SUV 中有几款价格在 premium 段”这类交叉统计。fill_value0把没有组合的空位补成 0避免表格里出现一堆 NaN。aggfunc 换成 mean 时交叉表会变成每个组合的价格均值但那时最好不要用 fill_value 补零用dropnaFalse保留空位更直观。交叉表能看出结构性差异小巧廉价车集中在 economy 段中大型 SUV 大量落在 mid_range 到 premium豪华段几乎被轿车和大型 SUV 瓜分。如果想进一步比较“谁的技术效率更高”可以构造一个每度电续航指标# 每度电跑多少英里nlargest 直接取前五 df[efficiency_mi_per_kwh] df[range_mi] / df[battery_kwh] print( df.nlargest(5, efficiency_mi_per_kwh)[ [brand, model, efficiency_mi_per_kwh] ] )这个指标表示每度电跑多少英里本质是电池技术的横截面效率对比。nlargest(5, efficiency_mi_per_kwh)直接取前五。注意效率指标不能跨级别硬比一台全尺寸皮卡受制于重量和风阻效率必然低于紧凑型轿车所以高效榜常年被小车占据这是分析的边界而非数据错误。5. 可视化分析常见问题与排查清单四条血泪坑复跑这份资源和类似的 CSV 分析项目时最容易栽的坑集中在两个环节数据读入和图表输出。下面四条是我实际踩过的按「现象 → 原因 → 解决」记下来每条都能直接对号入座。5.1 类型与索引陷阱第一条排序时直接报错价格列是字符串。现象对 df.sort_values(price) 时报错提示TypeError: not supported between instances of str and float。原因CSV 里价格列带了$和千分位逗号pandas 只能按字符串读出整列变成 object 类型如果列内还有空白字符排序时 str 与 float 不能比较直接抛出类型错误。解决按第二章的固定顺序处理astype(str)后 replace 掉符号再pd.to_numeric(errorscoerce)最后dropna(subset[price])。处理完记得打印 dtypes 确认这一列已经是 float。第二条groupby 聚合后和原表 merge 错位。现象聚合结果 brand_stats 与原始 df 合并时行数对不上或者合并后数据错乱某些品牌的值被串到别的行。原因groupby 默认把分组列作为索引保留如果直接 merge 而没有 reset_indexpandas 会按索引对齐而不是按列值对齐两边的索引顺序不一致时结果就乱了。解决聚合前用groupby(brand, as_indexFalse)或者聚合后立即reset_index()把 brand 还原成普通列。merge 之后再用df.merge(brand_stats, onbrand, validatem:1)这样的写法强制校验关系任何一对多的错位会在合并时报出来。5.2 输出与显示问题第三条中文标题和图例显示成方块。现象axis 标签和 title 里的中文全部渲染成豆腐块英文数字正常。原因matplotlib 默认字体是 DejaVu Sans不含中文字形Windows 和 macOS 的中文字体名也不一致直接写 SimHei 在 macOS 上会失效。解决在绘图代码前加一行plt.rcParams[font.sans-serif] [SimHei, Arial Unicode MS]把字体回退列表给全同时设plt.rcParams[axes.unicode_minus] False否则负号会变成方块。更省事的做法是图表标签统一用英文这份资源里很多示例图采用的就是英文标签既避免字体依赖也方便直接贴进英文汇报材料。第四条直方图 X 轴范围异常柱状图出现断崖。现象hist 画出来后横轴从 1000 到 900000某些区间没有任何柱体整体看起来像断成几段。原因价格或续航列里混入了数量级错误的脏行比如某一行把价格写成了 890000单位或币种混入或者续航单位英里与公里混杂。它们拉高了 max 值把正常区间压缩到图中很窄的一侧。解决先跑df.describe()看 max 和 75% 分位的倍数关系是否异常再对极端值做过滤价格用df df[df[price].between(10000, 150000)]续航用between(50, 700)。过滤后重新画图分布就会恢复正常。需要注意的是每做一次过滤都会改变样本量报告里要注明过滤阈值否则结论不可复现。6. 把静态图升级成交互分析plotly 转换与复用模板静态图适合写报告但做需求分析或给业务方确认结论时交互图往往更能让人自己上手探索。把上面所有 matplotlib 图转成交互式不需要重写整套逻辑用 plotly.express 一行就能替代散点图import plotly.express as px # hover 展示更多字段fig.show() 本地即可预览 fig px.scatter( df, xbattery_kwh, yrange_mi, colorbrand, sizeprice, hover_data[model, body_style, price_seg], titleEV Range vs Battery - Interactive View, ) fig.show()plotly.express 的 scatter 把 color 设为品牌、size 设为价格数据点会自动按品牌着色并按价格大小缩放hover_data指定鼠标悬停时额外展示的字段业务人员不用看代码就能自己查看每款车的车型、车身形式和价格段。fig.show()在本地会打开浏览器渲染也可以fig.write_html(ev_analysis.html)导出成单文件发给同事对方不需要装 Python 就能看。除了交互化我更习惯把这份资源沉淀成自己的复用模板。所谓模板不是把代码复制粘贴而是固定一套启动流程拿到任何 CSV先df.info()看类型再df.describe()看范围接着df.isna().sum()看缺失最后才是按业务口径做过滤和分箱。这三步写成一个函数或 notebook 头部以后换数据集只改文件名和字段映射其余不用动。当初我第一次跑这份资源时就是跳过了 brand 的 strip 清洗结果图里特斯拉被拆成两组柱状图看起来像灵异事件。从那以后我每次拿到新数据集都强制走一遍类型、空值、单位统一这三道工序再进入可视化环节。这份资源很适合作为你的第一套练手数据把这一套流程跑顺之后再遇到更乱的数据至少知道自己该往哪里查。希望帮到你。本文还有配套的精品资源点击获取