使用检查进行验证¶
检查是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 |
|---|
可以对列应用多个检查:
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 布尔值。
在检查失败时引发警告而不是错误¶
在某些情况下,您可能希望发出警告并继续执行程序。Check 和 Hypothesis 类及其内置方法支持关键字参数 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 命名空间中使用。有关更多信息,请参见 扩展 文档。