Great Expectations验证套件将数据质量规则组织为可复用的检查集合,覆盖列级、表级和多表关联级别的约束。它与数据血缘系统联动,当上游数据变更时自动触发受影响表的验证,形成从数据源到消费端的闭环质量保障体系。在数据驱动的AI系统中,该方案对保障数据质量和模型可靠性具有重要作用。
需要学习Great Expectations数据验证与测试套件,数据合规需要理解数据管道和数据处理流程。本篇涵盖数据清洗/ETL/数据仓库/特征工程,帮助你构建数据管道与洞察。
目录
可视化演示
展示Great Expectations验证套件的数据质量检查流程与质量报告仪表盘,包含列级/表级/多表关联验证、血缘联动触发和质量评分机制,支持模式切换与阶段步进交互。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+captionFlow+'
Great Expectations框架的定位与核心概念
Great Expectations(简称GE)是一个开源的Python数据验证与测试框架,专门用于对数据进行系统化的质量检查。它的核心设计理念借鉴了软件工程中的单元测试思想,但将验证对象从代码逻辑转移到了数据本身。在数据治理和MLOps的实际场景中,GE充当着数据质量门禁的角色,确保流入下游管道或模型训练流程的数据满足预先定义的质量标准。GE的文档化能力也使其成为数据团队与非技术利益相关者之间沟通数据质量状况的重要桥梁。
GE框架的核心概念包括四个关键组件。第一是Expectation(期望),它是对数据应该满足的某种条件的声明式描述,例如某列不应为空、某列的值应在指定范围内、某列的取值应属于某个枚举集合等。第二是Expectation Suite(期望套件),它是多个Expectation的逻辑组合,用于对一张数据表或一个数据集进行全方位的质量约束定义。第三是Checkpoint(检查点),它是将待验证的数据集与期望套件绑定在一起的执行单元,负责触发验证流程并输出结果。第四是Validation Result(验证结果),它记录了每一条期望的通过或失败状态、失败样本的详细信息以及整体的验证统计摘要,是后续数据质量报告和告警的数据来源。
这四个组件之间形成了清晰的层次关系:Expectation是最小粒度的验证规则,多个Expectation组成Expectation Suite来覆盖一个完整数据集的质量维度;Checkpoint将Suite与具体的DataSource关联起来并负责执行;Validation Result则是Checkpoint执行后的产物,它既包含当次验证的详细结果,也可以被序列化为JSON文件进行历史归档和趋势分析。理解这四个概念及其相互关系,是掌握GE框架使用方法的基础。
Expectation Suite期望套件的构建与组织
Expectation Suite的构建通常经历从自动生成到人工审核裁剪的两个阶段。GE提供了多种Profiler(分析器)工具,如BasicDatasetProfiler和SuiteBuilderProfiler,它们能够自动扫描数据集的统计特征,包括列的数据类型、取值范围、空值比例、分布形态等,并据此生成一组候选期望。例如,Profiler观察到某数值列的值始终在0到100之间,便会自动生成一条expect_column_values_to_be_between的候选期望。这种自动化的方式极大降低了从零开始编写期望的门槛,尤其适合对大型数据集进行快速的质量基线建立。
然而,Profiler自动生成的候选期望并不能直接投入使用,必须经过业务专家和数据工程师的审核与裁剪。自动生成的期望可能过于严格,将合理的边界值误判为异常;也可能遗漏了业务层面真正关心的质量约束,例如跨字段的逻辑一致性检查。审核过程中,团队需要根据实际业务场景对每条期望进行判断:保留那些确实反映数据质量要求的规则,调整阈值以适配合理的业务波动,补充Profiler无法自动发现的领域知识约束。这个过程本质上是将统计观察转化为业务规则的知识编码过程。
在版本管理方面,GE将Expectation Suite以JSON格式存储在本地文件系统或元数据存储中,这使得期望套件可以像代码一样纳入Git版本控制系统。每次对期望的修改都有清晰的变更记录,支持回溯和对比。团队可以在CI/CD管道中对期望套件的变更进行审查,确保验证规则的演进与业务需求的变化保持同步。此外,GE支持为不同的数据环境(开发、测试、生产)维护不同的期望套件变体,通过环境化的配置管理来实现灵活的质量策略部署。
列级、表级与多表关联的验证规则体系
GE的验证规则按照作用范围可以分为列级、表级和多表关联三个层次。列级验证是最基础也是最常用的验证粒度,它针对数据表中某一特定列施加约束。常见的列级验证包括:类型检查(expect_column_values_to_be_of_type),确保列的数据类型符合预期;范围检查(expect_column_values_to_be_between),确保数值列的取值在合理区间内;枚举检查(expect_column_values_to_be_in_set),确保列的取值属于预定义的合法值集合;空值检查(expect_column_values_to_not_be_null),确保关键列不存在缺失值;正则匹配(expect_column_values_to_match_regex),确保字符串列的格式符合规范。列级验证覆盖了数据质量中最常见的准确性与完整性维度。
表级验证则超越了单列的范畴,从整体上对数据表的结构性特征进行约束。典型的表级验证包括:行数检查(expect_table_row_count_to_be_between),确保数据表的记录数在合理范围内,防止数据丢失或重复加载;主键唯一性检查(expect_column_values_to_be_unique),确保某列或某列组合的值在整个表中唯一,维护实体的可辨识性;列存在性检查(expect_table_column_count_to_equal),确保数据表包含预期数量的列,防止模式漂移。表级验证对于保障数据集的整体结构完整性至关重要。
在多表关联的场景中,GE还支持跨表的一致性验证。这类验证确保不同数据表之间的关联关系保持正确和一致。例如,外键约束验证可以检查订单表中的用户ID是否都存在于用户表中,防止出现悬挂引用;跨表一致性验证可以比较两个表中同一实体记录的某个属性是否一致,例如用户表中的地区编码与订单表中的地区编码是否匹配。虽然GE原生对多表验证的支持不如列级和表级验证那样丰富,但通过自定义期望(Custom Expectation)和基于SQL的验证方式,可以灵活地实现各种跨表质量检查逻辑,满足复杂数据仓库和星型模式下的质量保障需求。
数据血缘联动的自动触发验证
数据血缘(Data Lineage)记录了数据从产生到消费的全链路流转关系,描述了哪些数据资产之间存在依赖关系。在GE的框架中,数据血缘信息可以与验证流程进行联动,实现基于依赖关系的自动化触发验证。具体而言,当上游数据表发生变更时,血缘系统能够识别出所有受影响的下游数据资产,并将变更事件传播给对应的Checkpoint,触发针对下游数据的自动验证。这种机制确保了数据变更的影响范围能够被及时评估,避免了因上游数据质量问题在数据链路中级联放大而导致的下游数据污染。
上游变更传播的实现通常依赖于事件驱动架构。当上游数据表完成一次ETL加载或模式变更后,数据管道会向消息队列或事件总线发布一条变更事件。GE的Checkpoint可以通过订阅这些事件来感知上游的变化,并根据预设的验证策略自动执行相应的Expectation Suite。例如,当源系统中的客户表完成每日增量同步后,事件触发器会通知依赖该客户表的所有下游Checkpoint依次执行验证,确保同步后的数据质量符合预期。如果验证失败,Checkpoint可以配置为阻止下游管道的继续执行,从而将质量问题控制在最早发现的环节。
Checkpoint的订阅事件自动执行机制是整个联动验证的核心执行层。在实际部署中,Checkpoint可以被配置为多种触发模式:定时触发(按固定时间间隔执行验证)、事件触发(响应上游变更事件)、API触发(由外部系统通过REST API调用)。事件触发模式与数据血缘的结合,使得验证不再是孤立的定时任务,而是嵌入到数据流转链路中的智能质量关卡。这种基于血缘的自动触发验证大幅提升了数据质量保障的时效性和覆盖范围,是现代数据治理体系中不可或缺的组成部分。
数据文档与质量报告的生成机制
GE的Data Docs模块是其区别于许多其他数据验证工具的一大特色。Data Docs能够将验证结果自动渲染为结构化的HTML报告页面,使得数据质量的状况可以以直观、可浏览的方式呈现给技术团队和业务用户。Data Docs的渲染内容包括:每个Expectation Suite的验证通过率和失败率统计、每条期望的具体验证结果(通过或失败)、失败期望对应的异常数据样本、以及历次验证结果的对比趋势。这些报告被组织为可导航的网站形式,用户可以通过点击不同的层级来查看从概要到细节的各个维度的质量信息。
在验证结果的呈现方面,GE提供了丰富的信息层次。最顶层是期望通过率,即本次验证中通过的期望数量占总期望数量的百分比,它是衡量数据集整体质量状况的快速指标。第二层是失败样本(Exception List),对于每条失败的期望,GE会记录导致失败的具体数据行及其字段值,帮助数据工程师快速定位问题数据的特征和分布。第三层是趋势对比(Validation Results History),GE会将历次Checkpoint执行的结果持久化存储,并在Data Docs中以时间序列的方式展示各条期望的通过率变化趋势,帮助团队识别数据质量的退化或改善。
Data Docs还支持自定义渲染器和输出目标。默认情况下,报告以静态HTML文件的形式存储在本地文件系统中,但通过配置不同的Site Builder,可以将报告发布到S3桶、GCS桶或Azure Blob Storage等云存储中,方便团队通过Web浏览器远程访问。此外,GE的回调机制(Callback)可以在验证完成后自动发送通知到Slack、Teams等协作工具,将关键的质量指标和失败告警实时推送给相关责任人,确保数据质量问题能够被及时响应和处理。
GE在MLOps数据质量保障中的闭环实践
在MLOps的实践中,数据质量保障需要贯穿机器学习管道的每一个关键阶段,而GE的Checkpoint机制为各阶段提供了统一的质量门禁能力。在数据接入阶段,GE对原始数据源执行验证,确保进入特征工程流程的数据满足基本的完整性和准确性要求。例如,检查源数据表的行数是否在预期范围内、关键特征列是否存在空值、数值特征是否出现异常的极端值等。如果数据接入阶段的验证失败,Checkpoint可以配置为阻断后续管道的执行,防止低质量数据污染整个特征计算过程。
在特征计算阶段,GE的验证重点转移到特征工程产出的质量上。这一阶段需要检查的特征质量问题包括:特征值的分布是否发生偏移(例如某个归一化特征的值突然超出0到1的范围)、特征之间的逻辑关系是否保持一致(例如年龄与出生日期是否匹配)、新特征的缺失率是否在可接受范围内等。特征计算阶段的验证结果不仅用于当次管道的质量控制,还可以与历史验证结果进行对比,帮助团队及时发现特征漂移(Feature Drift)的早期信号,为模型性能的退化预警提供数据支撑。
在模型训练阶段,GE的验证用于确保训练数据集、验证数据集和测试数据集的质量满足模型训练的前提条件。这包括:训练数据的样本量是否充足、标签分布是否合理、训练集与测试集之间是否存在数据泄露(例如相同样本同时出现在训练集和测试集中)等。通过将GE的Checkpoint集成到MLflow、Kubeflow Pipelines或Airflow等编排工具中,团队可以实现端到端的质量闭环:数据接入验证通过后触发特征计算,特征验证通过后触发模型训练,训练完成后对评估指标也进行期望检查。任何一个环节的验证失败都会触发告警并阻止管道继续推进,从而在工程层面保证了模型训练所依赖的数据质量底线。
AI 应用场景
训练数据的特征质量门禁
线上线下特征一致性验证
Python 演示
import json # ---- 轻量级 GE 风格数据验证框架(纯 Python,可在浏览器运行) ---- class ValidationResult: """单条期望的验证结果""" def __init__(self, expectation, success, failure_count=0, failure_sample=None): self.expectation = expectation self.success = success self.failure_count = failure_count self.failure_sample = failure_sample or [] def __repr__(self): status = "PASS" if self.success else "FAIL" return f"[{status}] {self.expectation} (failures: {self.failure_count})" class ExpectationSuite: """期望套件:管理并执行一组验证规则""" def __init__(self, name): self.name = name self.expectations = [] def add_expectation(self, func, description): self.expectations.append((func, description)) def run(self, data): results = [] for func, desc in self.expectations: r = func(data) r.expectation = desc results.append(r) return results # ---- 内置期望检查函数 ---- def expect_column_not_null(column_name): def check(data): values = [row[column_name] for row in data] nulls = [v for v in values if v is None] return ValidationResult( expectation="", success=len(nulls) == 0, failure_count=len(nulls), failure_sample=nulls[:5] ) return check def expect_column_values_between(column_name, min_val, max_val): def check(data): violations = [] for i, row in enumerate(data): v = row[column_name] if v is not None and (v < min_val or v > max_val): violations.append({"index": i, "value": v}) return ValidationResult( expectation="", success=len(violations) == 0, failure_count=len(violations), failure_sample=violations[:5] ) return check def expect_column_values_in_set(column_name, valid_set): def check(data): violations = [] for i, row in enumerate(data): v = row[column_name] if v is not None and v not in valid_set: violations.append({"index": i, "value": v}) return ValidationResult( expectation="", success=len(violations) == 0, failure_count=len(violations), failure_sample=violations[:5] ) return check def expect_row_count_between(min_count, max_count): def check(data): count = len(data) return ValidationResult( expectation="", success=min_count <= count <= max_count, failure_count=0 if min_count <= count <= max_count else 1, failure_sample=[{"actual_count": count}] ) return check def expect_column_to_be_unique(column_name): def check(data): values = [row[column_name] for row in data if row[column_name] is not None] seen = set() duplicates = [] for i, v in enumerate(values): if v in seen: duplicates.append({"index": i, "value": v}) seen.add(v) return ValidationResult( expectation="", success=len(duplicates) == 0, failure_count=len(duplicates), failure_sample=duplicates[:5] ) return check # ---- 模拟数据与执行验证 ---- sample_data = [ {"user_id": 1, "age": 25, "category": "electronics", "score": 88.5}, {"user_id": 2, "age": 32, "category": "books", "score": 92.1}, {"user_id": 3, "age": None, "category": "clothing", "score": 76.3}, {"user_id": 4, "age": 28, "category": "electronics", "score": 95.0}, {"user_id": 5, "age": 150, "category": "invalid_cat", "score": 60.2}, {"user_id": 3, "age": 22, "category": "books", "score": 81.7},] # 构建期望套件suite = ExpectationSuite("training_data_quality")suite.add_expectation(expect_column_not_null("user_id"), "user_id 列不允许为空")suite.add_expectation(expect_column_to_be_unique("user_id"), "user_id 列必须唯一")suite.add_expectation(expect_column_values_between("age", 1, 120), "age 值必须在 1~120 之间")suite.add_expectation( expect_column_values_in_set("category", ["electronics", "books", "clothing", "food"]), "category 必须属于合法枚举集合")suite.add_expectation(expect_row_count_between(5, 1000), "数据行数应在 5~1000 范围内") # 执行验证results = suite.run(sample_data) # 输出验证报告print(f"=== Expectation Suite: {suite.name} ===")pass_count = sum(1 for r in results if r.success)total = len(results)for r in results: print(r) if not r.success and r.failure_sample: print(f" 失败样本: {r.failure_sample[:3]}")print(f"\n总结: {pass_count}/{total} 条期望通过 (通过率 {pass_count/total*100:.1f}%)")
class ValidationResult:代码逻辑:class ValidationResult:def __init__(self, expectation, success, failure_count=0, failure_sample=None):代码逻辑:def init(self, expectation, success, failure_cself.expectation = expectation代码逻辑:self.expectation = expectationself.success = success代码逻辑:self.success = successself.failure_count = failure_count代码逻辑:self.failure_count = failure_countself.failure_sample = failure_sample or []代码逻辑:self.failure_sample = failure_sample or []def __repr__(self):代码逻辑:def repr(self):status = "PASS" if self.success else "FAIL"代码逻辑:status = "PASS" if self.success else "FAIL"return f"[{status}] {self.expectation} (failures: {self.failure_count})"代码逻辑:return f"[{status}] {self.expectation} (failures:class ExpectationSuite:代码逻辑:class ExpectationSuite:def __init__(self, name):代码逻辑:def init(self, name):self.name = name代码逻辑:self.name = name
常见问答
Great Expectations中的Expectation和Expectation Suite有什么区别?
GE的Profiler自动生成的Expectation Suite能否直接用于生产环境?
GE的Checkpoint有哪些触发模式?如何与数据血缘联动?
GE的Data Docs报告包含哪些层次的信息?
常见误区
误区GE和Python的assert语句没有本质区别,都是做条件判断
正解GE与Python assert存在本质区别。assert是代码级的断言,它在程序执行过程中进行条件判断,失败时抛出异常并中断程序,主要用于开发调试阶段的逻辑验证。而GE是一个声明式的数据验证框架,它通过JSON配置定义验证规则,支持验证结果的持久化存储、HTML报告生成、历史趋势对比、告警回调等企业级功能。GE的验证结果是结构化的数据,可以被其他系统消费和展示;assert的失败只是一个异常,不具备可观测性和可追溯性。此外,GE支持非侵入式验证,不需要修改数据管道的代码逻辑,而assert需要嵌入到业务代码中。
误区Profiler自动生成的Expectation Suite已经足够完善,可以直接用于生产环境
正解Profiler自动生成的Suite只是基于数据统计特征的候选期望集合,不能直接用于生产环境。原因有三:第一,Profiler基于观测到的数据分布生成阈值,可能将合理但罕见的边界值标记为异常,导致大量误报;第二,Profiler无法理解业务语义,无法自动生成涉及业务逻辑的验证规则(如'订单金额应等于单价乘以数量');第三,Profiler的分析基于当前数据快照,可能无法覆盖数据在不同时间段、不同业务场景下的变化特征。正确做法是将Profiler输出作为基线,由团队根据业务知识和实际需求进行审核、裁剪和补充。
误区GE验证只需要在数据接入时执行一次就够了,后续不需要重复验证
正解数据质量验证不是一次性任务,而是需要贯穿数据全生命周期的持续性活动。仅在数据接入时执行验证存在明显不足:第一,数据在管道流转过程中会经历特征计算、聚合、转换等操作,每个环节都可能引入新的质量问题;第二,上游数据源的质量可能随时间退化,即使初始接入时通过了验证,后续批次的数据仍可能出现问题;第三,在MLOps场景中,模型训练、评估和推理各阶段对数据质量的要求不同,需要针对性的验证规则。GE的Checkpoint设计为可重复执行的单元,应当在管道的每个关键节点设置验证门禁,形成持续的质量保障闭环。
参考来源
- webpageGreat Expectations 官方文档Great Expectations项目的官方技术文档,涵盖框架的核心概念(Expectation、Suite、Checkpoint、Data Docs)、快速入门指南、各数据源的集成方式以及API参考,是使用GE进行数据验证的权威参考资料。
- webpageGreat Expectations GitHub 仓库Great Expectations的开源代码仓库,包含完整的源代码、示例项目、贡献指南和社区讨论。通过阅读源码可以深入理解GE的内部实现机制,包括Expectation Suite的序列化格式、Checkpoint的执行流程和Data Docs的渲染逻辑。