2024电动汽车数据集可视化分析:从清洗到交互看板的实战指南
发布时间:2026/10/2 9:52:31来源:尧图网络
简介2024年全电动汽车数据集可视化分析案例包含原始数据与配套代码面向数据分析初学者及新能源汽车行业观察者可帮助理解电动汽车市场规模、用户偏好与充电设施分布等维度。压缩包共3个文件含csv格式的Electric Vehicle Population Data、用于逐步执行数据清洗与统计分析的ipynb代码以及用于网页端展示图表结论的html文件整体大小仅3.8MB轻量便于下载与快速启动。目前已有196人学习案例覆盖品牌销量对比、不同区域接受度、续航与充电次数关系等分析主题。通过运行代码读者不仅能直接复现从数据清洗到相关性分析再到可视化呈现的完整流程掌握pandas、matplotlib等常用库的实操方法还能基于已有分析框架替换自己的数据灵活调整图表参数从而拓展到其他行业数据项目中兼具学习与二次开发价值。1. 2024 年全电动汽车数据集可视化分析值得花一个周末跑完的理由「2024 年全电动汽车数据集可视化分析」这个包拿到手第一眼就是一份数据集加一段代码看起来平平无奇。真正跑起来才发现这组数据把车型、续航、价格、电池容量、充电规格这些平时分散在不同渠道的字段收进了同一张表而且刻意保留了缺失值、单位混用和重复记录。想从里面挖出「2024 年热门车型价格分布」「长续航和电池容量是不是强相关」这类结论正好需要完整过一遍数据清洗、聚合、Matplotlib 出图、Plotly 交互、成果交付的链路。下面就把这套数据的处理方法、值得调的参数和几个容易翻车的地方写清楚适合刚接触数据分析项目的人照着敲也适合有经验的读者直接跳过代码看参数和决策边界。2. 拆开 2024 全电动汽车数据集字段口径、单位混用和清洗三座大山2.1 先看字段清单哪些列是「事实」哪些列是「口径」第一步不是画图而是先把文件读进 pandas确认行数、列数和每列的数据类型。这个动作花不了三十秒但能提前暴露七成以上的数据问题。import pandas as pd df pd.read_csv(electric_vehicles_2024.csv) print(df.shape) # 行数、列数 print(df.dtypes) # 每列类型重点看有没有 object 冒充数值 print(df.head()) # 前几行用于肉眼核对单位与格式读进来之后你会发现一个典型现象价格、整备质量这些列被正确识别成 int64 或 float64但续航、电池容量这类「数值加单位」的列大概率变成 object。别急着转换先按字段角色分个类我一般分成四组字段分组典型字段作图时的作用标识类Make, Model, Year, Trim分组、筛选、图例价格类MSRP, Price条形图、趋势图、散点图纵轴性能类Range, Battery_Capacity, Charge_Time直方图、散点图横轴、相关性分析物理类Mass_kg, Motor_Power, Seats双轴图、箱线图的辅助维度这样做的好处是后面每画一张图都能快速回答「这张图的横轴是哪个字段、单位是什么、要不要参与清洗」。很多人拿到数据集直接 df.describe() 然后就开始画图结果画到一半发现续航单位有英里也有公里返工成本反而更高。2.2 清洗第一关单位混用是电动汽车数据集里最常见的脏数据同样是续航里程北美车型习惯用英里欧洲和国内车型用公里同样是电池容量有的记录写 kWh有的写 Ah 甚至 Wh。可视化分析本身不做单位换算的话直方图会出现两个离得很远的峰相关性分析也会被整体拉偏。# 统一续航单位为公里原列单位若是英里就乘 1.60934 df[Range_km] df[Range] * 1.60934 # 统一电池容量单位为 kWh原列单位若是 Wh 就先除 1000 df[Battery_kWh] df[Battery_Capacity] / 1000.0 # 缺失值处理按品牌组内中位数填充并单独留一列记录填充状态 df[Range_km] df.groupby(Make)[Range_km].transform( lambda x: x.fillna(x.median()) ) df[Range_km_filled] df[Range_km].isna()逻辑说明groupby 加 transform 是「组内填充」的标准写法它会按品牌分别计算中位数并回填到每一行而不是拿全局中位数硬套所有品牌。这样保住了品牌之间的差异比如高端品牌续航普遍偏高用全局中位数会把它的缺失值填低。参数说明英里转公里的系数固定为 1.60934Wh 转 kWh 是除以 1000。至于要不要保留「是否填充」这一列取决于后面做什么——如果只是画分布图可以不要如果后续要跑回归模型建议保留方便做敏感性分析。这里有个容易忽略的选择为什么用中位数而不是均值因为续航和价格都呈右偏分布均值会被少数长续航车型拉高组内中位数更接近「大多数车的水平」。如果某品牌所有车型的续航都缺失组内中位数也是空值此时再对剩余 NaN 做一次全局填充或直接丢弃都可以数据集里这类记录占比通常很小。2.3 重复记录和「价格等于零」的异常清洗下手要狠除了单位混用这份数据集里还有两类「脏」重复记录和占位型异常值。同一个车型出现在不同批次、不同来源的抓取里这是多源数据集合并后的通病而价格为 0 或负数的记录基本可以断定不是真实售价而是缺失值的占位写法。如果不处理后面算品牌均价时这些 0 会把均价显著拉低。# 去重先按价格排序再按关键列组合去重保留非零价格那条记录 df df.sort_values(Price).drop_duplicates( subset[Make, Model, Trim], keeplast ) # 去掉价格为 0 或负数的记录 df df[df[Price] 0] # 去掉续航为 0 的记录真实续航为 0 的产品不存在 df df[df[Range_km] 0]参数说明sort_values(Price) 的目的是让价格高的记录排在后面再用 keeplast 保留它们如果你希望保留价格最低的那条就把 keep 改成 first。subset 字段按实际数据集调整一般至少用 Make Model Year三者完全相同才判定为重复。执行完这两步行数通常会减少 3% 到 8%这个规模在可视化分析里完全可以接受。别舍不得删把占位型 0 和重复记录留在图里最后的均价趋势和续航分布都会失真到那时再排查就麻烦了。清洗完可以用 df.describe() 再扫一眼各列分布确认最小值、最大值、分位数都落在合理区间。这一步是后面所有图表的「后悔药」提前吃后面省事。3. 选对可视化工具链为什么不用 Excel也不一上来就上 BI3.1 三个工具的分工逻辑别指望一个工具干完所有事做 python 数据分析与可视化时最常见的默认组合是 pandas 加 Matplotlib再加一个 Plotly 兜底交互。真正动手前先想清楚这份数据集是要交付一个静态分析报告还是要交给业务方自己筛选查看前者用 Matplotlib 完全够后者需要 Plotly 或 BI 工具。Excel 适合数据量小、字段少、一次性探索的场景但它有两个硬伤一是聚合逻辑藏在单元格里换一批数据要手动改公式二是图表的配色、标注、批量导出很难脚本化。BI 工具Power BI、Tableau 这类适合企业里需要业务部门自助分析的情况但对个人做数据集练习来说搭建数据源、维护权限和仪表板配置的代价偏高而且代码不可复现。我自己的分法是pandas负责数据清洗、聚合、透视产出分析用的中间表Matplotlib Seaborn负责静态图的版式和密度表达输出 PNG/SVG 给报告Plotly负责把两三张关键图转成交互式 HTML交付给不写代码的人。3.2 用 Matplotlib 画品牌均价 Top 10最小可运行代码先做一个最常用的图各品牌平均售价的 Top 10 条形图。这段代码是「聚合-排序-截取-画图」的完整链路后面所有类似的条形图都可以套这个模板。import matplotlib.pyplot as plt price_by_make ( df.groupby(Make)[Price] .mean() .sort_values(ascendingFalse) .head(10) ) plt.figure(figsize(10, 6)) price_by_make.plot(kindbarh) plt.xlabel(Average Price (USD)) plt.title(Top 10 EV Makes by Average Price) plt.gca().invert_yaxis() # 让均价最高的品牌出现在顶部 plt.tight_layout() plt.show()逻辑说明groupby 按品牌分组后对 Price 求均值sort_values 降序排列head(10) 截取前十个然后直接用 Series.plot 画水平条形图。这里特意用 barh 而不是 bar是因为品牌名称通常较长竖排条形图的 x 轴标签会挤成一团。参数说明figsize(10, 6) 是 PPT 半页图的常见尺寸invert_yaxis() 不调用的话图表顶部是均价最低的品牌读图顺序不符合大多数人的直觉。这张图适合回答「哪个品牌均价最高」这类问题但要注意它掩盖了组内差异——特斯拉既卖几万美元的入门车型也有十几万的高性能版本只看均价会把它的价格带宽度完全抹掉。所以均价图一般配合箱线图一起看。import seaborn as sns top_makes price_by_make.index.tolist() plt.figure(figsize(12, 6)) sns.boxplot( datadf[df[Make].isin(top_makes)], xMake, yPrice, ordertop_makes ) plt.xticks(rotation45) plt.tight_layout() plt.show()参数说明order 参数传入我们刚才算出的 Top 10 品牌列表保证箱线图的排序和条形图一致rotation45 让品牌名斜着显示避免互相遮挡。通过对比条形图和箱线图能一眼看出哪些品牌是「全线高价」哪些品牌是「有高价也有走量车型」。3.3 什么时候切到 Plotly三种典型场景Matplotlib 擅长静态出版级图但交互场景它做起来很别扭。我认定需要切到 Plotly 的情形有三种第一读者需要悬停看到具体型号名而不是只看一个数据点第二需要缩放、框选局部点第三交付对象不装 Python。这三个条件只要中一个就直接上 Plotly。import plotly.express as px fig px.scatter( df, xRange_km, yPrice, colorMake, hover_data[Model, Battery_kWh], titlePrice vs. Range - 2024 EV Dataset ) fig.write_html(price_range.html) fig.show()参数说明hover_data 控制在悬停提示里额外显示哪些字段放两三个最合适放多了 Tooltip 会变成一整块表格反而难读。colorMake 会自动为每个品牌分配一种颜色如果品牌太多可以用 color_discrete_sequence 参数指定调色板比如 px.colors.qualitative.Set2。write_html 生成的单个 HTML 文件可直接发给别人对方用浏览器打开就能交互不需要装 Python 或 Jupyter。唯一需要注意的是文件体积后面避坑部分会详细讲。4. 把「报表」变成「洞察」四张核心图的落地代码与参数调优4.1 续航分布直方图bins 是最值得反复试的参数拿到清洗后的数据第一张图应该是续航分布的直方图。它能快速回答「这份数据集的车型是偏短续航还是长续航」「有没有明显的双峰结构」。直方图最关键的参数是 bins 和 range很多人忽略后者导致横轴上出现一大段没有数据的空白区间。plt.figure(figsize(10, 5)) plt.hist( df[Range_km], bins30, range(0, 800), color#2a6f97, edgecolorwhite, alpha0.85 ) plt.axvline(df[Range_km].median(), colorred, linestyle--, labelmedian) plt.xlabel(Range (km)) plt.ylabel(Count) plt.legend() plt.tight_layout() plt.show()参数说明bins30 在千条量级的数据里是比较稳的选择太少会丢掉双峰信息太多会出现大量空桶画出来像梳子。range(0, 800) 用来截掉极端值如果数据里有续航超过 1000 公里的概念车不设 range 的话整张图会被拉伸得很难看。axvline 画中位数参考线比手动在标题里写「中位数为 420 公里」更直观。这里有个常见的认知坑直方图的形状对 bins 的起始位置和宽度非常敏感。同样是这份数据bins15 和 bins60 画出来的结论可能不同——前者可能看不出双峰后者会显得噪声很大。一个可用经验是先用 bins30 扫一眼形状再尝试 20 和 40如果三种参数下图的形态基本一致说明结论稳如果差异很大说明样本量不足需要转为画累积分布函数CDF来替代直方图。4.2 价格与续航散点图相关性的真相和离群点标注散点图是看价格与续航关系的最直接方式。很多人拿到数据第一件事就是画散点图然后强行拟合一条直线但电动汽车数据集里这条关系通常不是理想线性——高端车型价格翻倍续航却只提升三成。更值得关注的是那些「贵且续航短」或「便宜且续航长」的离群点。import numpy as np x df[Range_km] y df[Price] fit np.polyfit(x, y, 1) plt.figure(figsize(10, 6)) plt.scatter(x, y, s18, alpha0.4, color#333) plt.plot(x, np.polyval(fit, x), color#c1121f, lw2, labellinear fit) # 抽稀标注离群点约每 50 个点里取一个做判断 step max(1, len(df) // 50) for idx in df.index[::step]: row df.loc[idx] if row[Price] 150000 or (row[Range_km] 200 and row[Price] 80000): plt.annotate( f{row[Make]} {row[Model]}, xy(row[Range_km], row[Price]), xytext(8, 8), textcoordsoffset points, fontsize7 ) plt.xlabel(Range (km)) plt.ylabel(Price (USD)) plt.legend() plt.tight_layout() plt.show()逻辑说明polyfit(x, y, 1) 做一元线性回归np.polyval 在同一条 x 上算出拟合值画出回归线。annotate 部分的抽稀逻辑用 df.index[::step]——每约 50 行取一行做判断避免对几千个点逐个标标签画布上会叠成一片黑色。判断条件里的两个阈值是按这份数据的实际分布定的价格超过 15 万美元或者续航低于 200 公里但价格高于 8 万美元都是「不符合主趋势」的典型。参数说明s18 控制点大小alpha0.4 控制透明度数据量大时二者配合能减少重叠区域的噪声。xytext(8, 8) 是标签相对数据点的偏移量单位像素字体设成 7 号在 10 英寸宽的画布上不至于遮太多点。画完后顺手算一下 Pearson 相关系数print(df[Range_km].corr(df[Price]))如果系数在 0.5 到 0.7 之间属于「中等偏正相关」说明价格确实和续航有关但远不是决定性因素这时候散点图上的离群点才是真正的分析价值。4.3 年度均价趋势线rolling 窗口设错会让拐点「慢半拍」如果数据集里带年份字段可以画一条年度均价趋势线。但直接按年分组求均值再做折线图往往折线很抖——某些小众品牌在个别年份可能只有一辆车均价被单辆车带飞。用 rolling 平滑能压住这种抖动但窗口参数有讲究。yearly_price ( df.groupby(Year)[Price] .mean() .reset_index() ) yearly_price[Price_smooth] yearly_price[Price].rolling( window3, centerTrue, min_periods1 ).mean() plt.figure(figsize(10, 5)) plt.plot( yearly_price[Year], yearly_price[Price], o-, labelraw ) plt.plot( yearly_price[Year], yearly_price[Price_smooth], s--, labelrolling(3) ) plt.xlabel(Year) plt.ylabel(Average Price (USD)) plt.legend() plt.tight_layout() plt.show()参数说明window3 表示取当前值、前一个、后一个的均值centerTrue 把窗口中心放在当前点上而不是默认的向左偏移min_periods1 保证首尾年份至少有一个值就能算出平滑结果。这三个参数里最容易踩坑的是 center——如果不设平滑曲线会比真实拐点晚一年出现在分析报告里被较真的读者一眼抓包。还有一个细节如果年份列本身是 object 类型比如「2024 款」这样的字符串直接用 groupby 会把每个字符串当成独立分组排序也会乱。先用 pd.to_numeric 提取年份或者用 str.extract 正则抓数字df[Year] df[Model_Year].astype(str).str.extract(r(\d{4})).astype(float)4.4 电池容量与整备质量双轴图单位不同的图别硬拼最后一个核心图是看电池容量和整备质量随年份的变化趋势。这两个变量量纲完全不通——一个是 kWh一个是 kg放在同一张图里只能做双轴。双轴图本身不是坏事但做得不好就容易误导读者让人觉得两条趋势线的数值是可以直接比较的。我做这类图的原则是颜色、刻度标签、曲线三者严格一一对应。fig, ax1 plt.subplots(figsize(10, 5)) color_batt #0077b6 color_mass #e07a5f ax1.scatter( df[Year], df[Battery_kWh], s15, alpha0.3, colorcolor_batt, labelBattery (kWh) ) ax1.set_ylabel(Battery capacity (kWh), colorcolor_batt) ax1.tick_params(axisy, labelcolorcolor_batt) ax2 ax1.twinx() ax2.scatter( df[Year], df[Mass_kg], s15, alpha0.3, colorcolor_mass, labelMass (kg) ) ax2.set_ylabel(Curb mass (kg), colorcolor_mass) ax2.tick_params(axisy, labelcolorcolor_mass) ax1.legend(locupper left) ax2.legend(locupper right) plt.tight_layout() plt.show()逻辑说明ax1 是主坐标轴twinx() 生成共享 x 轴的右侧副坐标轴。两个轴的刻度颜色分别和曲线颜色一致这样读者即使不看图例也能通过颜色建立「哪条线对应哪个轴」的映射。用 scatter 而不是折线是因为同一年的不同车型之间没有时间上的先后关系连成线反而制造出「按时间演进」的错觉。参数说明alpha0.3 在这里是必须的几百个点叠加后透明度低会直接黑成一团。这张图的价值在于能看出电池容量逐年上升的同时整备质量有没有同步走高。如果两者同向变化说明续航增长一部分是靠增重换来的这个发现比单纯看续航数字有意义得多。5. 避坑笔记全电动汽车可视化分析里最容易翻车的五个现场5.1 数值列被 pandas 读成 object画图直接报错现象代码执行到 plt.hist 或 df.corr() 时报 TypeError提示不能把字符串和数值做比较。原因数据集某列里混入了占位符比如「—」「未知」「N/A」pandas 读取时为了保住这些字符串把整列类型抬高成了 object。解决用 pd.to_numeric 配合 errorscoerce 强制转换转失败的值会变成 NaN再走缺失值处理逻辑。df[Price] pd.to_numeric(df[Price], errorscoerce) df df.dropna(subset[Price])这个坑的特点是df.head() 看不出问题只有跑到画图或数值运算那一行才爆出来。所以清洗阶段对每一列都要有明确的类型预期不要依赖 pandas 的自动识别。5.2 品牌太多时散点图糊成一片图例还占半张图现象按品牌着色的散点图里三四十个品牌挤在一个图例里图例占了画布四分之一数据点叠成黑团。原因品牌数量远超图例的合理承载范围同时散点的透明度和尺寸没有调低。解决先只保留 Top 10 或 Top 15 品牌再画图其余品牌合并成一个「Other」分组散点用 alpha0.4、s15 这套参数打底图例用 bbox_to_anchor(1.02, 1) 移到画布外。top_brands df[Make].value_counts().head(10).index df_plot df[df[Make].isin(top_brands)].copy() df_plot.loc[~df_plot[Make].isin(top_brands), Make] Other这样图例只剩下 11 项配色也能分辨整个图的可读性比硬塞全部品牌高出一个量级。5.3 Matplotlib 中文标签全部变成方块现象plt.title 里的中文在 Windows 和 Linux 服务器上显示为一个个方框。原因Matplotlib 默认字体是 DejaVu Sans不含中文字形。解决在脚本开头统一指定中文字体并关闭负号显示的问题。plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei, WenQuanYi Micro Hei] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题注意三个字体覆盖了 Windows 和 Linux 的常见环境。如果在 macOS 上跑需要再加 PingFang SC 或 Arial Unicode MS。这个问题最坑的地方在于代码在本机跑得好好的部署到服务器或发给同事图里全是方块。5.4 清洗后的 CSV 用 Excel 打开中文全变乱码现象df.to_csv(clean.csv, indexFalse) 导出后Excel 打开看到「鏄煶」这类乱码。原因Excel 默认按本地 ANSI 编码解析 CSV而 pandas 默认写 UTF-8 无 BOM。解决导出时指定 utf-8-sig 编码它会在文件头写入 BOM 标记Excel 才能正确识别。df.to_csv(ev_clean.csv, indexFalse, encodingutf-8-sig)这个坑属于典型的「自己电脑上没问题交付给别人就出事」尤其是非技术背景的同事他们只会双击 CSV不会去调编码。5.5 Plotly 导出的 HTML 文件巨大发出去别人打不开现象fig.write_html() 出来的单文件有几十 MB微信里发送后对方浏览器卡死或空白。原因默认配置会把整个 plotly.js 库内嵌进 HTML一份库就有几 MB 到几十 MB。解决使用 include_plotlyjscdn 参数HTML 会改为引用在线库体积骤减缺点是对方必须联网才能打开。如果必须离线交付建议保留默认嵌入但只放两到三张交互图不要一个 HTML 里塞几十张图。fig.write_html(price_range.html, include_plotlyjscdn)这五个坑前两个是新手最容易碰到的翻车现场后三个属于「交付时才会暴露」的隐性坑。做数据可视化分析有一条血泪经验图在自己的电脑上画出来只算完成三成真正的问题是换机器、换办公软件、发给别人之后还能不能原样显示。6. 进阶把核心图拼成可交互 HTML 看板交付前先过自查清单6.1 用 iframe 拼装多张 Plotly 图如果只看一份静态报告前面那些图已经够用。但实际工作中业务方经常会问「能不能让我自己筛选看看」。这时我会把三张核心图分别导出为三个 HTML再写一个简单的 index.html 把它们嵌进去。相比 Plotly 的 make_subplots 硬拼独立文件的好处是每张图都能单独打开、单独缩放、单独悬停互不干扰。scatter.write_html(view_price_range.html, include_plotlyjscdn) hist.write_html(view_range_hist.html, include_plotlyjscdn) fit.write_html(view_price_trend.html, include_plotlyjscdn) html_index !DOCTYPE html html headmeta charsetutf-8title2024 EV Analysis/title/head body h12024 电动汽车数据集分析看板/h1 iframe srcview_price_range.html width100% height520/iframe iframe srcview_range_hist.html width100% height520/iframe iframe srcview_price_trend.html width100% height520/iframe /body /html with open(index.html, w, encodingutf-8) as f: f.write(html_index)参数说明每个 iframe 的 height 设成 520px是浏览器一屏内比较舒适的高度宽度全部取 100%保证在不同屏幕下自适应。文件命名用 view_ 前缀和人手交互时能直观看出这组文件是「展示页」而不是原始数据避免交付时混淆。6.2 交付前的三道自查清单HTML 拼好后我会打开 index.html 逐图检查三件事第一横轴单位是否正确。凡是用 RANGE 的地方确认展示的是公里而不是英里凡是用价格的地方确认币种是美元且没有混入 0 占位值。第二图例和颜色是否能对上。按品牌着色时品牌列表是否和 Top N 一致「Other」分组有没有混进主品牌。第三悬停提示里的数值是否落在清洗后的合理范围比如续航不是负数、价格不是 0。最后留一道附加验证比较 Price 的均值和中位数如果两者相差超过 20%说明数据有长尾看图时优先参考中位数而非均值。这是我每次做电动汽车数据可视化分析都会保留的一道自查虽然有点像玄学但它确实帮我挡掉过好几次把异常值当趋势去汇报的尴尬。希望这套流程和参数能帮你少踩几个坑跑数据顺利。本文还有配套的精品资源点击获取
网站建设高端定制企业官网