ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

二手车数据分析与可视化实战:从数据清洗到深度洞察

二手车数据分析与可视化实战:从数据清洗到深度洞察 1. 项目概述与核心价值看到这个标题很多参加过数学建模比赛或者对数据分析感兴趣的朋友可能会心一笑。没错这正是2021年MathorCup高校数学建模挑战赛A赛道的核心任务之一。这个项目标题——“1 数据分析及可视化”——看似简单实则是一个数据科学项目从混沌走向清晰的关键起点。它不仅仅是比赛要求的一个步骤更是任何一位数据从业者面对新数据集时必须掌握的基本功。无论是评估二手车价格还是预测股票走势、分析用户行为第一步永远是理解你的数据。这个项目的核心价值在于它要求参赛者从一个包含数十个字段、可能数万条记录的原始二手车交易数据集中通过系统性的分析和可视化手段挖掘出影响车辆价格的关键因素并形成对数据的直观认知。这不仅仅是画几个图表那么简单它涉及到数据清洗、特征理解、分布探索、相关性分析以及最终将复杂信息转化为人类可快速理解的视觉语言。对于新手而言这是一个绝佳的实战案例能让你完整走一遍数据分析的标准流程对于有经验的分析师它则是一次对基础功的重新审视和提炼。接下来我将结合那次比赛的经验和后续的实践思考拆解如何高效、深入地进行这一步并分享那些在标准教程里不会写的“坑”与技巧。2. 数据理解与预处理从原始数据到分析就绪拿到数据后的第一反应不应该是直接导入绘图库开始画图。仓促的可视化只会产生一堆无意义的垃圾图表。真正有价值的数据分析始于对数据本身的深刻理解。2.1 数据初窥与质量评估通常比赛或项目提供的数据集是CSV或Excel格式。第一步是用pandas进行加载和快速浏览。import pandas as pd import numpy as np # 加载数据 df pd.read_csv(used_cars_data.csv) # 查看数据概览 print(df.info()) print(df.head()) print(df.describe(includeall))df.info()会告诉你数据维度、每列的非空值数量及数据类型。这是发现数据缺失问题的第一道关卡。在二手车数据中常见的问题包括registration_year注册年份有未来日期如2025年power功率字段有异常大值如99999notRepairedDamage是否有未修复损坏字段中“-”代表缺失值而非字符串。注意df.describe(includeall’)对于对象类型字符串的列也会显示频数最高的值这能快速发现数据录入错误比如品牌brand列里可能混入了型号model信息。2.2 数据清洗实战与逻辑清洗不是盲目删除每一步都需要业务逻辑支撑。异常值处理对于价格price我们通常会查看分布。如果存在价格为0或1的极端异常记录需要结合业务判断。在二手车场景价格过低可能是促销、数据错误或“背户车”等特殊情况。一个常见的策略是使用分位数进行截断。# 查看价格分布 import matplotlib.pyplot as plt plt.figure(figsize(10,6)) plt.hist(df[price], bins50, edgecolorblack) plt.title(Distribution of Car Price) plt.xlabel(Price) plt.ylabel(Frequency) plt.show() # 使用IQR法则处理极端异常值 Q1 df[price].quantile(0.25) Q3 df[price].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR df_cleaned df[(df[price] lower_bound) (df[price] upper_bound)].copy()但要注意IQR法可能会误伤一些真实的高端二手车。更好的方法是结合品牌和车型进行分析例如单独分析宝马7系的价格分布再判断其异常值。缺失值处理对于数值型特征如odometer里程数若缺失比例不高5%可用中位数或同品牌车型的平均里程填充。对于分类特征如fuelType燃油类型若缺失可单独设为“Unknown”作为一个类别因为缺失本身可能包含信息例如老旧车型信息不全。# 分类特征缺失值处理 df_cleaned[fuelType].fillna(Unknown, inplaceTrue) # 数值特征缺失值处理 - 按品牌分组填充中位数 df_cleaned[odometer] df_cleaned.groupby(brand)[odometer].transform(lambda x: x.fillna(x.median()))特征工程准备原始数据中的registrationYear注册年份可以直接用来计算车龄car_age。这是后续分析中一个极强的特征。current_year 2021 # 以比赛年份为基准 df_cleaned[car_age] current_year - df_cleaned[registrationYear] # 处理注册年份异常值如大于当前年份或过于久远 df_cleaned df_cleaned[(df_cleaned[car_age] 0) (df_cleaned[car_age] 50)]实操心得数据清洗时务必保留原始数据的副本所有清洗操作在副本上进行。每进行一步重要的清洗如删除大量行都记录下删除的逻辑和数量这在撰写论文或报告时是至关重要的过程描述也能帮助你在结果出现偏差时快速回溯。3. 单变量分析与可视化洞察每一个特征的秘密清洗后的数据我们需要逐个特征进行剖析理解其分布、集中趋势和离散程度。这是可视化开始大显身手的地方。3.1 数值型特征分析核心数值特征price价格、odometer里程、car_age车龄、power功率。分布直方图与密度图这是看数据分布形态最基本也最有效的方法。重点关注分布是正态、偏态左偏或右偏还是多峰。fig, axes plt.subplots(2, 2, figsize(14, 10)) numerical_features [price, odometer, car_age, power] for ax, feature in zip(axes.flat, numerical_features): ax.hist(df_cleaned[feature].dropna(), bins50, alpha0.7, edgecolorblack, densityTrue) df_cleaned[feature].plot(kindkde, axax, secondary_yTrue, colorred, lw2) # 叠加密度曲线 ax.set_title(fDistribution of {feature}) ax.set_xlabel(feature) ax.set_ylabel(Density) plt.tight_layout() plt.show()从图中你能发现什么价格和里程通常呈现严重的右偏分布长尾在右侧即大部分车价格和里程集中在较低区间少数车非常贵或里程极高。车龄分布可能呈现近似正态或略微右偏。功率分布可能在高功率区间出现异常峰值需要检查是否是数据录入问题如统一填了某个默认值。箱线图用于识别异常值和观察数据扩散情况。它能清晰展示中位数、上下四分位数以及“触须”外的离散点。plt.figure(figsize(12, 6)) df_cleaned[[price, odometer, car_age]].boxplot() plt.title(Boxplot of Key Numerical Features) plt.yscale(log) # 如果数据量级差异大考虑使用对数坐标 plt.show()注意箱线图对异常值非常敏感。在二手车数据中被箱线图标记为“异常值”的点未必是错误数据它们可能代表了真实的豪华车或特殊车型。切勿不假思索地删除所有箱线图外的点。3.2 分类型特征分析核心分类特征brand品牌、model车型、fuelType燃油类型、vehicleType车辆类型、gearbox变速箱。柱状图展示每个类别的数量分布。这是理解数据构成的基础。top_10_brands df_cleaned[brand].value_counts().head(10) plt.figure(figsize(12,6)) top_10_brands.plot(kindbar) plt.title(Top 10 Car Brands by Count) plt.xlabel(Brand) plt.ylabel(Number of Listings) plt.xticks(rotation45) plt.tight_layout() plt.show()通常会发现品牌分布极度不均衡大众、宝马、奥迪等德系品牌占据主流。对于长尾部分的小众品牌在后续建模时可能需要考虑归为“其他”类别。饼图慎用仅当类别很少5个且你想强调占比时使用例如gearbox手动 vs 自动。对于像brand这种类别多的特征饼图会变得难以阅读。可视化工具选择心得matplotlib是基础但定制化较繁琐。在此阶段我强烈推荐使用seaborn库它在matplotlib基础上封装默认样式更美观且多变量关系绘图功能强大。plotly或pyecharts适合制作交互式图表用于探索性分析非常棒但若最终输出静态报告或论文需注意其兼容性。4. 多变量关系探索发现特征间的故事单变量分析是基础但数据真正的价值藏在特征与特征、特征与目标变量的关系之中。4.1 数值型 vs 数值型散点图与相关矩阵散点图观察两个数值变量间关系最直观的工具。重点是看price目标变量与其他特征的关系。plt.figure(figsize(10,6)) plt.scatter(df_cleaned[car_age], df_cleaned[price], alpha0.3, s10) # alpha透明度避免重叠 plt.title(Car Price vs. Car Age) plt.xlabel(Car Age (years)) plt.ylabel(Price) plt.grid(True, linestyle--, alpha0.5) plt.show()你预期会看到一个明显的负相关趋势车龄越大价格越低。但散点图能告诉你更多趋势线是否是线性的是否存在价格不随车龄下降的“保值神车”集群数据点的分散程度如何相关矩阵热力图快速查看所有数值特征间的线性相关程度。import seaborn as sns corr_matrix df_cleaned[numerical_features].corr() plt.figure(figsize(8,6)) sns.heatmap(corr_matrix, annotTrue, cmapcoolwarm, center0, squareTrue) plt.title(Correlation Matrix of Numerical Features) plt.show()解读price与car_age、odometer预期呈负相关系数为负。power与price可能呈正相关。同时要关注car_age和odometer之间可能存在的强正相关车越老跑得越多这提示我们可能存在多重共线性问题在后续线性回归建模时需要留意。4.2 数值型 vs 分类型分组统计与可视化这是挖掘不同群体差异的关键。例如不同品牌的平均价格差异巨大。分组箱线图比较不同类别下数值变量的分布差异。plt.figure(figsize(14,6)) # 选取数量最多的前5个品牌进行对比避免图形过于拥挤 top_brands df_cleaned[brand].value_counts().index[:5] df_top_brands df_cleaned[df_cleaned[brand].isin(top_brands)] sns.boxplot(xbrand, yprice, datadf_top_brands) plt.title(Price Distribution across Top 5 Brands) plt.yscale(log) # 价格差异大使用对数坐标让图形更清晰 plt.xticks(rotation45) plt.tight_layout() plt.show()这个图能清晰展示品牌溢价。保时捷的箱体整体位置远高于大众且其价格区间箱体高度也更宽。聚合条形图展示不同分类下的数值均值。avg_price_by_fuel df_cleaned.groupby(fuelType)[price].mean().sort_values(ascendingFalse) avg_price_by_fuel.plot(kindbar, figsize(10,6)) plt.title(Average Price by Fuel Type) plt.xlabel(Fuel Type) plt.ylabel(Average Price) plt.tight_layout() plt.show()你可能会发现电动electric或插电混动hybrid车型的平均价格高于汽油petrol和柴油diesel车这反映了市场趋势和技术附加值。4.3 两个分类型特征交叉表与堆叠柱状图分析例如vehicleType和gearbox之间的关系。SUV车型中自动挡的比例是否高于小型车small carcross_tab pd.crosstab(df_cleaned[vehicleType], df_cleaned[gearbox], normalizeindex) # 行百分比 print(cross_tab) # 可视化 cross_tab.plot(kindbar, stackedTrue, figsize(12,6)) plt.title(Gearbox Distribution across Vehicle Types) plt.xlabel(Vehicle Type) plt.ylabel(Proportion) plt.legend(titleGearbox) plt.tight_layout() plt.show()实操心得在多变量探索时不要只满足于画图。对于重要的发现要用分组统计量如groupby().agg([mean’, ‘median’, ‘count’, ‘std’])进行量化确认并把关键数字记下来。例如“豪华品牌BBA的三年保值率平均比主流品牌高15%”这样的结论比单纯的图表更有说服力。5. 高阶可视化与深度洞察基础图表足以覆盖80%的分析需求但一些高级技巧能让你的分析报告脱颖而出并揭示更深层的信息。5.1 散点图矩阵当你想同时观察多个数值变量两两之间的关系时散点图矩阵Pairs Plot是神器。seaborn的pairplot函数可以轻松实现并且对角线可以显示每个变量的分布直方图或密度图。# 选取关键的几个数值特征 plot_features [price, car_age, odometer, power] sns.pairplot(df_cleaned[plot_features].sample(1000, random_state42)) # 数据量大时抽样 plt.suptitle(Pairs Plot of Key Numerical Features, y1.02) plt.show()通过这个图你可以一次性看到所有二元关系快速发现是否有非线性关系或明显的群体划分。5.2 带回归线的散点图在散点图基础上添加趋势线线性或多项式可以更直观地判断关系强度和方向。sns.lmplot(xcar_age, yprice, datadf_cleaned.sample(1000, random_state42), scatter_kws{alpha:0.3, s:10}, line_kws{color: red}) plt.title(Car Price vs. Age with Linear Regression Fit) plt.xlabel(Car Age) plt.ylabel(Price) plt.show()那条红色的线就是数据拟合出的线性趋势。你可以清楚地看到点围绕趋势线的离散程度这反映了车龄对价格解释力的强弱。5.3 分面网格如果你想在控制一个变量的情况下观察另外两个变量的关系分面网格Facet Grid是终极工具。例如我们想分别观察“手动挡”和“自动挡”车辆中价格与车龄的关系。g sns.FacetGrid(df_cleaned.sample(2000, random_state42), colgearbox, height5, aspect1.2) g.map_dataframe(sns.scatterplot, xcar_age, yprice, alpha0.5) g.map_dataframe(sns.regplot, xcar_age, yprice, scatterFalse, colorred) # 为每个分面添加回归线 g.set_axis_labels(Car Age, Price) g.set_titles(col_templateGearbox: {col_name}) plt.suptitle(Price vs. Car Age Faceted by Gearbox Type, y1.05) plt.show()这个图能清晰地告诉你变速箱类型是否改变了价格与车龄之间的关系模式。也许自动挡车辆的贬值曲线更平缓避坑技巧进行高阶可视化尤其是涉及大量数据点绘图时务必进行抽样。绘制数万个点的散点图不仅渲染极慢而且会因为点的大量重叠导致图形变成一片毫无信息量的色块称为“过绘制”。通常随机抽取1000-5000个样本点就足以反映整体的关系模式。6. 从可视化到故事构建分析报告数据分析与可视化的最终目的不是生产图表而是形成有逻辑、有洞见的叙述。在MathorCup这类比赛中这部分内容直接决定了你论文“问题分析”部分的深度。核心发现总结价格分布二手车价格呈右偏分布中位数在X万元存在少量极高价值的豪华车拉高了均价。关键影响因素强负相关车龄、行驶里程与价格呈显著负相关是决定价格的基础因素。品牌效应品牌是价格的重要分层器豪华品牌如奔驰、宝马存在明显的溢价区间。特征组合车辆类型SUV价格高于轿车、燃油类型新能源车溢价、变速箱自动挡普遍更贵等共同作用。异常与特殊群体发现了车龄长但价格依然坚挺的“经典车”群体以及里程极低的高龄“收藏车”群体这些是线性模型可能难以处理的个案。可视化报告编排逻辑第一部分数据全景。用单变量图表直方图、箱线图、柱状图展示核心特征的分布让读者对数据构成有基本印象。第二部分关系挖掘。用散点图、相关热力图、分组箱线图揭示特征与价格、特征与特征之间的关联。这是报告的核心。第三部分深度洞察。用分面网格、交互式图表如果允许深入探讨在特定条件如某个品牌、某种车型下关系的变与不变。所有图表必须配有清晰的标题、坐标轴标签、图例如果需要并在正文中对图表的关键发现进行文字描述和解读不能只扔一张图上去。为建模做准备 通过可视化分析你已经对特征有了深刻理解这直接指导后续的建模步骤特征选择剔除与目标变量无关或相关性极弱的特征。特征工程发现了“车龄”和“里程”的交互作用可能重要可以考虑创建age_mileage_ratio龄程比新特征。模型选择如果发现价格与关键特征存在明显的非线性关系如车龄在0-3年贬值快3-10年贬值慢那么决策树、梯度提升树等非线性模型可能比线性回归更合适。处理策略对识别出的异常值群体决定是在建模中保留作为特殊类别还是需要单独处理。在整个过程中我个人的体会是数据分析与可视化阶段花的时间至少应占整个项目周期的40%。这一步做得越扎实你对数据的“感觉”就越准后续的建模和调优就越有方向避免在错误的方向上浪费大量时间。那些看似枯燥的分布图和散点图正是你与数据对话的语言读懂它们你就掌握了从数据中提炼价值的钥匙。最后一个小建议在绘制每张图之前先问自己“我想通过这张图回答一个什么问题”带着问题去探索你的分析将更具目的性和冲击力。
返回列表