使用检查进行验证

检查是pandera的基本构造之一。它们允许您指定关于数据框、列、索引和系列对象的属性,这些属性在数据类型验证/强制转换和核心pandera检查应用于要验证的数据之后应用。

重要

您可以了解更多关于数据类型验证如何工作的内容 Data Type Validation

检查列属性

Check 对象接受一个函数作为必需的参数,预计该函数接受一个 pa.Series 输入并输出一个 boolean 或一个 Series 的布尔值序列。为了使检查通过,布尔序列中的所有元素必须评估为 True,例如:

import pandera as pa
import pandas as pd

check_lt_10 = pa.Check(lambda s: s <= 10)

schema = pa.DataFrameSchema({"column1": pa.Column(int, check_lt_10)})
schema.validate(pd.DataFrame({"column1": range(10)}))
列1
0 0
1 1
2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

可以对列应用多个检查:

schema = pa.DataFrameSchema({
    "column2": pa.Column(str, [
        pa.Check(lambda s: s.str.startswith("value")),
        pa.Check(lambda s: s.str.split("_", expand=True).shape[1] == 2)
    ]),
})

内置检查

对于常见的验证任务,内置检查可在 pandera 中使用。

import pandera as pa

schema = pa.DataFrameSchema({
    "small_values": pa.Column(float, pa.Check.less_than(100)),
    "one_to_three": pa.Column(int, pa.Check.isin([1, 2, 3])),
    "phone_number": pa.Column(str, pa.Check.str_matches(r'^[a-z0-9-]+$')),
})

请参阅Check API参考以获取内置检查的完整列表。

矢量化与逐元素检查

默认情况下,Check 对象在 pd.Series 对象上操作。如果你想对列中的每个元素进行逐个检查,那么你可以提供 element_wise=True 关键字参数:

import pandas as pd
import pandera as pa

schema = pa.DataFrameSchema({
    "a": pa.Column(
        int,
        checks=[
            # a vectorized check that returns a bool
            pa.Check(lambda s: s.mean() > 5, element_wise=False),

            # a vectorized check that returns a boolean series
            pa.Check(lambda s: s > 0, element_wise=False),

            # an element-wise check that returns a bool
            pa.Check(lambda x: x > 0, element_wise=True),
        ]
    ),
})
df = pd.DataFrame({"a": [4, 4, 5, 6, 6, 7, 8, 9]})
schema.validate(df)
没有需要翻译的内容。
a
0 4
1 4
2 5
3 6
4 6
5 7

element_wise == False 默认情况下为 False,以便您可以通过编写向量化检查来利用 pd.Series API 提供的速度提升。

处理空值

默认情况下, pandera 在将对象传递给检查函数进行验证之前会删除空值。对于 Series 对象,空元素会被删除(这也适用于列),而对于 DataFrame 对象,任何包含空值的行都会被删除。

如果您想在保留空值的情况下检查pandas数据结构的属性,请在定义检查时指定 Check(..., ignore_na=False)

注意,这与Column对象中的nullable参数不同,该参数仅检查列中的空值。

列检查组

Column 检查是否支持按不同列分组,以便您可以对感兴趣列的子集进行断言。这改变了 Check 函数的函数签名,使其输入为一个字典,其中键是组名,值是正在验证的系列的子集。

groupby 指定为列名、列名列表或可调用的对象会改变 Check 函数参数的预期签名为:

Callable[Dict[Any, pd.Series] -> Union[bool, pd.Series]

其中字典的键是 groupby 列中的离散键。

在下面的示例中,我们定义了一个 DataFrameSchema,为 height_in_feet 进行列检查,使用单列、多列和更复杂的 groupby 函数,该函数动态创建了一个新列 age_less_than_15

import pandas as pd
import pandera as pa

schema = pa.DataFrameSchema({
    "height_in_feet": pa.Column(
        float, [
            # groupby as a single column
            pa.Check(
                lambda g: g[False].mean() > 6,
                groupby="age_less_than_20"),

            # define multiple groupby columns
            pa.Check(
                lambda g: g[(True, "F")].sum() == 9.1,
                groupby=["age_less_than_20", "sex"]),

            # groupby as a callable with signature:
            # (DataFrame) -> DataFrameGroupBy
            pa.Check(
                lambda g: g[(False, "M")].median() == 6.75,
                groupby=lambda df: (
                    df.assign(age_less_than_15=lambda d: d["age"] < 15)
                    .groupby(["age_less_than_15", "sex"]))),
        ]),
    "age": pa.Column(int, pa.Check(lambda s: s > 0)),
    "age_less_than_20": pa.Column(bool),
    "sex": pa.Column(str, pa.Check(lambda s: s.isin(["M", "F"])))
})

df = (
    pd.DataFrame({
        "height_in_feet": [6.5, 7, 6.1, 5.1, 4],
        "age": [25, 30, 21, 18, 13],
        "sex": ["M", "M", "F", "F", "F"]
    })
    .assign(age_less_than_20=lambda x: x["age"] < 20)
)

schema.validate(df)
身高(英尺) 年龄 性别 年龄小于20
0 6.5 25
1 7.0 30
2 6.1 21 F
3 5.1 18 F
4 4.0 13 F

宽范围检查

pandera 主要设计用于操作长格式数据(通常称为 tidy data),其中每一行是一个观察,每一列是与观察相关的属性。

然而,pandera 也支持在宽格式数据上进行检查,以便跨 DataFrame 的列操作。例如,如果您想对两个组的 height 进行断言,整洁的数据集和模式可能如下所示:

import pandas as pd
import pandera as pa


df = pd.DataFrame({
    "height": [5.6, 6.4, 4.0, 7.1],
    "group": ["A", "B", "A", "B"],
})

schema = pa.DataFrameSchema({
    "height": pa.Column(
        float,
        pa.Check(lambda g: g["A"].mean() < g["B"].mean(), groupby="group")
    ),
    "group": pa.Column(str)
})

schema.validate(df)
高度
0 5.6 A
1 6.4 B
2 4.0 A
3 7.1 B

而等效的宽格式模式将如下所示:

df = pd.DataFrame({
    "height_A": [5.6, 4.0],
    "height_B": [6.4, 7.1],
})

schema = pa.DataFrameSchema(
    columns={
        "height_A": pa.Column(float),
        "height_B": pa.Column(float),
    },
    # define checks at the DataFrameSchema-level
    checks=pa.Check(
        lambda df: df["height_A"].mean() < df["height_B"].mean()
    )
)

schema.validate(df)
高度_A 高度_B
0 5.6 6.4
1 4.0 7.1

您可以看到,当检查被提供给 DataFrameSchema checks 关键字参数时,检查函数应该期望一个 pandas DataFrame 并且应该返回一个 bool,一个 Series 布尔值,或者一个 DataFrame 布尔值。

在检查失败时引发警告而不是错误

在某些情况下,您可能希望发出警告并继续执行程序。CheckHypothesis 类及其内置方法支持关键字参数 raise_warning,默认值为 False。如果设置为 True,检查将发出 SchemaWarning 警告,而不是引发 SchemaError 异常。

注意

谨慎使用此功能!如果检查仅用于信息目的,而对数据完整性并不关键,则使用 raise_warning=True。然而,如果在 Check 中表达的假设是考虑您的数据有效的必要条件,则不要将此选项设置为 true。

一个您可能希望这样做的场景是在数据管道中,该管道进行一些预处理,检查某些列的正态性,并将结果数据集写入表中。在这种情况下,您想查看某些列是否未满足正态性假设,但您仍然希望得到结果表以进行进一步分析。

import warnings

import numpy as np
import pandas as pd
import pandera as pa

from scipy.stats import normaltest


np.random.seed(1000)

df = pd.DataFrame({
    "var1": np.random.normal(loc=0, scale=1, size=1000),
    "var2": np.random.uniform(low=0, high=10, size=1000),
})

normal_check = pa.Hypothesis(
    test=normaltest,
    samples="normal_variable",
    # null hypotheses: sample comes from a normal distribution. The
    # relationship function checks if we cannot reject the null hypothesis,
    # i.e. the p-value is greater or equal to alpha.
    relationship=lambda stat, pvalue, alpha=0.05: pvalue >= alpha,
    error="normality test",
    raise_warning=True,
)

schema = pa.DataFrameSchema(
    columns={
        "var1": pa.Column(checks=normal_check),
        "var2": pa.Column(checks=normal_check),
    }
)

# catch and print warnings
with warnings.catch_warnings(record=True) as caught_warnings:
    warnings.simplefilter("always")
    validated_df = schema(df)
    for warning in caught_warnings:
        print(warning.message)
Column 'var2' failed series or dataframe validator 0: <Check normaltest: normality test>

注册自定义检查

pandera 现在提供一个接口来注册自定义检查函数,以便它们可以在 Check 命名空间中使用。有关更多信息,请参见 扩展 文档。