深入理解 Expectation 类型:Column Map Expectations、Column Aggregate Expectations、Table Expectations

Great Expectations 的核心,说白了就是一套「断言」系统。你告诉它数据应该长什么样,它去检查,然后告诉你过没过。但数据检查的场景千差万别——有的要逐行扫描,有的要算个总数,有的要看整张表的元数据。所以框架把 Expectation 分成了三大类。

我个人习惯把这三种类型比作「显微镜」、「温度计」和「X光机」。显微镜看细节,温度计量整体,X光机看结构。下面我们一个一个拆开讲。

一句话总结: Column Map Expectations 逐行验证,Column Aggregate Expectations 聚合验证,Table Expectations 验证表级元数据。

2.1 Column Map Expectations:逐行扫描的「显微镜」

这类 Expectation 是最常用的。它对每一列、每一行应用一个函数,返回一个布尔值。说白了就是「这行数据符不符合规则?」。

举个例子,你有一个「年龄」列,要求所有值都大于 0。用 expect_column_values_to_be_between 就能搞定。框架会遍历每一行,把不符合条件的行标记出来。

# 检查 age 列是否都在 0 到 150 之间
expectation = ExpectColumnValuesToBeBetween(
    column="age",
    min_value=0,
    max_value=150
)
result = expectation(configuration)
print(result.success)  # True 或 False
print(result["unexpected_list"])  # 不符合条件的值列表

我在项目中遇到过一个问题:数据量上亿时,逐行检查非常慢。后来发现 mostly 参数可以救场——它允许一定比例的异常值通过。比如 mostly=0.95 表示允许 5% 的数据不合格。这个参数在数据质量容忍度高的场景下特别好用。

我的经验: 用 Column Map Expectations 时,记得关注 unexpected_list 的长度。如果异常值太多,列表会截断。建议配合 result_metrics 里的 unexpected_percent 一起看。

常见的 Column Map Expectations 包括:

  • expect_column_values_to_not_be_null — 非空检查
  • expect_column_values_to_be_unique — 唯一性检查
  • expect_column_values_to_match_regex — 正则匹配
  • expect_column_values_to_be_in_set — 枚举值检查
  • expect_column_values_to_be_between — 范围检查

2.2 Column Aggregate Expectations:看整体的「温度计」

这类 Expectation 不关心具体哪行数据有问题,它只看聚合后的统计量。比如平均值、最大值、标准差、唯一值数量等等。

你想想看,有时候你并不需要知道哪条数据错了,你只需要知道「这个月的平均销售额是不是在合理范围内」。这时候 Column Aggregate Expectations 就派上用场了。

# 检查 price 列的平均值是否在 100 到 500 之间
expectation = ExpectColumnMeanToBeBetween(
    column="price",
    min_value=100,
    max_value=500
)
result = expectation(configuration)
print(result.success)
print(result["observed_value"])  # 实际平均值

我记得有一次做金融风控数据校验,客户要求「每日交易金额的方差不能超过历史均值的 3 倍」。用 expect_column_stdev_to_be_between 配合动态阈值,一行代码就搞定了。要是手写 SQL 做同样的事,得写好几层子查询。

避坑指南: 我曾经在空值较多的列上直接用了 expect_column_mean_to_be_between,结果平均值被拉偏了。后来养成了习惯——用 Aggregate Expectations 之前,先跑一个 expect_column_values_to_not_be_null 确认空值率。

常用的 Column Aggregate Expectations:

Expectation 名称 检查内容 典型场景
expect_column_mean_to_be_between 平均值是否在范围内 销售额、温度等连续值
expect_column_median_to_be_between 中位数是否在范围内 收入分布、房价等偏态数据
expect_column_stdev_to_be_between 标准差是否在范围内 波动性监控、异常检测
expect_column_unique_value_count_to_be_between 唯一值数量是否在范围内 分类列、ID 列完整性
expect_column_sum_to_be_between 总和是否在范围内 金额、数量等累加值

2.3 Table Expectations:看结构的「X光机」

这类 Expectation 不关心具体列里的数据值,它关心的是整张表的元数据——列数、行数、列名、表结构等等。

嗯,这里要注意:Table Expectations 通常是在数据加载完成后第一个跑的。如果表结构都不对,后面的列级检查全是白费功夫。

# 检查表是否有恰好 15 列
expectation = ExpectTableColumnCountToEqual(
    value=15
)
result = expectation(configuration)
print(result.success)

# 检查表行数是否在 1000 到 10000 之间
expectation = ExpectTableRowCountToBeBetween(
    min_value=1000,
    max_value=10000
)
result = expectation(configuration)

我曾经接手过一个数据管道,上游改了 schema 但没通知下游。结果下游的 ETL 跑了三天,全跑在错误的列名上。后来我在管道入口加了一个 expect_table_columns_to_match_ordered_list,列名对不上就直接报错。从那以后,这种「幽灵问题」再也没出现过。

我的建议: 把 Table Expectations 放在每个数据管道的第一个检查点。先确认表结构,再确认列级数据质量。顺序搞反了,排查问题会多花好几倍时间。

常见的 Table Expectations:

  • expect_table_row_count_to_be_between — 行数范围
  • expect_table_column_count_to_equal — 列数精确匹配
  • expect_table_columns_to_match_ordered_list — 列名顺序匹配
  • expect_table_columns_to_match_set — 列名集合匹配(不关心顺序)

2.4 三类 Expectation 的关系与选择

这三类不是互斥的,而是互补的。一个完整的数据质量检查套件,通常三者都要用。我画了一张图来展示它们的关系:

Expectation 三大类型关系图 数据质量检查 Column Map 逐行验证 Column Aggregate 聚合验证 Table 元数据验证 检查顺序建议:先 Table → 再 Column Aggregate → 最后 Column Map 结构不对,数据白查;整体不对,细节白费 适用场景 性能开销 异常定位 字段级规则校验 高(逐行扫描) 精确到行 统计量监控 中(一次聚合) 仅到列 表结构校验 低(元数据) 仅到表

从图中你能看到,三种类型覆盖了数据质量的不同维度。实际项目中,我通常这样组合:

  1. 先跑 Table Expectations — 确认表结构、行数、列名正确
  2. 再跑 Column Aggregate Expectations — 确认关键列的统计量在合理范围
  3. 最后跑 Column Map Expectations — 对关键字段做逐行精细检查
注意: 不要一上来就跑 Column Map Expectations。如果表结构都变了,逐行检查的结果毫无意义。我曾经见过一个团队,表被删了两列,他们还在跑列值检查,跑了半小时才发现问题。先检查结构,能省下大量无用计算。

2.5 如何选择正确的 Expectation 类型

这个问题没有标准答案,但我有一个简单的判断方法:

  • 你想知道「哪条数据有问题」? → 用 Column Map
  • 你只想知道「整体趋势对不对」? → 用 Column Aggregate
  • 你想确认「表本身有没有变」? → 用 Table

举个例子。你在监控一个电商订单表:

  • 想知道有没有订单金额为负数?→ expect_column_values_to_be_between(Column Map)
  • 想知道今天的平均客单价是否在正常范围?→ expect_column_mean_to_be_between(Column Aggregate)
  • 想知道今天的数据表有没有新增或删除列?→ expect_table_column_count_to_equal(Table)

这三种类型,加上它们各自的参数配置,基本能覆盖 90% 的数据质量检查场景。剩下的 10%,就需要我们自定义 Expectation 了——那是后面章节的内容。

一个小技巧: 如果你不确定该用哪种,先问自己一个问题:「这个检查失败后,我需要知道具体是哪几行数据出问题吗?」需要就选 Column Map,不需要就选 Column Aggregate。这个判断标准在我手上用了三年,从来没出错过。