尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python实现新能源汽车销售数据时空分析与趋势预测
简介这是一份面向新能源汽车市场研究人员、数据分析与机器学习学习者的完整代码数据集聚焦销售数据的时空特征分析与未来趋势预测覆盖从数据清洗、特征工程、模型训练到结果可视化的全流程可用于揭示销量随季节、地区等维度变化的规律。资源共104个文件压缩后约2.64MB以Python脚本、训练模型.pt、可视化图表.png和交互式HTML报告为主并附CSV源数据、JSON配置文件与CSS样式可直接复现完整分析过程其中模型文件保存了训练好的预测结果图表直观展示时空分布HTML报告汇总关键结论。全流程基于主流数据分析与机器学习库实现既能作为练手项目也能为相关市场研究提供参考。目前已有34人浏览学习适合希望系统掌握新能源汽车销量分析完整方案的初学者与进阶者尤其有助于理解时间序列与空间分析的落地方法。1. 新能源汽车销售数据时空分析一套能直接跑出结论的Python方案如果你手头有一份按省份、按月份记录的新能源汽车销量表第一反应可能是拉个折线图看大盘。但落到经营决策层面真正要回答的不是“涨了还是跌了”而是哪些区域的销量波动在主导大盘、月度数据的季节性成分有多强、下个季度重点押注哪个市场。这份“新能源汽车销售数据时空特征分析与趋势预测”资源包正好把这三件事拆成了可复现的Python脚本加配套数据集从数据清洗出发构造时间与空间特征做省份维度的交叉分析最后用机器学习模型做趋势预测并给出误差评估。适合三类人想用销售数据做月度复盘的数据分析师需要判断区域投放节奏的运营策略岗以及刚学完pandas和sklearn、想拿真实数据练手的Python学习者。2. 数据预处理与时空特征构建先搞懂字段再动手写代码2.1 原始销售数据的典型结构以这份资源里的数据集为例销售记录就是一张CSV核心字段围绕三个维度展开空间字段省/市、时间字段月份或日期、销量字段辆数或销售额。我第一次拿到这类数据时不会急着跑代码而是先做三件事看head()样本确认字段名有没有中文编码问题用info()看每列非空数量再用nunique()确认省份个数是否合理。这样做是因为销售数据的脏点往往不在数值本身而在字段口径。同一个省在不同批次数据里可能同时出现“广东”和“广东省”两种写法月份列有的是“2024-01”有的是“2024/1”销量列偶有负值或0值通常对应退订或未上牌的记录。这些如果不清理后面做透视表和聚合时会出现莫名其妙的省份数量翻倍或同比暴跌。import pandas as pd import numpy as np df pd.read_csv(sales_data.csv, encodingutf-8) # 先看字段、缺失情况和省份数量 print(df.head()) print(df.info()) print(df[province].nunique()) # 省份名称归一去掉“省/市/自治区”等后缀 df[province] df[province].str.replace(省|市|自治区, , regexTrue) # 时间归一统一转成 YYYY-MM 字符串 df[month] pd.to_datetime(df[month], formatmixed).dt.strftime(%Y-%m) # 负销量视为异常置为 NaN df.loc[df[sales] 0, sales] np.nan # 同省同月有多条车型记录时先按月份汇总 df df.groupby([province, month], as_indexFalse)[sales].sum() # 缺失销量用该省份历史中位数填充仍然缺失的兜底为 0 df[sales] df.groupby(province)[sales].transform( lambda x: x.fillna(x.median()) ) df[sales] df[sales].fillna(0)代码逻辑说明formatmixed是pandas 2.0的参数能同时解析“2024-01”和“2024/1”两种写法如果不支持先手动把/替换成-再转换。负销量置NaN后用省份中位数填充比用全局均值填充更稳因为不同省份的量级差异很大全局均值会把低销量省份的异常值强行拉高。最后一步groupby汇总很关键——原始数据里如果按车型拆行必须先聚合到省份×月粒度否则透视表会出现重复索引。2.2 时间特征与空间特征的构造细节时间特征是预测模型里的基础特征核心是让模型“知道”这是几月、哪个季度、当年第几个月。空间特征则要回答“这是哪个区域”以及“区域之间是否有邻近关系”。我一般构造以下几组特征名类型构造方式用途year数值从月份解析捕捉年度间趋势month_num数值从月份解析捕捉季节性quarter数值1/2/3/4捕捉季度节奏province_code类别数值省份映射编码模型区分省份region类别字符串华北/华东/华南等做区域聚合分析is_year_end0/112月置1捕捉年末冲量效应这里有一个常见误区直接把省份中文名丢给回归模型。树模型虽然能容忍标签编码但编码的数字大小本身没有语义模型容易把“广东5”和“江苏6”理解成“江苏比广东大”。更好的做法是用类别特征或干脆用省份销量排名作为编码。# 解析时间特征 df[year] pd.to_datetime(df[month]).dt.year df[month_num] pd.to_datetime(df[month]).dt.month df[quarter] pd.to_datetime(df[month]).dt.quarter # 年末节点标记12月置1其他月份置0 df[is_year_end] (df[month_num] 12).astype(int) # 区域映射按业务分区手动维护省份字典这里是示例 region_map { 北京: 华北, 上海: 华东, 广东: 华南, # 完整映射关系见资源包内 region_config.py } df[region] df[province].map(region_map) # 没有映射到的省份归入“其他” df[region] df[region].fillna(其他)这段代码有两个细节值得注意。第一is_year_end是针对新能源汽车年底冲量的业务特点加的如果你处理的是其他品类可以替换成“双11月是否大促”之类的业务标记。第二区域映射按直辖市和省会所在区域归类没有覆盖港澳台因为销量数据里通常不包含这些地区如果你的数据里出现了先确认是真实业务还是脏数据再决定是否单独归类。2.3 数据检查与特征验证特征构造完之后不要急着训练模型先做一轮验证。我会跑df.groupby([year, quarter])[sales].sum()看季度汇总是否与常识吻合再跑df.groupby(province)[sales].sum().sort_values()看省份排名核对头部省份是否与新能源渗透率较高的地区匹配。这一步能拦截大多数字段映射错误。还要检查数据集的时空完整性。有的省份只出现在某几个月份有的月份缺了某个省份的记录。做时间序列预测时这类缺失会造成“省份忽然消失”的假象。处理方式是先构造完整的时间×省份笛卡尔积再左连接原表缺的销量用前后月份插值。# 构造完整的时间-省份组合 all_months pd.period_range( df[month].min(), df[month].max(), freqM ).astype(str) all_provinces df[province].unique() full_index pd.MultiIndex.from_product( [all_months, all_provinces], names[month, province] ) df_full df.set_index([month, province]).reindex(full_index).reset_index() # 缺失销量用相邻月份插值首尾缺失也补上 df_full[sales] df_full.groupby(province)[sales].transform( lambda x: x.interpolate(limit_directionboth) )这段代码里reindex是预处理中最关键的一步它保证后面做省份×月份透视表时矩阵完整齐整热力图不会出现白块。插值时注意limit_directionboth因为首尾月份也可能缺数据只向前或只向后插会留下NaN。3. 时空交叉分析省份×月份的二维观察3.1 为什么总量曲线会掩盖结构问题只看全国总销量曲线通常只能看到“一路上涨”或“某月下跌”这样的大趋势。真正有意思的变化藏在结构里某个省份在快速放量另一个省份却在退坡总量曲线把这两股力量抵消掉了。比如华东某省在6月出现销量骤增东北某省在年末冲量阶段拉升最猛这些规律只在省份×月份的矩阵里才能看到。用时空交叉分析就是把“总量”拆成“省份×时间”的矩阵逐个格子看变化。横向看省份哪些省份在持续增长哪些波动剧烈纵向看月份哪些月份是全行业旺季哪些月份存在明显的政策驱动拐点。3.2 透视表与热力图实现pivot df_full.pivot_table( indexmonth, columnsprovince, valuessales, aggfuncsum ).sort_index() # 月份按时间排序 pivot pivot.reindex(sorted(pivot.index)) # 计算省份维度的环比增速无穷大替换为 NaN mom_growth pivot.pct_change().replace([np.inf, -np.inf], np.nan) # 只看最近12个月避免矩阵过大 import matplotlib.pyplot as plt import seaborn as sns recent pivot.tail(12).T plt.figure(figsize(14, 8)) sns.heatmap(recent, cmapYlOrRd, annotFalse, fmt.0f, cbar_kws{label: 销量辆}) plt.title(近12个月各省新能源销量热力图) plt.tight_layout() plt.show()这里的透视表用aggfuncsum同省同月若有多条记录会自动汇总pct_change之后的环比增速矩阵可以继续做聚类把增长模式相近的省份分到一组。热力图上如果某一行的所有格子颜色都很浅说明这个省份近一年销量偏低基本可以判断为低优先级市场。读热力图时我一般关注两种模式持续深色的行是基本盘省份波动小预测相对容易忽深忽浅的行受政策或新车型影响大预测时要加宽区间。同一列同一个月颜色深的省份通常是当月冲量主力值得重点跟进。3.3 用STL分解拆出趋势、季节和残差热力图解决的是空间差异时间维度上的趋势和季节性我建议用STL分解。选择STL而不是简单移动平均是因为它能同时输出趋势项、季节项、残差项三个分量而且robustTrue时对异常值不敏感不会因为某个月政策抢装出现天量销量就把季节项拉变形。from statsmodels.tsa.seasonal import STL # 取全国月度总销量 national df_full.groupby(month)[sales].sum().astype(float) national.index pd.to_datetime(national.index) # STL分解period12 表示年度季节性 stl STL(national, period12, robustTrue) result stl.fit() fig, axes plt.subplots(4, 1, figsize(12, 10), sharexTrue) result.observed.plot(axaxes[0], titleObserved) result.trend.plot(axaxes[1], titleTrend) result.seasonal.plot(axaxes[2], titleSeasonal) result.resid.plot(axaxes[3], titleResidual) plt.tight_layout() plt.show()STL参数里period是季节周期长度月度数据一般取12如果有明显的季度节奏可以试4robustTrue能让分解结果不受少数异常月份干扰。分解结果里最值得看的是残差项如果残差在某段时间持续偏高或偏低说明存在模型没捕捉到的外部因素比如补贴政策退坡或新车型集中上市。趋势项会告诉你全国大盘是处于上升通道还是平台期季节项幅度如果达到月均销量的20%以上说明季节节奏明显预测模型必须把月份特征作为第一优先级。残差项里连续3个月出现同方向偏离时通常意味着有新车型或新政策在起作用这种信号模型抓不到要靠业务侧人工补充。4. 趋势预测建模先定基线再上机器学习4.1 预测任务的边界定义做预测前先定义清楚预测多长、用什么粒度。这个项目里的典型设定是基于截至某月的各省月度销量预测未来3个月。粒度选省份×月不做全国单序列因为各省差异太大全国模型只会学到“平均状态”对具体省份没有指导意义。时间切分有一个原则预测任务必须按时间切分训练集和测试集不能用随机切分。随机切分会把未来数据混进训练集模型在评测时看起来很好上线后立刻打脸。我用最后3个月做测试集前面的数据做训练集。# 按时间排序后切分 df_full df_full.sort_values([month, province]).reset_index(dropTrue) months np.sort(df_full[month].unique()) train_months months[:-3] test_months months[-3:] train_df df_full[df_full[month].isin(train_months)] test_df df_full[df_full[month].isin(test_months)]这里months[:-3]取全部月份减去最后3个月months[-3:]取最后3个月。注意顺序是先排序再切分如果原始数据顺序是乱的切出来的测试集会混进早期数据。4.2 三个基线模型基线模型的目的不是追求精度而是给机器学习模型设定一个“及格线”。如果机器学习模型连移动平均都打不过说明特征构造有问题而不是模型不够高级。我一般先跑三个基线去年同期预测、近3个月移动平均、线性回归。其中去年同期预测对新能源销售尤其重要因为这个行业同比惯性很强。# 构造滞后特征和滚动均值特征 def make_features(df, lags[1, 2, 3, 12], windows[3, 6]): df df.sort_values([province, month]).copy() for lag in lags: df[flag_{lag}] df.groupby(province)[sales].shift(lag) for w in windows: df[frolling_mean_{w}] ( df.groupby(province)[sales] .transform(lambda x: x.rolling(w).mean()) ) # 去年同期销量 df[prev_year_same_month] df.groupby(province)[sales].shift(12) return df feature_df make_features(df_full) # 构造特征过程中会产生 NaN直接删掉 feature_df feature_df.dropna(subset[ lag_1, lag_3, lag_12, rolling_mean_3, rolling_mean_6, prev_year_same_month ])滞后特征lag_1代表上个月销量lag_12代表去年同月这两个特征对新能源销售预测非常重要。滚动窗口取3和6个月是为了平滑单月波动。省份分组做滞后很关键——不同省份之间没有直接销量传导关系不能跨省份取滞后值。4.3 机器学习模型训练与评估特征准备好后我直接用随机森林和LightGBM各跑一版比较MAE和RMSE。随机森林做对照LightGBM作为主力模型它们都能处理表格数据对特征间非线性交互的拟合能力足够。from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error # 省份做类别编码 province_codes {p: i for i, p in enumerate(df_full[province].unique())} df_full[province_code] df_full[province].map(province_codes) feature_cols [year, month_num, quarter, is_year_end, lag_1, lag_2, lag_3, lag_12, rolling_mean_3, rolling_mean_6, prev_year_same_month, province_code] train_data feature_df[feature_df[month].isin(train_months)] test_data feature_df[feature_df[month].isin(test_months)] X_train train_data[feature_cols].fillna(0) y_train train_data[sales] X_test test_data[feature_cols].fillna(0) y_test test_data[sales] model RandomForestRegressor( n_estimators300, max_depth10, min_samples_leaf3, random_state42, n_jobs-1 ) model.fit(X_train, y_train) pred model.predict(X_test) mae mean_absolute_error(y_test, pred) rmse np.sqrt(mean_squared_error(y_test, pred)) print(fMAE: {mae:.2f}, RMSE: {rmse:.2f}) # 按省份看误差分布 test_result test_data[[province, month, sales]].copy() test_result[pred] pred test_result[err] np.abs(test_result[sales] - test_result[pred]) print(test_result.groupby(province)[err].mean().sort_values(ascendingFalse))训练参数里n_estimators300对几千行数据已经足够再大收益有限但训练时间翻倍max_depth10限制单棵树深度防止模型记住极端值min_samples_leaf3强制叶子节点至少3个样本减少过拟合。评估指标选MAE和RMSE一起看MAE反映平均误差水平RMSE对预测离谱的月份更敏感两个指标相差越大说明某些月份预测偏差特别严重。按省份查看误差分布这一步很关键。如果个别省份MAE是其他省份的几倍通常有两个原因一是该省份销量波动剧烈需要更多历史特征二是该省份在训练集里出现的月份不完整特征比别的省份少。这两种情况都有对应补救方案但前提是先看到误差分布而不是只看一个全国平均MAE。注意不同来源的销量数据可能混用“辆”和“千辆”两种单位训练前先看sales列的量级混用会让模型学出完全错误的趋势。5. 避坑记录我在这个项目里翻过的五个车这套代码跑通之后真正花时间的是排查问题。我把踩过的坑按现象、原因、解决整理成五条每条都是实际操作中真实发生过的。5.1 随机切分导致预测虚高现象用train_test_split(random_state42)切数据测试集MAE只有业务预期的一半模型看起来完美到不真实。原因随机切分把同一省份相邻月份的数据同时放进训练集和测试集模型通过滞后特征直接“看见”了测试集信息。时间序列数据自带时间相关性随机切分破坏了这种结构造成信息泄漏。解决一律按时间顺序切分测试集必须是时间上最靠后的连续月份。我在代码里用sorted(months)取最后3个月绝不用随机切分。从那以后每次拿到时间序列数据第一件事就是确认切分方式。5.2 省份名称不统一导致聚合漂移现象透视表里省份数量比实际多出一倍“广东”和“广东省”被当成两个省份所有聚合结果全部偏小。原因上游数据采集时不同渠道对省份名称写法不统一有的带后缀有的不带还有“内蒙古自治区”这类多字后缀。解决在预处理第一步做省份名称归一化用str.replace(省|市|自治区, , regexTrue)去掉后缀并且对“内蒙古”“广西”这类双字地区单独做映射表兜底。这个操作必须放在最前面否则后面所有groupby都会裂开。5.3 滞后12个月特征丢失大量样本现象构造完lag_12特征后训练数据行数少了将近三分之一模型在测试集上的表现反而不如少特征的版本。原因每个省份前12个月没有“去年同期”数据groupby().shift(12)全是NaN直接dropna把这些行删了导致早期样本全部丢失。解决如果历史数据不足一年就不要硬塞lag_12和prev_year_same_month这两个特征改用lag_1、lag_2、lag_3。实在想保留就把缺失值填成该省份历史均值而不是直接删行。5.4 STL分解的period设错导致季节项和趋势项混杂现象分解出来的趋势项带有明显波浪状季节项幅度异常大看起来像是把趋势的波动全塞进了季节项。原因月度数据的周期设成了4而不是12模型把年度季节性误判成季度性导致季节项和趋势项互相污染。解决月度销量数据一律从period12开始试只有明确存在季度周期时才用4。判断方法很简单先画出原始序列如果每年同一个月份都有相似的波峰波谷就是年度季节性必须用12。5.5 预测结果出现负销量现象某省份下个月的预测值是-120辆业务方直接质疑模型不可用。原因回归模型没有输出约束低销量省份的极端波动会让模型学到负向趋势尤其是训练集里出现过个位数销量的月份。解决预测后做一层clip把负值归到0或该省份历史最小非零销量的10%。如果模型支持也可以改用分位数回归直接预测p10/p50/p90天然规避负数问题。6. 进阶把预测结果变成区域决策建议6.1 用SHAP解释省份特征的影响模型训练完不是终点业务方更关心的是为什么预测这个省涨这么多。我会用SHAP值做特征归因看每个省份预测值的主要驱动因素。import shap explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test, feature_namesfeature_cols)SHAP输出的横坐标表示特征对预测值的影响方向lag_1和lag_12通常是影响最大的两个特征这符合业务直觉——上个月卖得好下个月大概率也不差去年同月卖得好今年同月也弱不到哪去。如果某个省份的rolling_mean_6贡献特别大说明它的销量高度依赖过去半年的积累属于稳步爬坡型市场投放时应优先保障库存深度而不是营销拉新。6.2 输出带有波动区间的预测结果单点预测会给业务方一种“预测就是精确值”的错觉。我会对每个省份输出预测值±1.2倍MAE的范围作为预期波动区间。区间宽度直接告诉决策者这个预测有多可靠MAE小的省份区间窄可以按这个数字排产MAE大的省份区间宽只能作为参考方向。# 按省份聚合MAE并生成预测区间 prov_mae test_result.groupby(province)[err].mean() prov_pred test_result.groupby(province)[pred].mean() for province in [广东, 上海, 四川]: pred_val prov_pred[province] m prov_mae[province] print(f{province}: 预测 {pred_val:.0f} 辆, f区间 [{pred_val - 1.2*m:.0f}, {pred_val 1.2*m:.0f}])1.2倍MAE是经验值大约能覆盖七成实际值不会宽到没信息量也不会窄到频繁被打脸。如果要更严格可以换成分布外分位数预测区间但业务场景里这个简化已经够用。6.3 把模型输出做成月度滚动预测项目做完之后我养成了一个习惯每个月初拿到新一期销量数据先做一轮快速检查把最新真实值追加到训练集重新训练模型再预测未来3个月。这样的滚动方式让滞后特征始终指向最新信息模型不会越跑越旧。顺带一提做月度滚动时如果某个省份连续两个月真实值都落在预测区间之外我会暂停纯模型预测改成人工调研该省份是否有政策变化或经销商网络变动。机器学习模型擅长捕捉稳定模式但捕捉不了突发的政策冲击这种时候人工判断比调参更有价值。从那以后我每次跑销售预测都强制把“更新数据→重训模型→复核区间外省份”走一遍。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

AI硬件选型指南:从Stable Diffusion到模型微调的四维决策法

AI硬件选型指南:从Stable Diffusion到模型微调的四维决策法

1. 这不是一张“买来就用”的显卡指南,而是一份AI工作流的硬件决策地图RTX 50系列显卡还没发布,但围绕它的讨论已经像一场提前预演的风暴。你刷到的每一条“RTX 50爆料”,背后站着的是正在为Stable Diffusion跑图卡顿发愁的设计师、是训练一个…

📅 2026/10/3 4:01:38
USACO银组真题解析:用图论连通分量建模,破解奶牛语言翻译问题

USACO银组真题解析:用图论连通分量建模,破解奶牛语言翻译问题

做USACO往年的银组题,最常有的感受是:题目背景憨憨的,考点却不含糊。P3026 [USACO11OPEN] Learning Languages S 是典型一例——农场里有 N 头牛、M 种语言,每头牛会若干门语言,问最少让多少头牛额外学一门语言&#x…

📅 2026/10/3 3:56:38
Python模型持久化选型:Joblib与pickle的边界及高效缓存实践

Python模型持久化选型:Joblib与pickle的边界及高效缓存实践

1. 为什么选Joblib而不直接pickle:两者的边界差异接触Python的朋友,特别是做过机器学习模型落地的人,基本都经历过同一个场景:模型训练好了,想保存下来下次直接用,网上一搜,大半教程告诉你用pic…

📅 2026/10/3 3:56:38
MORE NEWS

更多资讯

📰

Python实现KMeans聚类算法:源码、数据集与实战避坑指南

简介:这份资源面向机器学习入门者与数据挖掘方向的学习者,提供了一套用Python实现的KMeans聚类算法完整方案,可用于理解聚类分析从数据预处理、核心算法执行到结果可视化的全流程,适合作为课程实验、算法练习或项目参考的实践素材…

📰

激光雷达接收芯片选型实战:APD、SiPM与SPAD阵列对比

1. 这不是芯片参数表,而是一份激光雷达接收端的“实战选型手记”我干激光雷达硬件设计快八年了,从最早给扫地机器人配单点TOF模组,到后来做车载前向4D成像雷达的接收链路,踩过的坑比走过的桥还多。今天聊的这个标题——“激光雷达…

📰

SpringBoot+Vue 项目申报管理系统源码实战解析

做项目申报管理系统的人,应该都经历过申报季那种兵荒马乱的阶段:通知发下去、材料收上来、格式五花八门、打回重报的信息散落在聊天记录里,评审打分靠纸质表格统计到半夜。这套基于SpringBootVueMyBatisMySQL的源码项目,就是冲着这…

📰

QwenPaw本地部署与调用指南:从安装到批量处理

1. 从零上手 QwenPaw:这个工具到底解决什么问题第一次听到 QwenPaw 这个名字,很多人会下意识把它和某个模型或者某个框架联系起来。实际上,QwenPaw 是一个面向本地化部署与调用的工具型项目,核心定位是让使用者能够在自己熟悉的开…

📰

Python+Twilio搭建短信通知系统,实现服务器监控与告警

1. 项目整体设计与思路拆解1.1 为什么选Twilio而不是自己搭短信网关做短信通知系统,第一关其实是“选型”。我见过不少人一上来就研究短信猫、GSM模块,或者去对接国内各种短信服务商,折腾半个月还在签名审核和模板报备里打转。如果你只是想给…

📰

基于S7-200 PLC与组态王的中小型污水处理控制系统设计与实现

前阵子接手了一个小型生活污水处理站的控制系统改造,现场核心控制器是一台西门子S7-200 PLC,在柜子里稳定跑了七八年,但因为一直没上位机,运行状态全靠人工跑现场看,异常了也不知道。业主要求加一套中控监控&#xff0…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬