2. 数据探索与质量评估:数据概览、描述性统计、缺失值/异常值/重复值初步诊断

拿到一份新数据,别急着往上怼模型。我个人的习惯是,先花20%的时间做数据探索。为什么?因为数据不会告诉你它有问题,你得自己去“审问”它。说白了,这一步就是给你的数据做个体检,看看它有没有“发烧”、“咳嗽”或者“缺胳膊少腿”。

核心目标:在正式清洗之前,快速摸清数据的“脾气”和“底细”。

2.1 数据概览:先看全貌,再看细节

我一般会先问三个问题:数据长什么样?有多少行多少列?每列是干嘛的?

在Python里,我习惯用Pandas。你想想看,如果连数据长啥样都不知道,后面怎么干活?

import pandas as pd

# 加载数据
df = pd.read_csv('sales_data.csv')

# 看一眼前5行
print(df.head())

# 看看数据规模
print(f"数据集形状: {df.shape}")

# 看看每列的类型和非空数量
print(df.info())

df.head() 能让你快速看到数据的“长相”。df.info() 则告诉你每列的数据类型,以及有没有缺失值。嗯,这里要注意,info() 里显示的 Non-Null Count 就是非空值的数量,如果它比总行数少,那就有缺失值了。

我曾经接手过一个项目,数据有200多列,info() 一打出来,发现有一半的列全是 object 类型。我当时就意识到,这数据肯定有大量的文本或者脏数据,清洗工作量不小。

2.2 描述性统计:用数字说话

光看几行数据不够,你得用统计指标来量化。对于数值型列,describe() 是神器。

# 数值列的统计摘要
print(df.describe())

它会输出:计数、均值、标准差、最小值、四分位数、最大值。这些数字能告诉你什么?

  • 均值 vs 中位数:如果均值远大于中位数(比如均值100,中位数50),说明数据右偏,可能有很大的异常值在拉高均值。
  • 标准差:标准差太大,说明数据波动剧烈;太小,说明数据很集中。
  • 最小值/最大值:看看有没有明显不合理的值。比如年龄列最小值是-1,那肯定有问题。

我的小技巧:对于分类列(比如城市、性别),我会用 df['column'].value_counts() 看看分布。如果某个类别的占比超过99%,那这个特征基本没什么区分度,可以考虑删掉。

2.3 缺失值初步诊断:别慌,先摸清规律

缺失值很常见,但处理方式取决于它为什么缺失。我一般先统计缺失率。

# 统计每列的缺失值数量和比例
missing_count = df.isnull().sum()
missing_ratio = df.isnull().mean()

# 合并成一个DataFrame
missing_df = pd.DataFrame({
    '缺失数量': missing_count,
    '缺失比例': missing_ratio
})
print(missing_df[missing_df['缺失数量'] > 0])

为什么要看比例?如果一个列缺失了90%,我个人建议直接删掉这列,因为补全的意义不大,反而可能引入噪声。

我曾经遇到过一个用户画像数据,其中“收入”列缺失了30%。我一开始想用均值填充,但后来发现,高收入人群更倾向于不填收入。如果简单用均值填充,反而会拉低高收入人群的收入水平,导致模型偏差。所以,缺失值的处理一定要结合业务逻辑。

避坑指南:千万不要一上来就用 df.dropna() 删掉所有含缺失值的行。我曾经这么干过,结果把80%的数据都删了,模型直接没法训练。先分析,再动手。

2.4 异常值初步诊断:用“三西格玛”和“箱线图”

异常值就是那些“格格不入”的数据点。怎么找?我常用两种方法。

方法一:3σ 原则(适用于正态分布)

如果数据近似正态分布,那么99.7%的数据会落在均值±3个标准差之内。超出这个范围,就可以视为异常值。

import numpy as np

def detect_outliers_zscore(data, threshold=3):
    z_scores = np.abs((data - data.mean()) / data.std())
    return data[z_scores > threshold]

# 示例:检测'age'列的异常值
outliers = detect_outliers_zscore(df['age'])
print(f"检测到 {len(outliers)} 个异常值")

方法二:箱线图(IQR,适用于偏态分布)

箱线图不依赖正态分布假设。它用四分位距(IQR)来定义异常值:小于 Q1 - 1.5*IQR 或大于 Q3 + 1.5*IQR 的点。

def detect_outliers_iqr(data):
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    return data[(data < lower_bound) | (data > upper_bound)]

outliers_iqr = detect_outliers_iqr(df['age'])
print(f"IQR方法检测到 {len(outliers_iqr)} 个异常值")

我个人更倾向于用箱线图,因为现实中的数据很少是完美的正态分布。比如电商订单金额,往往就是长尾分布,用3σ会误杀很多正常的高消费用户。

2.5 重复值初步诊断:别让“复制粘贴”坑了你

重复值通常是因为数据采集时的错误或者系统bug。比如用户不小心提交了两次表单。

# 检查完全重复的行
duplicate_rows = df.duplicated()
print(f"完全重复的行数: {duplicate_rows.sum()}")

# 查看重复的行
print(df[duplicate_rows].head())

这里要注意,duplicated() 默认是检查所有列是否完全一样。但有时候,我们只关心某些关键列是否重复。比如,同一个用户ID出现了两次,但其他信息不同,那可能是数据更新导致的,不一定是真正的重复。

我的经验:对于用户ID或订单ID这种唯一标识列,我会用 df['user_id'].duplicated() 来检查。如果发现重复,我会保留最新的一条记录(比如根据时间戳排序),或者直接删除。

2.6 知识体系总览

下面这张图,是我自己总结的数据探索与质量评估的流程。你可以把它当作一个检查清单。

数据探索与质量评估流程 原始数据集 数据概览 描述性统计 缺失值诊断 异常值诊断 重复值诊断 df.head() / df.info() df.shape df.describe() value_counts() isnull().sum() 缺失比例分析 3σ / IQR 箱线图 duplicated() 输出:数据质量报告 & 清洗策略

这张图把整个流程串起来了。从原始数据出发,经过数据概览、描述性统计、缺失值、异常值、重复值这五个维度的诊断,最终输出一份数据质量报告,并指导后续的清洗策略。

2.7 实战小贴士:把诊断结果汇总

我建议你把每一步的诊断结果汇总到一个表格里,方便后续决策。

列名 数据类型 缺失比例 异常值数量 重复值数量 初步处理建议
user_id int64 0% 0 5 删除重复行
age float64 5% 3 0 中位数填充缺失值,删除异常值
income float64 30% 10 0 根据业务分组填充,或保留缺失作为特征
city object 1% N/A 0 众数填充

有了这个表格,你就能清晰地知道每列该怎么处理。记住,数据探索不是一次性工作,它应该贯穿整个数据清洗流程。你清洗完一轮,可能还需要再回来看看效果。

总结一下:数据探索与质量评估,就是给数据做“体检”。用 head()info()describe() 看全貌,用 isnull() 找缺失,用 IQR 找异常,用 duplicated() 找重复。每一步都别偷懒,因为数据质量决定了模型的天花板。

专注资料整理