恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

二手车价格预测EDA实战:从数据诊断到特征工程准备

  • 首页
  • 资讯中心
  • /
  • 二手车价格预测EDA实战:从数据诊断到特征工程准备

相关资讯

TPU 仓库 ViLD 开放词汇检测实战:基于视觉-语言知识蒸馏的两阶段检测器全解析 2026/10/13 6:41:03
Gabor滤波器实战:从参数调优到纹理分类的完整指南 2026/10/13 6:41:03
lakeFS Python SDK 中的 GarbageCollectionConfig 模型:grace_period 参数解析与 GC 实战指南 2026/10/13 6:41:03

最新资讯

无模块注入实战:从Shellcode到EDR绕过与C++实现
无标题项目自救指南:从项目定位到命名落地的完整方法
Oracle EBS R12多组织多币种核算架构解析:跨国企业财务落地指南
vagaa 2.6.6.7实战:ED2K老客户端的配置、避坑与网络调优
鸿蒙ACL权限申请全攻略:从system_basic到错误码201排查
路由器与交换机的区别:二层与三层设备如何配合组网

今日推荐

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析
CAN总线错误处理机制深度解析:从位错误到Bus Off的硬件级调试

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

二手车价格预测EDA实战:从数据诊断到特征工程准备

发布时间:2026/10/13 6:41:03
二手车价格预测EDA实战:从数据诊断到特征工程准备 1. 从零读懂Task2为什么EDA是二手车价格预测的第一道分水岭很多人拿到一个回归预测任务第一反应是赶紧上模型、调参数、刷榜单。我见过太多这样的操作数据读进来简单fillna一下直接XGBoost跑起来然后盯着线上分数反复调参调了三天发现还不如别人一个简单的线性回归。问题出在哪出在压根没搞明白数据长什么样。DataWhale的Task2定位非常明确在Task1已经完成数据加载和初步观察的基础上系统性地做探索性数据分析EDA。这个阶段的核心目标不是建模而是回答几个关键问题——目标变量长什么样特征和目标之间是什么关系特征之间有没有冗余缺失值和异常值该怎么处理这些问题的答案直接决定了后续特征工程的方向和模型选型的天花板。二手车交易价格预测这个场景尤其特殊。它的目标变量是价格而价格数据天然具有几个特点右偏分布、存在极端值、受多因素非线性影响。如果你不做EDA直接建模模型很可能被少数高价车带偏或者被大量缺失值搞得性能骤降。我个人的经验是在一个中等规模的结构化数据竞赛中EDA做得好不好直接决定了你最终成绩是前30%还是前10%。这篇文章面向的是已经完成Task1、正准备进入Task2的参赛者也适合任何想系统学习结构化数据EDA方法论的人。我会把Task2涉及的每个环节拆开讲透包括为什么要这么做、具体怎么操作、实测中会遇到什么问题、以及我踩过的那些坑。代码以Python为主工具链是pandas matplotlib seaborn这些都是数据竞赛的标配不需要额外折腾环境。2. 数据加载后的第一件事把数据摸清楚2.1 数据规模与字段类型的快速体检拿到数据后别急着画图。先做一次基础体检搞清楚三件事数据有多少行多少列、每个字段是什么类型、内存占用多大。这听起来很简单但我见过有人跑了半天代码才发现数据读进来时类型全错了数值型被当成了字符串。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 读取数据 train pd.read_csv(used_car_train.csv) test pd.read_csv(used_car_test.csv) # 基础体检 print(f训练集形状: {train.shape}) print(f测试集形状: {test.shape}) print(f内存占用: {train.memory_usage(deepTrue).sum() / 1024**2:.2f} MB) print(\n字段类型分布:) print(train.dtypes.value_counts())这里有个细节值得注意二手车数据里经常有power、kilometer这类数值字段但有时候因为缺失值的存在pandas会把它们读成object类型。如果你不检查后面做数值计算时就会报错。我的习惯是读完数据后立刻用train.info()看一眼确认没有类型异常。另外训练集和测试集的字段差异也要检查。测试集通常会少一个目标列price但其他字段应该完全一致。如果发现字段不一致要么是数据版本有问题要么是读取时出了错。2.2 目标变量price的分布诊断目标变量的分布是EDA的重中之重。对于价格预测任务你需要搞清楚它是正态分布、右偏分布还是双峰分布。这直接决定了后续要不要做对数变换。# 目标变量分布 fig, axes plt.subplots(1, 3, figsize(18, 5)) # 原始分布 sns.histplot(train[price], bins50, axaxes[0], kdeTrue) axes[0].set_title(Price原始分布) # 对数变换后 sns.histplot(np.log1p(train[price]), bins50, axaxes[1], kdeTrue) axes[1].set_title(Log(Price1)分布) # 箱线图看异常值 sns.boxplot(xtrain[price], axaxes[2]) axes[2].set_title(Price箱线图) plt.tight_layout() plt.show() # 统计量 print(train[price].describe()) print(f偏度: {train[price].skew():.4f}) print(f峰度: {train[price].kurtosis():.4f})实测下来二手车价格数据几乎必然是右偏的偏度通常在2以上。这意味着大部分车价格集中在较低区间少数豪车拉高了尾部。如果你直接用原始price做回归模型会过度关注那些高价样本导致整体预测偏差。我的处理建议是先看对数变换后的分布是否接近正态。如果接近后续建模时对目标变量做log1p变换预测后再用expm1还原。这个操作在Kaggle的很多回归竞赛中都是标配效果立竿见影。但要注意如果评价指标是RMSE对数变换后还原会引入偏差这时候需要对预测结果做无偏修正或者直接用MAE作为评价指标。2.3 缺失值的全局扫描与分类缺失值处理是EDA中最容易被轻视的环节。很多人看到缺失值就无脑填均值或中位数这是大忌。正确的做法是先搞清楚缺失的模式是随机缺失还是系统性缺失缺失比例是多少缺失本身是否携带信息# 缺失值统计 missing train.isnull().sum() missing_pct (missing / len(train)) * 100 missing_df pd.DataFrame({ 缺失数量: missing, 缺失比例(%): missing_pct }).sort_values(缺失比例(%), ascendingFalse) print(missing_df[missing_df[缺失数量] 0])在二手车数据中bodyType、fuelType、gearbox这几个类别字段经常有缺失而model、brand等字段缺失较少。这里有个关键判断如果某个字段缺失比例超过40%填充的风险很大可以考虑直接构造一个缺失类别让模型自己学习缺失是否意味着某种特殊车况。我踩过的一个坑是对notRepairedDamage字段做填充时这个字段的值是-和0.0直接astype(float)会报错。必须先replace(-, np.nan)再处理。这种脏数据在真实业务数据里非常常见EDA阶段不发现建模时就会卡住。3. 单变量与多变量分析从每个字段里榨出信息3.1 数值型特征的分布与业务含义解读数值型特征不能只看统计量要结合业务含义去理解。以kilometer为例它代表车辆行驶里程。如果你只看describe()可能觉得就是个普通数值。但结合业务想一想里程越高车况越差价格应该越低。如果EDA发现里程和价格没有明显负相关那要么是数据有问题要么是这个字段被噪声污染了。# 数值特征分布 num_cols [power, kilometer, brand, model] fig, axes plt.subplots(2, 2, figsize(14, 10)) for idx, col in enumerate(num_cols): ax axes[idx // 2][idx % 2] sns.histplot(train[col].dropna(), bins50, axax, kdeTrue) ax.set_title(f{col}分布) plt.tight_layout() plt.show()power字段马力通常也是右偏的大部分车在100-200马力之间少数高性能车超过500。这个字段和价格的相关性往往很强但要注意非线性关系——马力从100到200对价格的影响可能远大于从400到500。brand和model虽然是数值编码但本质上是类别特征。直接当数值用会引入错误的序关系。比如brand1和brand2之间未必有大小关系。这类字段在EDA阶段就要标记出来后续做类别编码。3.2 类别型特征的频次分析与目标编码预判类别特征的分析重点是频次分布和目标均值分布。频次太低的小类别模型很难学到有效模式可以考虑合并。而目标均值分布则能告诉你这个特征是否有区分度。# 类别特征分析 cat_cols [brand, bodyType, fuelType, gearbox, notRepairedDamage] for col in cat_cols: print(f\n {col} ) print(f唯一值数量: {train[col].nunique()}) print(train[col].value_counts().head(10)) print(f目标均值:\n{train.groupby(col)[price].mean().head(10)})这里有个经验如果某个类别特征的唯一值超过50个比如model字段可能有上千个取值直接用One-Hot会导致维度爆炸。这时候目标编码Target Encoding或者频率编码是更好的选择。但目标编码容易过拟合必须配合交叉验证或者平滑处理。notRepairedDamage这个字段特别有意思。它表示车辆是否有未修复的损坏。从业务逻辑看有未修复损坏的车价格应该更低。如果EDA发现这个字段的缺失本身就和低价相关那缺失值就不能随便填而要当作一个独立类别。3.3 特征与目标的相关性矩阵与非线性关系识别相关性分析是EDA的常规操作但很多人只看皮尔逊相关系数这只能捕捉线性关系。对于二手车价格这种复杂场景非线性关系才是主流。# 相关性矩阵 corr_matrix train.corr() plt.figure(figsize(16, 12)) sns.heatmap(corr_matrix, cmapcoolwarm, center0, annotFalse) plt.title(特征相关性热力图) plt.show() # 与目标的相关性排序 target_corr corr_matrix[price].sort_values(ascendingFalse) print(target_corr)实测中和price相关性最高的往往是power、kilometer、brand这几个。但相关系数低不代表特征没用可能是非线性关系。比如kilometer和price可能是分段关系低里程区间价格下降快高里程区间价格下降慢。我的做法是对每个重要数值特征画散点图或者用分箱后的箱线图看趋势。如果发现明显的非线性模式后续可以考虑多项式特征或者分箱离散化。4. 异常值与重复值的处理策略别让脏数据毁掉模型4.1 异常值检测IQR、Z-Score与业务规则三管齐下异常值处理没有万能公式必须结合统计方法和业务规则。统计方法能发现数值上的离群点业务规则能判断这些离群点是否合理。# IQR方法检测异常值 def detect_outliers_iqr(df, col): Q1 df[col].quantile(0.25) Q3 df[col].quantile(0.75) IQR Q3 - Q1 lower Q1 - 1.5 * IQR upper Q3 1.5 * IQR outliers df[(df[col] lower) | (df[col] upper)] return outliers, lower, upper # 对price检测 outliers, lower, upper detect_outliers_iqr(train, price) print(fPrice异常值数量: {len(outliers)}) print(f正常范围: [{lower:.2f}, {upper:.2f}])但要注意价格数据里的异常值未必是错误数据。一辆限量版豪车价格远超普通车这是合理的业务现象不是数据错误。如果直接删除模型就学不到高价车的模式。我的建议是对于价格预测任务不要轻易删除高价样本而是考虑用对数变换压缩尺度或者用分位数回归、Huber损失等对异常值鲁棒的建模方法。power字段有时候会出现0或者异常大的值。0马力显然不合理可能是缺失值被填成了0。这种就要当作缺失值重新处理。4.2 重复值排查完全重复与业务重复的区别重复值分两种完全重复所有字段都一样和业务重复关键字段一样但其他字段有差异。完全重复直接drop_duplicates即可但业务重复要小心。# 完全重复 print(f完全重复行数: {train.duplicated().sum()}) # 业务重复同一辆车多次出现 key_cols [brand, model, kilometer, power] biz_dup train.duplicated(subsetkey_cols, keepFalse) print(f业务重复行数: {biz_dup.sum()})如果发现同一组关键字段对应多个不同的price可能是数据采集时的重复记录也可能是不同交易时间的价格差异。这时候不能简单去重要结合业务判断。我一般会保留第一条或者取价格均值。4.3 异常值处理后的验证分布对比与模型反馈处理完异常值后一定要做前后对比验证。最直接的方法是画处理前后的分布对比图看分布形态是否更合理。# 处理前后对比 fig, axes plt.subplots(1, 2, figsize(14, 5)) sns.histplot(train[price], bins50, axaxes[0]) axes[0].set_title(处理前) sns.histplot(train_clean[price], bins50, axaxes[1]) axes[1].set_title(处理后) plt.show()更严谨的做法是用处理后的数据跑一个基线模型看交叉验证分数是否提升。如果处理后分数反而下降说明你可能删掉了有用信息。这个反馈循环是EDA和建模之间的桥梁很多人忽略了这一步导致EDA和建模脱节。5. 特征工程前的EDA准备为后续Task铺路5.1 时间特征的拆解与业务周期识别二手车数据里通常有regDate注册日期和creatDate数据创建日期。这两个字段能衍生出车龄、使用年限等强特征。# 时间特征处理 train[regDate] pd.to_datetime(train[regDate], format%Y%m%d, errorscoerce) train[creatDate] pd.to_datetime(train[creatDate], format%Y%m%d, errorscoerce) train[car_age] (train[creatDate] - train[regDate]).dt.days / 365 # 车龄分布 sns.histplot(train[car_age].dropna(), bins50) plt.title(车龄分布) plt.show()车龄和价格的关系通常是非线性的3年内的车贬值最快之后趋于平缓。EDA阶段要确认这个模式是否存在如果存在后续可以对车龄做分箱或者样条变换。另外creatDate本身可能携带时间信息。如果数据采集跨越了较长时间不同时间段的市场行情不同这个字段可能和价格有隐含关联。但要注意如果测试集的creatDate分布和训练集差异很大直接用这个字段可能导致过拟合。5.2 高基数类别特征的编码预研model字段可能有上千个取值brand也有几十个。这类高基数类别特征怎么编码是EDA阶段就要想清楚的问题。# 高基数特征统计 for col in [brand, model]: freq train[col].value_counts() print(f{col}: 总类别数{len(freq)}, 出现次数10的类别数{(freq10).sum()})如果大量类别出现次数极少可以考虑把低频类别合并成其他。或者用目标编码但必须做平滑encoding (count * mean prior * smoothing) / (count smoothing)。这个平滑参数需要在交叉验证中调。我的经验是对于二手车这种数据brand用目标编码效果通常不错但model因为基数太高目标编码容易过拟合可以考虑用频率编码或者嵌入Embedding的方式。5.3 特征交互的初步探索哪些组合值得深挖EDA的最后一步是探索特征交互。有些特征单独看区分度一般但组合起来很强。比如brand和car_age的组合同样是3年车龄豪华品牌和普通品牌的贬值曲线完全不同。# 特征交互探索 pivot train.pivot_table(valuesprice, indexbrand, columnscar_age_bin, aggfuncmean) sns.heatmap(pivot, cmapYlOrRd) plt.title(品牌与车龄的价格交互) plt.show()这种交互分析能帮你发现哪些组合特征值得构造。但要注意EDA阶段只是探索不要在这里花太多时间构造特征那是Task3的事。EDA的目标是找到方向不是完成所有工作。6. 我在Task2实操中踩过的坑与总结的技巧第一个坑是数据类型陷阱。二手车数据里很多字段看起来是数值实际是字符串比如notRepairedDamage的-。如果不处理后面所有数值操作都会报错。我的建议是读完数据后立刻做一次df.apply(lambda x: x.dtype)检查对object类型的字段逐个确认。第二个坑是缺失值填充的连锁反应。我曾经对bodyType用众数填充结果发现填充后这个字段和目标变量的相关性反而下降了。后来才明白缺失本身可能意味着某种特殊车型填成众数反而抹掉了这个信息。正确的做法是先构造一个bodyType_isnull的指示特征再用模型预测填充或者保留缺失。第三个坑是异常值删除过度。有一次我按IQR删掉了price的异常值结果交叉验证分数掉了0.02。原因是那些高价样本虽然少但包含了重要的模式信息。后来我改用对数变换分数就回来了。第四个坑是忽略了训练集和测试集的分布一致性。EDA不能只看训练集测试集的分布也要对比。如果某个特征在测试集里的分布和训练集差异很大说明可能存在数据漂移直接用这个特征风险很高。最后分享一个实用技巧EDA阶段的所有发现都要记录下来形成一个数据洞察清单。比如price右偏需要log变换、kilometer和price非线性、bodyType缺失可能携带信息。这个清单就是Task3特征工程的路线图。没有这个清单你做到Task3时会忘记EDA阶段的发现又得重新看一遍数据。另外可视化不要追求花哨能说明问题就行。我见过有人花大量时间做交互式图表结果对建模没有任何帮助。EDA的可视化目标是快速验证假设不是做数据报告。一张清晰的箱线图比十张炫酷的3D图更有价值。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号