3.4 数据分析实战:体检报告

实战目标

  • 综合运用head()info()describe()value_counts()等工具
  • 对二手车数据集完成完整的数据体检
  • 产出结构化的"数据质量体检报告"
  • 学会发现数据问题并记录,为后续清洗做准备

一、实战背景:二手车估价预测

1.1 业务场景

二手车市场具有"一车一况"的特点,价格受品牌、车龄、里程、车况等多种因素影响。准确评估二手车价格对于买家、卖家以及二手车商都至关重要。

本次实战的数据集来自一个二手车交易平台的历史成交记录,目标是通过数据体检,了解数据的整体质量,为后续价格预测建模做好准备

1.2 数据集说明

这是一个典型的二手车价格预测数据集,包含以下字段:

字段名 类型 说明
SaleID 整数 交易ID,唯一标识
name 文本 车型名称(已脱敏)
regDate 日期 车辆注册日期(如20160101表示2016年1月1日)
model 文本 车型编码(已脱敏)
brand 文本 品牌编码(已脱敏)
bodyType 整数 车身类型(1-10编码)
fuelType 整数 燃油类型(1-6编码)
gearbox 整数 变速箱类型(0=手动,1=自动)
power 整数 发动机功率(单位:马力)
kilometer 整数 行驶里程(单位:万公里)
notRepairedDamage 文本 是否有未修复损伤(0=否,1=是,-表示未知)
regionCode 整数 地区编码
seller 整数 卖家类型(0=个人,1=商家)
offerType 整数 报价类型
creatDate 日期 上线时间(开始售卖日期)
price 整数 交易价格(目标变量,单位:元)
v_0 ~ v_14 浮点数 15个匿名特征(已脱敏的统计特征)

注:数据来自阿里云天池大赛"二手车交易价格预测"赛题,共包含15万条训练样本。

二、体检执行过程

第1步:head() ------ 看一眼数据长什么样

操作 :读取数据后,立即使用head()查看前5行。

输出预览

SaleID name regDate model brand kilometer price notRepairedDamage
0 1 147 20040402 40.0 1 15.0 3500 0.0
1 2 66 20020620 1.0 3 12.5 4500 0.0
2 3 101 20030809 8.0 5 18.0 5500 -
3 4 31 20040402 26.0 0 9.8 3800 -
4 5 255 20030315 21.0 7 22.0 4000 1.0

发现的问题

观察 意味着什么 记录为
price值在3500-5500之间 价格单位是元,几千万把块的车价正常 正常
regDate是20040402这样的数字 日期被存成了整数,不是真正的日期格式 ⚠️ 需要转换
notRepairedDamage显示为- -表示缺失值,不是标准的NaN ⚠️ 需要处理
namemodelbrand是脱敏编码 无法得知具体品牌名称,但可以用于分组分析 正常

体检报告记录

【发现1】日期字段regDatecreatDate存储为整数格式(如20040402),需要转换为标准日期类型才能计算车龄。

【发现2】notRepairedDamage字段中,缺失值用-表示,而非标准空值,需要统一转换。

第2步:info() ------ 全身体检

操作 :使用info()了解数据集的整体情况------行数、列数、每列类型、缺失情况、内存占用。

输出解读(基于15万条数据):

复制代码
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 30 columns):
 #   Column             Non-Null Count   Dtype  
---  ------             --------------   -----  
 0   SaleID             150000 non-null  int64  
 1   name               150000 non-null  int64  
 2   regDate            150000 non-null  int64  
 3   model              149999 non-null  float64
 4   brand              150000 non-null  int64  
 5   bodyType           145000 non-null  float64
 6   fuelType           141000 non-null  float64
 7   gearbox            144000 non-null  float64
 8   power              150000 non-null  int64  
 9   kilometer          150000 non-null  float64
 10  notRepairedDamage  120000 non-null  object 
 11  regionCode         150000 non-null  int64  
 12  seller             150000 non-null  int64  
 13  offerType          150000 non-null  int64  
 14  creatDate          150000 non-null  int64  
 15  price              150000 non-null  int64  
 16  v_0                150000 non-null  float64
 ...(省略v_1到v_14,共15个匿名特征)
dtypes: float64(18), int64(12), object(1)
memory usage: 34.4 MB

从输出中发现的问题

观察 数据 意味着什么 严重程度
总行数15万,列数30 shape=(150000, 30) 中等规模数据集,常规分析没问题 ✅ 正常
model缺失1条 149999/150000 缺失极少,可忽略 ✅ 可忽略
bodyType缺失5000条 145000/150000 缺失约3.3% ⚠️ 需关注
fuelType缺失9000条 141000/150000 缺失约6% ⚠️ 需处理
gearbox缺失6000条 144000/150000 缺失约4% ⚠️ 需关注
notRepairedDamage缺失3万条 120000/150000 缺失20% 🔴 重要
regDatecreatDate是int64 应是日期类型 需要转换 ⚠️ 需转换
notRepairedDamage是object类型 文本类型 需要检查取值情况 ⚠️ 需检查
内存占用34.4 MB 15万行30列 内存很小,无压力 ✅ 正常

体检报告记录

【发现3】数据集共150,000行、30列,内存占用约34MB,规模适中。

【发现4】缺失情况汇总:

  • bodyType:缺失约5,000条(3.3%)
  • fuelType:缺失约9,000条(6%)
  • gearbox:缺失约6,000条(4%)
  • notRepairedDamage:缺失约30,000条(20%)------ 最严重

【发现5】数据类型问题:

  • regDatecreatDate应为日期,当前是整数
  • notRepairedDamage应为数值(0/1),当前是文本

第3步:dtypes ------ 确认类型问题

操作:重点检查可疑列的类型,确认需要转换的字段。

关键列的当前类型

列名 当前类型 应该是什么 需要转换
regDate int64 datetime ✅ 是
creatDate int64 datetime ✅ 是
notRepairedDamage object(文本) int/float(0/1) ✅ 是
price int64 int64(目标变量) ❌ 否
kilometer float64 float64 ❌ 否

体检报告记录

【发现6】类型转换清单:

  • regDate:int64 → datetime(用于计算车龄)
  • creatDate:int64 → datetime(用于计算售卖周期)
  • notRepairedDamage:object → int(将"-"转为NaN,再转数值)

第4步:describe() ------ 数值列统计摘要

操作 :对所有数值列进行描述性统计,重点检查price(目标变量)和关键特征的分布。

数值列统计输出(部分重要列)

统计指标 price(元) power(马力) kilometer(万公里) bodyType fuelType
count 150,000 150,000 150,000 145,000 141,000
mean 5,923 120 12.4 2.8 3.5
std 7,505 240 10.5 1.9 2.1
min 11 0 0.5 0 0
25% 1,500 80 5.0 1 2
50% 3,500 110 9.0 3 4
75% 7,500 150 18.0 4 5
max 99,999 12,000 99.0 7 6

从输出中发现的问题

观察 数值 意味着什么 处理建议
price最小值=11元 11元 明显异常,车不可能只卖11元 🔴 异常值,需检查并处理
price最大值=99,999 约10万元 在合理范围内(普通二手车) ✅ 正常
price均值=5,923,中位数=3,500 均值>中位数 数据右偏,存在高价车拉高均值 正常现象,之后建模可考虑log变换
power最大值=12,000马力 12,000 货车也到不了这个数,明显异常 🔴 异常值,需检查处理
power最小值=0 0马力 不可能,至少应该有功率 🔴 异常值,需处理
power均值=120,中位数=110 均值>中位数 右偏,有异常高值拉高均值 同异常值问题
kilometer最大值=99万公里 99万公里 车子开不到这个数(地球到月球?),可能数据错误 🔴 异常值,需检查
bodyType有缺失 count=145000 缺失5,000条 后续需填充或删除
fuelType有缺失 count=141000 缺失9,000条 后续需填充或删除

体检报告记录

【发现7】数值列异常值汇总:

  • price:最小值11元(异常),最大值99,999元(正常)
  • power:存在0马力和12,000马力等明显异常值,需用品牌/车型中位数替换
  • kilometer:存在99万公里等异常高值,需核实处理

【发现8】price分布:均值(5,923) > 中位数(3,500),数据右偏,存在高价车,后续建模时可考虑对price做log变换。

【发现9】缺失补充:bodyTypefuelType有3%-6%的缺失,需决定填充或删除策略。

第5步:value_counts() + unique() ------ 分类变量检查

操作:重点检查分类/文本列的唯一值和频次分布,发现脏数据。

5.1 检查 notRepairedDamage(是否有未修复损伤)
python 复制代码
df['notRepairedDamage'].value_counts(dropna=False)

输出

取值 数量 说明
0.0 90,000 无未修复损伤
1.0 30,000 有未修复损伤
- 30,000 -表示的缺失值

发现的问题

  • 缺失值用-而非标准NaN表示
  • 约20%的数据缺失

处理建议 :将-转换为NaN,后续统一处理缺失值。

5.2 检查 gearbox(变速箱类型)
python 复制代码
df['gearbox'].value_counts()

输出

取值 数量 说明
0 80,000 手动挡
1 64,000 自动挡
(缺失) 6,000 未记录

发现:取值规范,只有0和1,缺失约4%。

5.3 检查 seller(卖家类型)
python 复制代码
df['seller'].value_counts()

输出

取值 数量 说明
0 149,900 个人卖家
1 100 商家卖家

发现:类别极度不平衡!商家卖家只占0.07%。

业务洞察:数据集中绝大多数是个人卖家,商家样本极少,后续建模时需注意。

5.4 检查 offerType(报价类型)
python 复制代码
df['offerType'].value_counts()

输出

取值 数量 说明
0 150,000 全部是0

发现:整列只有一个值(全0)------ 该列完全没有信息量!

处理建议:这列对预测没有任何帮助,可以直接删除。

体检报告记录

【发现10】分类变量检查结果:

  • notRepairedDamage:缺失值用-表示,需转换为NaN
  • seller:类别极度不平衡(商家仅占0.07%),建模时需注意
  • offerType:全列只有一个值(0),建议删除------无信息量

第6步:特殊问题排查 ------ 脏数据与业务合理性

6.1 匿名特征(v_0 到 v_14)

这15个特征是已经脱敏的匿名特征,由数据提供方预处理过,通常不需要额外处理。但它们可能包含重要信息------在类似的数据集中,v_3等匿名特征往往具有较高的特征重要性。

处理建议:保留这些特征,后续建模时让模型自动判断重要性。

6.2 特征相关性预判(针对建模阶段)

在后续建模时,需要注意以下问题:

问题类型 涉及的字段 处理方式
多重共线性 匿名特征之间可能存在高度相关 建模时检查VIF,考虑降维
ID列 SaleID 唯一标识,与价格无关,应删除
车龄构造 regDate和交易时间 可构造车龄 = 交易年份 - 注册年份作为新特征
价格里程比 pricekilometer 可构造price_per_km特征

三、完整体检报告

经过以上6步检查,产出完整的数据质量体检报告

markdown 复制代码
# 二手车估价预测数据集 ------ 数据质量体检报告

## 一、数据概况
- 总行数:150,000
- 总列数:30
- 内存占用:约34 MB
- 数据集规模:中等,常规分析无压力

## 二、数据质量检查结果

### 2.1 缺失值情况
| 字段 | 缺失数量 | 缺失比例 | 处理建议 |
|------|----------|----------|----------|
| notRepairedDamage | 30,000 | 20.0% | 🔴 高缺失,需填充或分析缺失原因 |
| fuelType | 9,000 | 6.0% | ⚠️ 中缺失,用众数填充 |
| gearbox | 6,000 | 4.0% | ⚠️ 中缺失,用众数填充 |
| bodyType | 5,000 | 3.3% | ⚠️ 中缺失,用众数填充 |
| model | 1 | 0.0007% | ✅ 极小缺失,可删除该行 |

### 2.2 数据类型问题
| 字段 | 当前类型 | 目标类型 | 处理方式 |
|------|----------|----------|----------|
| regDate | int64 | datetime | 转换为日期格式 |
| creatDate | int64 | datetime | 转换为日期格式 |
| notRepairedDamage | object | float | 先替换"-"为NaN,再转数值 |

### 2.3 异常值情况
| 字段 | 异常值 | 业务合理性 | 处理建议 |
|------|--------|-----------|----------|
| price | 11元 | ❌ 不合理 | 检查是否为数据错误,考虑删除或用中位数替换 |
| power | 0马力 | ❌ 不合理 | 用同品牌/车型中位数替换 |
| power | 12,000马力 | ❌ 不合理 | 用同品牌/车型中位数替换 |
| kilometer | 99万公里 | ❌ 不合理 | 设定合理上限(如50万),超出视为异常 |

### 2.4 分类变量脏数据
| 字段 | 问题 | 处理方式 |
|------|------|----------|
| notRepairedDamage | 用"-"表示缺失 | 替换为NaN |
| seller | 类别极度不平衡(商家0.07%) | 建模时注意,或合并为"非个人" |
| offerType | 全列只有1个值 | ✅ 直接删除该列 |
| model | 取值数量极多 | 正常,后续可用频次编码 |

### 2.5 需要删除的列
| 列名 | 原因 |
|------|------|
| offerType | 所有值相同,无预测能力 |
| SaleID | 唯一标识,与价格无关 |

## 三、核心结论

✅ **可以继续分析的条件**:完成上述清洗后,数据集质量良好

⚠️ **重点关注的清洗项**:
1. `notRepairedDamage`缺失20%,需决定处理策略
2. `power`和`price`存在明显异常值,需清洗
3. `offerType`直接删除

💡 **后续建模建议**:
- `price`分布右偏,可考虑log变换
- `seller`类别极不平衡,建模时需关注少数类
- 可构造车龄、价格里程比等组合特征
- 匿名特征v_3等往往有高重要性,不要轻易删除

## 四、后续步骤
1. 根据本报告进行数据清洗
2. 构造新特征(车龄、价格里程比等)
3. 进行EDA可视化分析
4. 建立价格预测模型

四、如何向AI描述体检需求

在整个体检过程中,你不需要记住任何代码,只需要这样告诉AI:

你想要做的事 你应该这样告诉AI
开始体检 "帮我读取这个二手车数据集,做一份完整的数据体检报告"
查看数据样子 "显示前10行,让我看看数据长什么样"
了解整体情况 "用info查看数据集的行数、列数、类型和缺失情况"
检查数值分布 "对数值列做描述性统计,特别关注price、power、kilometer有没有异常值"
检查分类变量 "检查notRepairedDamage字段的取值分布,看看有没有不规范的地方"
发现无信息列 "检查所有列,看看有没有整列值都一样的、对预测没用的列"
产整体报告 "整理所有发现,生成一份结构化的数据质量体检报告"

五、本章总结

体检流程回顾

复制代码
第1步:head()        → 看一眼数据样子,发现明显的格式问题
第2步:info()        → 了解全貌:行数列数、类型、缺失、内存
第3步:dtypes        → 确认类型问题,列出需转换的字段
第4步:describe()    → 检查数值分布,发现异常值
第5步:value_counts()→ 检查分类变量,发现脏数据和无信息列
第6步:整理报告      → 汇总所有发现,形成体检报告

本次体检的核心发现

问题类型 具体问题 处理方向
数据类型 日期存为整数、缺失值用- 类型转换
缺失值 up to 20%缺失(notRepairedDamage) 需要填充策略
异常值 price(11元)、power(0/12000) 清洗或替换
无信息列 offerType全为0 删除
类别不平衡 seller商家仅0.07% 建模时注意

核心心法

"体检不是目的,发现问题才是。一份好的体检报告,能让后续的清洗和建模事半功倍。"

体检报告的价值在于:

  1. 提前预警:知道哪里有问题,避免分析时被"坑"
  2. 指导清洗:知道要做什么,而不是盲目操作
  3. 记录决策:为什么删这列、为什么填那个值------都有据可查
  4. 沟通工具:可以和团队分享数据的真实质量状况

六、思考题

  1. 本次体检发现offerType整列只有一个值。为什么这种列对预测模型没有用?还有哪些类型的列应该考虑删除?

  2. price的最小值是11元,业务上显然不合理。你打算怎么处理这些异常值?删除还是替换?用中位数还是均值?为什么?

  3. notRepairedDamage有20%的缺失。如果你决定填充这一列,应该用什么值填充------均值、中位数、众数?为什么?

  4. seller列中,商家卖家只占0.07%。如果直接使用这个特征建模,可能会有什么问题?有什么办法可以缓解?

  5. power列中既出现了0马力,也出现了12,000马力。你觉得用"同品牌中位数"替换是不是一个好方案?为什么?

下一节预告:第4章 数据清洗 ------ 洗完"体检"发现的问题,下一步就是"治病"。我们将逐一处理缺失值、异常值、类型转换、删除冗余列......把脏数据变成干净数据。

相关推荐
70asunflower1 小时前
数据分析实战教程:从思维到落地
数据挖掘·数据分析
babe小鑫2 小时前
财务经理学数据分析可行性分析
信息可视化·数据挖掘·数据分析
babe小鑫2 小时前
2026市场投放学数据分析的价值分析
数据挖掘·数据分析
clarance20152 小时前
基于NLP的BI工具DataFocus实战:从自然语言查询到智能数据分析
人工智能·经验分享·自然语言处理·数据分析
源码之家3 小时前
计算机毕业设计:Python基于知识图谱与深度学习的医疗智能问答系统 Django框架 Bert模型 深度学习 知识图谱 大模型(建议收藏)✅
python·深度学习·机器学习·数据分析·flask·知识图谱·课程设计
yzx9910133 小时前
软件脚本定制开发:从需求到交付的技术实战指南
大数据·人工智能·数据挖掘
条俐开水喉3 小时前
U位资产×大数据分析:数据驱动的容量优化与风险防控
数据分析·u位资产管理
YangYang9YangYan3 小时前
2026营销新人学习数据分析的应用
学习·数据挖掘·数据分析
Sharewinfo_BJ4 小时前
上北智信携“智信BI”闪耀2026上海全球数据周,以灵活部署方案赋能企业数据价值跃升
大数据·人工智能·ai·数据挖掘·微软·powerbi