用 Pointblank 在 Python 中声明式校验数据质量

2026-08-05 43 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

数据校验最容易失控的地方,不是不会写 if,而是校验规则散落在 ETL 脚本、Notebook 和定时任务里,失败后也很难回答两个问题:哪些行出了问题?这套规则能不能在下一次任务中原样重跑?

Pointblank 提供了一种更集中的做法:用声明式验证计划描述数据质量要求,执行后查看验证结果,并把通过行与失败行分开处理。对于需要反复运行的批处理任务,还可以把计划放进 YAML,让校验逻辑脱离业务代码单独维护。

把数据质量规则写成验证计划

一个典型的订单数据集可能要求:订单号不能为空,金额必须大于零,订单状态只能取有限的几个值。与其在多个处理步骤中重复写条件,不如把这些要求组织成一条 Pointblank 验证链。

下面的示例使用 Pandas 创建数据,并通过 Pointblank 声明校验规则。运行前安装依赖:

python -m pip install pandas pointblank

可复制运行的示例:

import pandas as pd
import pointblank as pb

orders = pd.DataFrame(
    {
        "order_id": ["A001", "A002", None, "A004"],
        "amount": [120.0, -5.0, 80.0, 42.0],
        "status": ["paid", "pending", "paid", "unknown"],
    }
)

validation = (
    pb.Validate(data=orders)
    .col_exists(columns=["order_id", "amount", "status"])
    .col_vals_not_null(columns="order_id")
    .col_vals_gt(columns="amount", value=0)
    .col_vals_in_set(
        columns="status",
        set=["paid", "pending", "cancelled"],
    )
    .interrogate()
)

print(validation)

这里的每个验证步骤都表达一个明确的质量契约:

  • col_exists 检查输入表是否包含预期字段。
  • col_vals_not_null 防止关键标识缺失。
  • col_vals_gt 检查数值下界。
  • col_vals_in_set 限制枚举字段的合法取值。
  • interrogate 执行整条验证计划并生成结果。

实际项目中,列名、允许的状态集合和数值阈值应该从业务契约或配置文件中来,而不是散落在脚本中的魔法值。

失败行不要直接丢掉

验证失败不等于数据没有价值。批处理系统通常需要把正常数据继续送入下游,同时把失败数据保存到隔离区,供修复、告警或人工复核。

Pointblank 负责定义并执行质量检查;如果应用需要精确地拆分行,可以结合相同规则使用 Pandas 构造布尔掩码。这样做的好处是数据分流逻辑清晰、输出结果可直接写入文件或数据库。

import pandas as pd

orders = pd.DataFrame(
    {
        "order_id": ["A001", "A002", None, "A004"],
        "amount": [120.0, -5.0, 80.0, 42.0],
        "status": ["paid", "pending", "paid", "unknown"],
    }
)

valid_mask = (
    orders["order_id"].notna()
    & orders["amount"].gt(0)
    & orders["status"].isin(["paid", "pending", "cancelled"])
)

clean_rows = orders.loc[valid_mask].copy()
failing_rows = orders.loc[~valid_mask].copy()

clean_rows.to_parquet("orders_clean.parquet", index=False)
failing_rows.to_parquet("orders_rejected.parquet", index=False)

print(f"clean={len(clean_rows)}, rejected={len(failing_rows)}")

这里有一个重要边界:不要让 Pointblank 的验证结果和分流条件各自维护一套、逐渐产生差异。可以把规则参数集中到一个配置对象,再分别生成 Pointblank 验证计划和分流掩码;或者在团队约定中明确,Pointblank 是质量报告的权威来源,Pandas 掩码只负责输出数据分区。

失败数据最好至少附带以下信息:批次号、源文件或表名、校验失败原因、发现时间和原始行标识。单独保存一份没有上下文的坏数据,后续很难定位上游问题。

用 YAML 让验证计划可重跑

当规则需要由数据工程师、分析师和业务人员共同维护时,YAML 比把条件硬编码在 Python 中更容易审查。可以把 YAML 作为团队自己的验证计划格式,再由 Python 读取配置并构造 Pointblank 计划。

例如建立 orders_validation.yml

columns:
  required:
    - order_id
    - amount
    - status
rules:
  order_id:
    not_null: true
  amount:
    greater_than: 0
  status:
    allowed:
      - paid
      - pending
      - cancelled

下面的代码展示一种轻量的配置驱动方式。它不是对所有 Pointblank 版本 YAML 接口的固定封装,项目应根据当前安装版本的 YAML API 调整加载部分;核心思路是保持规则配置与执行代码分离。

from pathlib import Path

import pandas as pd
import pointblank as pb
import yaml


def build_validation(data: pd.DataFrame, config: dict):
    validation = pb.Validate(data=data)
    required = config["columns"]["required"]
    validation = validation.col_exists(columns=required)

    rules = config["rules"]
    if rules["order_id"].get("not_null"):
        validation = validation.col_vals_not_null(columns="order_id")

    if "greater_than" in rules["amount"]:
        validation = validation.col_vals_gt(
            columns="amount",
            value=rules["amount"]["greater_than"],
        )

    if "allowed" in rules["status"]:
        validation = validation.col_vals_in_set(
            columns="status",
            set=rules["status"]["allowed"],
        )

    return validation.interrogate()


config = yaml.safe_load(Path("orders_validation.yml").read_text())
orders = pd.read_csv("orders.csv")
result = build_validation(orders, config)
print(result)

安装 YAML 解析依赖:

python -m pip install pyyaml
python validate_orders.py

如果当前 Pointblank 版本支持直接从 YAML 加载验证计划,也可以采用其原生接口,并保留同样的配置版本管理方式。无论采用哪种加载路径,CI 或调度系统都应该固定依赖版本,并在每次运行中记录 YAML 文件版本、数据批次和验证摘要。

在数据管道中设置失败策略

验证计划真正有价值的地方,在于它会影响管道决策。不要只在日志里打印“校验失败”,而应该明确不同规则的处理方式:

  • 结构性错误,例如缺少 order_id,通常应立即终止批次。
  • 少量坏行,例如金额为负,可以隔离失败行并继续处理通过行。
  • 统计性异常,例如空值比例突然升高,可以告警但暂不阻断,具体取决于业务容忍度。

可以在任务入口处加入一个简单的门禁逻辑。下面的 validation 结果对象具体属性可能随 Pointblank 版本变化,实际使用时应以当前版本文档和返回对象为准;示例重点是把“验证”和“管道决策”分成两个步骤。

import sys

result = build_validation(orders, config)

# 根据项目采用的 Pointblank 版本替换为对应的失败状态判断。
validation_failed = result.get("all_passed") is False if isinstance(result, dict) else False

if validation_failed:
    print("validation failed; stop this batch", file=sys.stderr)
    sys.exit(1)

print("validation passed; continue downstream processing")

生产环境中建议把验证摘要写入监控系统,至少包含通过率、失败规则、失败行数和批次标识。这样数据质量问题才能从一次性的脚本输出,变成可追踪的运行指标。

采用前的检查清单

Pointblank 适合把数据质量规则集中表达、执行和审查,但它不会自动决定什么是“正确数据”。落地时可以检查以下事项:

  • 先定义关键列、合法范围和允许的异常比例。
  • 为每条规则确定失败后的动作:终止、隔离还是告警。
  • 让失败行保留原因和批次上下文,避免静默丢弃。
  • 将验证计划放入版本控制,随数据模型变更一起评审。
  • 固定 Pointblank、Pandas 和 YAML 解析器版本,避免返回结构或接口变化影响调度任务。
  • 用一批包含正常值、空值、边界值和非法值的样本测试验证计划。

当规则数量增加时,声明式验证的价值会越来越明显:规则有名字、有执行结果,也能在不同批次之间重复运行。真正需要谨慎的是失败策略和配置版本,而不是把所有检查都堆进一条很长的验证链。


相关推荐