3. 数据探索基础:使用 Spark DataFrame 进行数据概览

在特征工程中,样本偏差校验的第一步是对原始数据进行全面、快速的数据探索。Spark DataFrame 提供了丰富的 API,帮助我们在分布式环境下高效地获取数据概览、统计摘要和结构信息。本节将系统介绍 show()、printSchema() 和 describe() 三个核心方法,并结合实际案例演示如何通过它们发现数据质量问题与潜在偏差。

3.1 数据预览:show()

show() 是最直观的数据查看方法,默认显示前 20 行数据。在样本偏差校验中,我们通常用它快速检查数据格式、缺失值标记(如 null、NaN、?)以及异常值。

关键参数:
  • numRows:控制显示行数,建议在探索阶段设置为 50~100 行以覆盖更多样本。
  • truncate:是否截断长字符串,设为 false 可查看完整内容。
  • vertical:设为 true 时以垂直模式输出,适合列数较多的 DataFrame。
# 加载数据
df = spark.read.csv("data/user_behavior.csv", header=True, inferSchema=True)

# 显示前 10 行,不截断字符串
df.show(10, truncate=False)

# 垂直显示前 5 行,便于观察所有列
df.show(5, vertical=True)
偏差校验提示: 使用 show() 时,注意观察各列是否有统一的缺失值占位符(如 "Unknown"、-1),这些往往是样本偏差的信号。

3.2 结构探查:printSchema()

printSchema() 以树形结构输出 DataFrame 的 Schema,包括列名、数据类型和可空性。在偏差校验中,Schema 信息帮助我们判断:

  • 类型推断是否正确:例如,数值列是否被错误推断为字符串。
  • 可空性标记:nullable = true 的列可能存在缺失值,需进一步分析缺失模式。
  • 特征类型分布:数值型、类别型、时间型特征的比例,为后续偏差检测方法选择提供依据。
df.printSchema()

# 输出示例:
# root
#  |-- user_id: integer (nullable = true)
#  |-- age: integer (nullable = true)
#  |-- income: double (nullable = true)
#  |-- city: string (nullable = false)
#  |-- signup_date: date (nullable = true)
#  |-- label: integer (nullable = true)
常见陷阱: 当 inferSchema 设置为 true 时,Spark 会扫描部分数据推断类型。如果样本偏差导致前几行数据缺失严重,可能将数值列错误推断为字符串类型,进而影响后续统计计算。

3.3 统计摘要:describe()

describe() 对数值列生成统计摘要,包含 count、mean、stddev、min、max 以及四分位数(需额外配置)。这是样本偏差校验的核心工具之一,通过对比不同分组的统计量,可以快速定位分布偏移。

# 对所有数值列生成统计摘要
summary_df = df.describe()
summary_df.show()

# 对指定列生成摘要,并转为 Pandas 便于可视化
df.select("age", "income", "label").describe().toPandas()
统计量 age income label
count 100000 98500 100000
mean 38.5 75200.0 0.12
stddev 12.3 45000.0 0.33
min 18 0.0 0
max 85 500000.0 1
偏差识别要点:
  • count 差异:如果某列的 count 明显少于其他列,说明存在缺失值,需分析缺失是否随机。
  • min/max 异常:收入为 0 或年龄为 0 可能是数据错误或特殊编码,需确认是否代表缺失。
  • stddev 过大:标准差异常大可能暗示存在离群点或数据分布严重偏斜。

3.4 综合数据探索流程

在实际项目中,我们通常将上述三个方法组合使用,形成标准的数据探索流水线:

  1. 结构检查:printSchema() 确认类型推断和可空性。
  2. 数据预览:show(50, truncate=False) 查看原始数据样例。
  3. 统计摘要:describe() 获取数值列分布概览。
  4. 分组对比:结合 groupBy() 和 describe() 对比不同分组的统计量,初步判断是否存在样本偏差。
# 分组对比示例:按 label 分组查看 age 分布
df.groupBy("label").agg(
    F.count("age").alias("count"),
    F.avg("age").alias("mean_age"),
    F.stddev("age").alias("std_age")
).show()
数据探索流程与样本偏差发现路径 printSchema() 类型推断 / 可空性 show() 缺失标记 / 异常值 describe() count / mean / std 偏差信号 1:缺失不随机 偏差信号 2:分布偏移 偏差信号 3:类型错误 数据探索与偏差校验的联动关系 • printSchema() 发现类型错误 → 可能由前几行数据缺失导致推断偏差 • show() 发现缺失标记不一致 → 不同分组的缺失模式可能不同 • describe() 发现分组统计量差异 → 直接指示样本分布偏移

3.5 进阶:自定义统计摘要

当 describe() 提供的统计量不足以支撑偏差分析时,我们可以使用 agg() 结合多个聚合函数自定义摘要:

from pyspark.sql import functions as F

df.agg(
    F.count("income").alias("count"),
    F.mean("income").alias("mean"),
    F.stddev("income").alias("stddev"),
    F.skewness("income").alias("skewness"),   # 偏度
    F.kurtosis("income").alias("kurtosis"),   # 峰度
    F.expr("percentile_approx(income, 0.25)").alias("q1"),
    F.expr("percentile_approx(income, 0.5)").alias("median"),
    F.expr("percentile_approx(income, 0.75)").alias("q3")
).show()
偏度与峰度的作用: 偏度反映分布对称性,偏度绝对值大于 1 通常表示严重偏斜;峰度反映尾部厚度,峰度大于 3 表示存在较多离群点。这两个指标在样本偏差校验中能有效发现分布形态的异常变化。

3.6 实践:快速识别样本偏差

以下是一个完整的探索脚本,结合了上述所有方法,并输出偏差预警:

def explore_data(df, target_col="label"):
    """数据探索与偏差预警"""
    print("=" * 60)
    print("1. Schema 检查")
    print("=" * 60)
    df.printSchema()

    print("\n" + "=" * 60)
    print("2. 数据预览(前 20 行)")
    print("=" * 60)
    df.show(20, truncate=False)

    print("\n" + "=" * 60)
    print("3. 全局统计摘要")
    print("=" * 60)
    df.describe().show()

    print("\n" + "=" * 60)
    print("4. 分组统计对比(按 label)")
    print("=" * 60)
    df.groupBy(target_col).agg(
        F.count("*").alias("样本量"),
        F.avg("age").alias("平均年龄"),
        F.stddev("age").alias("年龄标准差"),
        F.avg("income").alias("平均收入")
    ).show()

    # 偏差预警:检查样本量是否严重失衡
    counts = df.groupBy(target_col).count().collect()
    ratios = [row["count"] / sum([r["count"] for r in counts]) for row in counts]
    if max(ratios) / min(ratios) > 10:
        print("⚠️ 预警:正负样本比例超过 10:1,存在严重样本不平衡!")
重要提醒: 数据探索不是一次性工作。在特征工程的不同阶段(原始数据、清洗后、特征构造后),都应重复执行上述探索流程,以监控样本偏差是否在特征变换过程中被引入或放大。