如何用Pandera校验DataFrame中一列子串是否存在于另一列?
问题
我定义了如下 Pandas DataFrameSchema:
Schema = DataFrameSchema( columns={ 'Complete_Name': Column(name='Complete_Name', dtype='string', nullable=True), 'Surname': Column(name='Surname', dtype='string', nullable=True), }, checks=check_cross_check_name_and_surname, coerce=True, strict=True, ordered=False, unique_column_names=True, metadata=None )
需要实现的校验逻辑等价于如下 SQL 查询(每条Complete_Name必须包含对应的Surname):
SELECT * FROM database.user WHERE Complete_Name LIKE '%'||Surname||'%'
我尝试了以下几种写法,但都没达到预期效果:
check_cross_check_name_and_surname = [pa.Check(lambda df: df["Complete_Name"].str.contains(df["Surname"], regex=True), element_wise=True, ignore_na=False)] check_cross_check_name_and_surname = [pa.Check(lambda df: df["Complete_Name"].str.contains(df["Surname"], regex=False), element_wise=True, ignore_na=False)] check_cross_check_name_and_surname = [pa.Check(lambda df: df["Surname"] in df["Complete_Name"], element_wise=True, ignore_na=False)]
请问应该如何编写check_cross_check_name_and_surname,才能在调用Schema.validate(dataframe)时正确执行校验?
正确的校验逻辑实现
之前的写法存在两个核心问题:
- 设置
element_wise=True时,Check的lambda参数是单条数据的Series而非整个DataFrame,直接用df["Complete_Name"]会引发索引或取值错误。 str.contains默认是向量化匹配,当传入Series作为匹配值时,无法逐行对应匹配,必须显式处理每行的对应关系。
推荐两种可行的实现方式:
方式一:逐行处理(直观易维护)
利用apply逐行检查,同时处理空值场景(因为字段允许为空):
from pandera import Check def check_name_contains_surname(df): # 空值行直接通过校验 null_mask = df[["Complete_Name", "Surname"]].isna().any(axis=1) # 非空行检查Complete_Name是否包含对应Surname match_mask = df.apply(lambda row: row["Surname"] in row["Complete_Name"], axis=1) # 所有行都满足条件则返回True return (null_mask | match_mask).all() check_cross_check_name_and_surname = [Check(check_name_contains_surname)]
方式二:向量化处理(性能更优)
用numpy.vectorize包装匹配逻辑,实现向量化运算:
import numpy as np import pandas as pd from pandera import Check def contains_surname(full_name, surname): if pd.isna(full_name) or pd.isna(surname): return True return surname in full_name vectorized_check = np.vectorize(contains_surname) check_cross_check_name_and_surname = [ Check(lambda df: vectorized_check(df["Complete_Name"], df["Surname"]).all()) ]
关键注意点
- 必须移除
element_wise=True,因为我们需要对整个DataFrame做跨行校验,而非单条数据的字段校验。 - 空值处理:由于字段设置了
nullable=True,所以只要任一字段为空,校验自动通过;如果需要空值也触发校验失败,可以删除空值判断逻辑,直接返回surname in full_name(此时空值会返回False,触发校验错误)。 - Python的
in运算符和SQL的LIKE '%...%'行为完全一致,都是简单的子串匹配,不需要启用正则模式。
内容的提问来源于stack exchange,提问作者Rafael Cassemiro Tezzotto
相关产品推荐
相关产品推荐

