4. 数据剖析与探索:数据概况统计、缺失值分析、异常值检测、分布分析
数据剖析,说白了就是给数据做个体检。拿到一张表,别急着上模型,先摸摸它的底细。我见过太多人,数据还没看明白就往上堆算法,结果跑出来的结果自己都不敢信。
这一节,我们聊聊怎么系统地做数据探索。四个维度:概况统计、缺失值、异常值、分布分析。按这个顺序走一遍,你对数据的脾气就摸得差不多了。
4.1 数据概况统计
先看整体,再看局部。我习惯用 df.describe() 开道,几秒钟就能拿到数值列的统计摘要。
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('order_data.csv')
# 数值列概况
print(df.describe())
# 分类列概况
print(df.describe(include=['object']))
输出里重点看什么?
- count:非空记录数。如果某列 count 明显少于总行数,说明有缺失。
- min / max:极值。有没有明显不合理的数据?比如订单金额出现负数。
- mean / std:均值和标准差。标准差太大,说明数据波动剧烈,要小心。
- 25% / 50% / 75%:四分位数。能帮你判断数据是否偏态。
我的习惯:先看 count 列,再扫 min 和 max。如果某列 count 比总行数少 5% 以上,我会标记为「需要处理」。如果 min 出现负数但业务上不该有负数,直接标异常。
4.2 缺失值分析
缺失值不是小事。它可能意味着系统 bug、人为漏填、或者数据源本身就不完整。
先统计缺失比例:
# 缺失值统计
missing = df.isnull().sum()
missing_pct = (df.isnull().sum() / len(df)) * 100
missing_df = pd.DataFrame({
'缺失数量': missing,
'缺失比例(%)': missing_pct
})
print(missing_df[missing_df['缺失数量'] > 0].sort_values('缺失比例(%)', ascending=False))
拿到结果后,怎么判断?我一般按这个标准来:
| 缺失比例 | 处理建议 |
|---|---|
| < 1% | 直接删除或填充均值/中位数 |
| 1% - 5% | 用中位数、众数或模型预测填充 |
| 5% - 20% | 需要分析缺失原因,考虑是否与目标变量相关 |
| > 20% | 慎重。如果列不重要,直接删除;如果重要,必须溯源 |
我曾经踩过的坑:有一次做客户流失分析,发现「最近一次登录时间」缺失了 30%。我以为是系统没记录,直接填充了平均值。结果模型效果极差。后来一查,缺失的其实是已经流失的客户——系统在客户注销后清空了登录记录。缺失本身就是一个信号,不能随便填。
4.3 异常值检测
异常值,就是那些「看着不对劲」的数据。比如订单金额 100 万,但正常范围是 100-5000。这种数据要么是录入错误,要么是特殊场景。
我常用的方法有三种:
4.3.1 箱线图法(IQR)
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers
outliers = detect_outliers_iqr(df, 'amount')
print(f'异常值数量: {len(outliers)}')
4.3.2 Z-Score 法
from scipy import stats
z_scores = np.abs(stats.zscore(df['amount']))
outliers_z = df[z_scores > 3]
print(f'Z-Score 异常值数量: {len(outliers_z)}')
4.3.3 业务规则法
这个方法最靠谱。比如订单金额不能为负,年龄不能超过 120。直接写规则过滤:
# 业务规则
invalid_amount = df[df['amount'] <= 0]
invalid_age = df[df['age'] > 120]
print(f'金额异常: {len(invalid_amount)} 条')
print(f'年龄异常: {len(invalid_age)} 条')
核心原则:异常值不一定要删除。先问自己三个问题:
1. 这个值在业务上可能吗?
2. 它是录入错误还是真实事件?
3. 删除后会不会丢失重要信息?
如果答案是「可能真实」,先保留,标记出来,后续分析时单独处理。
1. 这个值在业务上可能吗?
2. 它是录入错误还是真实事件?
3. 删除后会不会丢失重要信息?
如果答案是「可能真实」,先保留,标记出来,后续分析时单独处理。
4.4 分布分析
分布分析,就是看数据长什么样。是正态分布?偏态?还是双峰?
我一般用直方图和 Q-Q 图来快速判断。
import matplotlib.pyplot as plt
import seaborn as sns
# 直方图 + 密度曲线
plt.figure(figsize=(10, 5))
sns.histplot(df['amount'], kde=True, bins=50)
plt.title('订单金额分布')
plt.show()
# Q-Q 图
from scipy import stats
stats.probplot(df['amount'], dist="norm", plot=plt)
plt.title('Q-Q 图')
plt.show()
分布分析能告诉你什么?
- 正态分布:很多统计方法的前提,如果数据偏态严重,可能需要做变换。
- 长尾分布:少数极端值拉高了均值,中位数更能代表「典型值」。
- 双峰分布:数据可能来自两个不同的群体,需要拆分分析。
- 离散分布:值集中在几个点上,比如评分数据 1-5 分。
一个小技巧:如果直方图看不清楚,试试分位数-分位数图(Q-Q 图)。点越接近对角线,数据越接近正态分布。偏离太远,就要考虑做对数变换或 Box-Cox 变换。
4.5 知识体系总览
下面这张图,把数据剖析的四个维度串起来了。你可以把它当作检查清单,每次做数据探索时对照着走一遍。
4.6 实战建议
数据剖析不是一次性工作。我建议你把它做成一个可复用的脚本,每次拿到新数据就跑一遍。这样能快速发现数据质量问题,避免后期返工。
嗯,这里要注意:不要只看数字,要结合业务。比如缺失率 10%,如果是因为新功能上线还没积累数据,那就不是问题。但如果是因为接口报错导致数据没落库,那就是大问题。
数据剖析做得好,根因分析就成功了一半。剩下的,就是顺着线索往下挖了。