【数据预处理】EDA详解

一、一句话定义

EDA = 探索性数据分析,是建模 / 数据清洗之前的诊断步骤:不预设模型、不做预测,只用图表、统计指标把数据集全貌、问题、规律全部挖出来,指导后续数据预处理、清洗、特征工程。你可以理解成:给数据做一次全面体检。

二、EDA 核心目标(为什么预处理前必须做 EDA)

  1. 看懂数据:每列含义、数据类型、取值范围;

  2. 定位脏数据:缺失值、异常值、重复数据、错误编码;

  3. 识别分布:数值是否正态、是否偏斜、有无长尾;

  4. 分析关系:特征与标签相关性、特征间多重共线性;

  5. 发现业务规律:分组差异、潜在分层;

  6. 指导后续处理方案(最重要)

    • 缺失值:该删除、均值填充、中位数填充还是模型填充?
    • 异常值:直接剔除、缩尾处理、对数转换?
    • 偏态数据:是否需要 log 标准化?
    • 强相关特征:是否要删除冗余特征?

三、EDA 完整 4 大模块(预处理诊断标准流程)

模块 1:基础概况检查(宏观)

读取数据后第一步,快速掌握数据集基本信息

  1. 样本量、特征数量、标签列;
  2. 字段类型:数值型 (int/float)、类别型 (object/category)、时间型;
  3. 基础统计:count、mean、std、min、四分位数、max(describe);
  4. 重复行数量、完全重复样本;
  5. 字段唯一值数量(区分高基数 / 低基数分类特征)。

输出结论示例:数据集共 10000 行,12 特征;3 个分类字段、9 个数值字段;存在 230 条重复样本。

模块 2:缺失值诊断(清洗第一重点)

统计每列缺失比例、缺失分布:

  1. 各字段缺失数量、缺失占比;
  2. 缺失是否存在关联(比如 A 字段缺失时 B 也缺失,是业务逻辑导致);
  3. 可视化热力图展示缺失分布。

指导清洗

  • 缺失率 > 70%:直接删除该列;
  • 低缺失连续特征:中位数填充(抗异常);
  • 分类特征:众数填充 / 新增 “未知” 类别;
  • 缺失有业务含义:新增 Is_Missing 标记特征。

模块 3:单变量分析(单列分布、异常检测)

分别对数值特征分类特征单独分析

1)数值型特征

  • 图表:直方图、箱线图;
  • 重点观察:
    ① 分布:正态 / 左偏 / 右偏、长尾;② 异常值:箱线图离群点、远超合理业务区间的值;③ 取值范围是否符合常识(如年龄 = 200、收入 =-1000)。

2)分类特征

  • 图表:计数柱状图、饼图;
  • 重点观察:类别不平衡、稀有小类别、错误文本编码。

指导预处理

  • 右偏严重:log 变换、Box-Cox 转换;
  • 大量极端异常:截断 / 缩尾;
  • 稀有类别合并为 other。

模块 4:多变量关系分析(特征 - 特征、特征 - 标签)

  1. 相关性分析(数值)

    • 皮尔逊相关系数热力图;
    • 识别多重共线性(两个特征高度相关,后续需降维 / 删特征);
  2. 特征 vs 目标标签(建模关键)

    • 分类任务:箱线图、分组柱状图,看不同类别下目标分布差异;
    • 回归任务:散点图,观察线性 / 非线性关系;
  3. 交叉分组:多特征组合,挖掘隐藏分层规律。

指导特征工程

  • 和标签几乎无关的弱相关特征可剔除;
  • 高度共线性二选一删除;
  • 非线性关系特征:新增多项式特征、分箱特征。

四、EDA 与「数据预处理」的先后关系

  1. EDA(探索诊断)→ 2. 数据清洗预处理 → 3. 特征工程 → 4. 建模
  • 不做 EDA 直接清洗:盲目操作,比如随便均值填充、乱删异常值,会丢失关键业务信息;
  • EDA 是诊断,预处理是对症下药。

五、EDA 常用工具代码(Python pandas/matplotlib/seaborn)

1. 基础统计:均值、方差、最值、分位数、样本量

需要获取的指标
  1. 全局:总样本量、总字段数
  2. 数值字段:count有效样本、mean均值、std方差、min最小值、max最大值、25%/50%/75%四分位数
  3. 类别字段:唯一值数量、频次topN、占比
获取方法(Python)
import pandas as pd

df = pd.read_csv("data.csv")
# 1. 样本、字段总量
print(f"总样本数:{df.shape[0]}, 总特征数:{df.shape[1]}")

# 2. 数值型基础统计(均值/方差/最值/分位数一键输出)
print(df.describe())

# 3. 类别型统计:唯一值、频次
for col in df.select_dtypes(include=["object", "category"]).columns:
    print(f"\n字段 {col} 频次分布:")
    print(df[col].value_counts(normalize=True).round(3))

2. 缺失值探查:缺失比例、缺失分布

需要获取指标
  1. 单字段缺失数量、缺失百分比
  2. 缺失联合分布:多字段是否同步缺失(如设备故障时多个传感器同时为空)
  3. 缺失分层分布:按标签/时间分组看缺失率差异
获取方法
# 1. 单字段缺失统计
missing_cnt = df.isnull().sum()
missing_ratio = (df.isnull().sum() / len(df)).round(4)
missing_df = pd.DataFrame({"缺失数量": missing_cnt, "缺失占比": missing_ratio})
print(missing_df[missing_df["缺失数量"] > 0])

# 2. 缺失组合分布(查看哪些行多字段同时缺失)
df[df.isnull().any(axis=1)].isnull().sum(axis=1).value_counts()

# 3. 分组缺失:按标签y看不同类别缺失率
df.groupby("label")["feature1"].apply(lambda x: x.isnull().mean())

3. 异常值探查:箱线图、3σ准则、IQR四分位法

需要获取内容
  1. IQR法:上下边界、落在边界外的异常样本数量、占比
  2. 3σ法:均值±3倍标准差,越界异常样本
  3. 可视化:箱线图直观展示离群点
核心公式
  • IQR = Q75 - Q25
  • 下界 = Q25 - 1.5*IQR
  • 上界 = Q75 + 1.5*IQR
  • 3σ区间:

image

获取方法
import seaborn as sns
import matplotlib.pyplot as plt

def detect_outlier(col_series):
    # IQR检测
    q1 = col_series.quantile(0.25)
    q3 = col_series.quantile(0.75)
    iqr = q3 - q1
    lower = q1 - 1.5 * iqr
    upper = q3 + 1.5 * iqr
    iqr_outlier = ((col_series < lower) | (col_series > upper)).sum()
    
    # 3σ检测
    mu = col_series.mean()
    std = col_series.std()
    sigma_outlier = ((col_series < mu-3*std) | (col_series > mu+3*std)).sum()
    return {"IQR异常数":iqr_outlier, "3σ异常数":sigma_outlier, "上下限":[lower, upper]}

# 遍历所有数值列
num_cols = df.select_dtypes(include=["int64","float64"]).columns
for col in num_cols:
    res = detect_outlier(df[col])
    print(f"{col}: {res}")
    # 绘制箱线图
    plt.figure()
    sns.boxplot(y=df[col])
    plt.title(f"{col} 箱线图")
    plt.show()

4. 分布探查:特征直方图、标签分布、类别不平衡

需要获取内容
  1. 数值特征:直方图/核密度图,判断正态/右偏/左偏/双峰分布
  2. 分类标签:各类样本数量、占比,计算不平衡比例(最大类/最小类)
获取方法
# 1. 数值特征分布直方图
for col in num_cols:
    plt.figure()
    sns.histplot(df[col], kde=True)
    plt.title(f"{col} 分布直方图")
    plt.show()

# 2. 标签分布,判断类别不平衡
label_dist = df["label"].value_counts(normalize=True).round(3)
print("标签分布:\n", label_dist)
# 不平衡系数
imb_ratio = label_dist.max() / label_dist.min()
print(f"类别不平衡系数:{imb_ratio:.2f},大于4即严重不平衡")

5. 相关性分析:皮尔逊系数、热力图、多重共线性检测

需要获取内容
  1. 数值特征两两皮尔逊相关系数矩阵
  2. 相关性热力图可视化,标记|r|>0.7强相关特征对
  3. 多重共线性:VIF方差膨胀因子(VIF>10存在严重共线性)
获取方法
import statsmodels.stats.outliers_influence as oi

# 1. 皮尔逊相关系数
corr_matrix = df[num_cols].corr()
print(corr_matrix)

# 2. 热力图
plt.figure(figsize=(10,8))
sns.heatmap(corr_matrix, annot=True, cmap="coolwarm", vmin=-1, vmax=1)
plt.title("特征相关性热力图")
plt.show()

# 3. VIF多重共线性检测
X = df[num_cols].dropna()
vif_df = pd.DataFrame()
vif_df["特征名"] = X.columns
vif_df["VIF"] = [oi.variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
print(vif_df[vif_df["VIF"] > 10])

6. 数据类型校验:数值型、类别型、文本、时间特征

需要获取内容
  1. 每列原始dtype
  2. 人工区分四类:
  • 数值型:int/float(连续、离散数值)
  • 类别型:object/category(有限枚举值:性别、地区)
  • 文本型:长字符串、无固定枚举(评论、摘要)
  • 时间特征:datetime格式、可转为时间戳
获取方法
# 1. 打印全部字段原始类型
print(df.dtypes)

# 2. 自动筛选四类特征
num_cols = df.select_dtypes(include=["int64", "float64"]).columns.tolist()
cat_cols = df.select_dtypes(include=["object", "category"]).columns.tolist()
time_cols = df.select_dtypes(include=["datetime64"]).columns.tolist()

# 人工区分文本/普通类别:字符串平均长度区分
text_cols = []
pure_cat_cols = []
for c in cat_cols:
    avg_len = df[c].astype(str).str.len().mean()
    if avg_len > 15: # 长文本阈值可自定义
        text_cols.append(c)
    else:
        pure_cat_cols.append(c)

print("数值特征:", num_cols)
print("纯类别特征:", pure_cat_cols)
print("文本特征:", text_cols)
print("时间特征:", time_cols)

六、EDA 输出产出物

一份 EDA 分析报告,包含:

  1. 数据集基础信息;
  2. 缺失值统计与处理建议;
  3. 各特征分布、异常值清单;
  4. 特征相关性、与目标变量关联度;
  5. 完整预处理、清洗、特征工程行动方案。
posted @ 2026-06-23 10:34  静心笃行。  阅读(9)  评论(0)    收藏  举报