4. 数据剖析与探索:用Pandas看清数据全貌
拿到一份设备目录数据,别急着动手清洗。我个人的习惯是——先花10分钟做一次「数据体检」。说白了,就是搞清楚数据长什么样、哪里有问题、哪些字段值得深挖。这一步做扎实了,后面的清洗工作才能有的放矢。
今天我们就用Pandas这把瑞士军刀,把数据剖析这件事拆开揉碎了讲。我会带着你从概览、统计描述、缺失值、唯一值四个维度,把设备目录数据摸个透。
核心思路:数据剖析不是炫技,而是为了回答三个问题——数据完整吗?数据合理吗?数据能用吗?
4.1 数据概览:先看头,再看尾
我每次拿到新数据集,第一件事就是敲 df.head()。这就像翻开一本书的目录,能快速知道数据有哪些列、每列大概长什么样。
import pandas as pd
# 读取设备目录数据
df = pd.read_csv('device_catalog.csv')
# 查看前5行
print(df.head())
# 查看后5行
print(df.tail())
# 查看数据集的形状(行数, 列数)
print(f"数据集形状: {df.shape}")
# 查看每列的数据类型
print(df.dtypes)
嗯,这里要注意——head() 默认显示5行,但你可以传参 head(10) 看更多。我个人习惯先看10行,因为5行有时候样本量太小,看不出规律。
我在项目中遇到过一件事:有次看 head() 觉得数据挺干净,结果 tail() 一翻,最后100行全是空值。你想想看,如果只看开头就下结论,后面得踩多大坑?
小技巧:用 df.sample(5) 随机抽取5行,能避免数据排序带来的「幸存者偏差」。
4.2 统计描述:用 describe() 读懂数值
describe() 是我最常用的函数之一。它自动计算数值列的均值、标准差、四分位数等统计量。说白了,就是给数据做一次「体检报告」。
# 默认只统计数值列
print(df.describe())
# 如果想看所有列(包括对象类型),加参数
print(df.describe(include='all'))
输出结果长这样(以设备价格字段为例):
| 统计量 | 价格(元) | 库存数量 | 设备重量(kg) |
|---|---|---|---|
| count | 980 | 1000 | 850 |
| mean | 12500.50 | 45.20 | 12.30 |
| std | 3200.80 | 18.50 | 5.60 |
| min | 0.00 | 0 | 0.50 |
| 25% | 9800.00 | 30 | 8.20 |
| 50% | 12000.00 | 42 | 11.80 |
| 75% | 15000.00 | 58 | 15.40 |
| max | 999999.00 | 200 | 50.00 |
看到这个表格,你发现了什么?
- count 不一致:价格只有980条,库存有1000条——说明价格字段有20个缺失值。
- min 为0:价格最小值是0,这合理吗?设备价格不可能免费吧?
- max 异常:价格最大值是999999,明显是异常值。我遇到过类似情况,后来发现是录入时多按了几个9。
注意:describe() 只能帮你「发现」异常,不能「解释」异常。看到不合理的数据,一定要结合业务逻辑去判断。
4.3 缺失值统计:数据完整度的照妖镜
缺失值处理是数据清洗的重头戏。第一步,先搞清楚哪些列有缺失、缺了多少。
# 统计每列的缺失值数量
missing_count = df.isnull().sum()
print(missing_count)
# 计算缺失比例
missing_ratio = df.isnull().sum() / len(df) * 100
print(missing_ratio)
# 把结果拼在一起看
missing_df = pd.DataFrame({
'缺失数量': missing_count,
'缺失比例(%)': missing_ratio
})
print(missing_df[missing_df['缺失数量'] > 0])
输出示例:
| 字段名 | 缺失数量 | 缺失比例(%) |
|---|---|---|
| 设备名称 | 5 | 0.5 |
| 设备型号 | 120 | 12.0 |
| 价格 | 20 | 2.0 |
| 生产日期 | 350 | 35.0 |
看到这个结果,我的第一反应是:
- 设备名称缺失0.5%,可以忽略或直接填充「未知设备」。
- 设备型号缺失12%,需要找业务方确认是否有默认值。
- 生产日期缺失35%——这问题大了。我曾经处理过类似的数据,后来发现是因为老设备没有录入生产日期。这种情况下,直接删除会损失太多数据,我建议标记为「未知」或单独分组处理。
经验之谈:缺失比例低于5%的字段,可以直接删除缺失行或用均值/众数填充。超过30%的字段,就要慎重了——删除会损失大量信息,填充又可能引入偏差。
4.4 唯一值分析:字段的区分度与质量
唯一值分析能告诉你两件事:字段的区分度好不好,以及有没有脏数据。
# 查看每列的唯一值数量
unique_counts = df.nunique()
print(unique_counts)
# 查看某个字段的具体唯一值
print(df['设备类型'].unique())
# 统计唯一值的频次
print(df['设备类型'].value_counts())
举个例子,假设「设备类型」字段的结果是这样的:
设备类型
服务器 450
交换机 320
路由器 280
路由器 1 # 注意:多了一个空格
Server 5 # 中英文混用
服务器 3 # 全角空格
Name: 设备类型, dtype: int64
你看,明明只有3种设备类型,却出现了6个不同的值。这就是典型的脏数据问题——空格、大小写、中英文混用。
我遇到过更离谱的:有个字段叫「设备状态」,理论上只有「正常」「维修」「报废」三种,结果 unique() 一看,有17种!仔细排查发现,有人填了「正常使用」「已报废」「维修中」等等。你想想看,如果不做唯一值分析,这种问题根本发现不了。
核心结论:唯一值分析是数据质量的第一道防线。如果唯一值数量远大于业务预期,大概率是数据录入不规范。
4.5 实战:把四步走串起来
好了,理论讲完了。我们用一个完整的例子,把今天学的四步走串起来。
import pandas as pd
# 1. 加载数据
df = pd.read_csv('device_catalog.csv')
# 2. 数据概览
print("=== 数据概览 ===")
print(f"数据集大小: {df.shape}")
print(f"列名: {df.columns.tolist()}")
print(f"数据类型:\n{df.dtypes}")
print(f"\n前5行:\n{df.head()}")
# 3. 统计描述
print("\n=== 统计描述 ===")
print(df.describe(include='all'))
# 4. 缺失值统计
print("\n=== 缺失值统计 ===")
missing = df.isnull().sum()
missing_pct = (missing / len(df)) * 100
missing_report = pd.DataFrame({'缺失数量': missing, '缺失比例(%)': missing_pct})
print(missing_report[missing_report['缺失数量'] > 0].sort_values('缺失比例(%)', ascending=False))
# 5. 唯一值分析
print("\n=== 唯一值分析 ===")
for col in df.columns:
unique_count = df[col].nunique()
print(f"{col}: {unique_count} 个唯一值")
if unique_count < 20: # 唯一值少的字段,打印具体值
print(f" 具体值: {df[col].unique()}")
这段代码我几乎每个项目都会用。把它封装成一个函数,以后每次拿到新数据,跑一遍就心里有数了。
建议:把上面的代码保存为 data_profile.py,以后每次做数据清洗前先跑一遍。我自己的工具箱里就有这个脚本,用了三年,省了不少时间。
4.6 避坑指南
最后,分享几个我踩过的坑:
- 别只看 describe() 的均值:均值容易被极端值拉偏。我遇到过一批设备价格,均值显示1.2万,但实际大部分设备在8000左右,因为有几个100万的异常值把均值拉高了。一定要结合中位数和四分位数一起看。
- 缺失值统计要关注「隐式缺失」:有些数据不是 NaN,而是用「-」「未知」「0」代替。这些在
isnull()里是统计不到的。我建议对关键字段做一次value_counts(),看看有没有异常占位符。 - 唯一值分析要区分「业务唯一」和「技术唯一」:比如设备编号,技术上应该是唯一的,但如果发现重复值,可能是数据录入错误。而设备颜色,重复是正常的,不需要处理。
数据剖析这件事,说白了就是「先调查,再动手」。花10分钟做一次全面体检,能帮你避免后面几小时的返工。记住我这句话:数据清洗的80%问题,在剖析阶段就能发现。