图片通过NightCafe(网址是 https://creator.nightcafe.studio/)创作。
数据验证是生产应用中的关键步骤。你需要确保输入的数据与你的数据流兼容,并且没有意外的值出现。此外,数据验证是一种安全措施,可以防止任何损坏或不准确的数据进一步处理,在第一步就及时发出警报。
Python 已经有一个很棒的项目可以用于此任务,名为 Pydantic。然而,在处理例如机器学习中的大型数据框对象时,Pandera 是一种更快且更可扩展的数据验证工具(可以参考这篇文章(链接),其中包含了公共笔记本)。
性能比较:pandera 和使用 Pydantic 进行逐行验证在不同大小的 pandas.DataFrame 对象上的表现。图片来源 来源)-,基准测试:使用 Pydantic 对 Pandera 的逐行验证进行性能对比)。
此外,Pandera 还支持多种数据框库,如 pandas
、polars
、dask
、modin
和 pyspark.pandas
。更多关于这些库的信息,请参阅Pandera 的文档📄 获取更多信息。
使用Pandera来验证数据声明: Pandera 是一个 MIT 许可证下的开源项目。我与 Pandera 团队或 Union.ai 并无任何关联。该帖子并无任何商业利益。
Pandera有两种定义验证器的方法:模式和模型。我将主要关注第二种,因为它与Pydantic模型相似且代码非常清晰。
要定义一个Pandera模型的话,可以创建一个继承自DataframeModel的子类,并开始定义数据框需要包含的列及其数据类型:
import pandera as pa
# 用户模型类,继承自pandera的DataFrameModel
class UserModel(pa.DataFrameModel):
# 用户ID,类型为整数
id: int
# 用户名,类型为字符串
username: str
# 用户邮箱,类型为字符串
email: str
# 用户是否活跃,类型为布尔值
is_active: bool
# 用户会员类型,类型为字符串
membership: str
# 用户创建日期,类型为带时区的日期时间
creation_date: pd.DatetimeTZDtype # pd.DatetimeTZDtype表示带时区的日期时间类型
# 使用
df = pd.DataFrame(...)
UserModel.validate(df) # <- 如果无效,则会抛出 SchemaError
注意,为了定义用户的创建时间戳,我使用了Pandas自带的数据类型,而不是其他类型,比如datetime.datetime
。Pandera只支持内置的Python、NumPy和Pandas数据类型。你也可以创建自定义数据类型(如逻辑数据类型),但这是个高级主题,在大多数情况下很少用到。
通过 Pandera,你还可以验证其他列的属性,包括但不限于数据类型。
class UserModel(pa.DataFrameModel):
id: int = pa.Field(unique=True, ge=0) # 唯一标识符,必须为非负整数
username: str = pa.Field(str_matches=r"^[a-zA-Z0-9_]+$") # 用户名只能包含字母、数字和下划线
email: str = pa.Field(str_matches=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") # 邮箱地址的正则表达式
is_active: bool # 表示用户是否活跃
membership: str = pa.Field(isin=["premium", "free"]) # 会员类型可以是 "premium" 或 "free"
creation_date: pd.DatetimeTZDtype = pa.Field(dtype_kwargs={"unit": "ns", "tz": "UTC"}) # 创建日期,使用时间戳表示,单位为纳秒,时区为UTC
这里我也是用 pandera 的 Field,就像用 pydantic 的 Field 一样。
- 首先,我规定
id
列不能有重复值,且这些值必须大于等于0。 - 在
username
和email
中,我使用正则表达式检查字符串是否有效。用户名称只能包含字母、数字和下划线,而电子邮件还可以包含破折号和点,但应当符合“smth@smth.smth”的格式。 membership
只能取列表中的值。更好的做法是使用StrEnum
来定义有效值,而不是直接硬编码。- 最后,
creation_date
必须使用纳秒单位并且是UTC时间。这一行可以更整洁地通过typing
库中的Annotated
来实现:creation_date: Annotated[pd.DatetimeTZDtype, "ns", "UTC"]
😋看看文档中的所有字段选项,你会发现更多
自定义验证规则有时候你需要添加自己的自定义验证。Pandera 允许你加入单列自定义检查(比如列或索引检查),以及多列之间检查。
导入 pandera as pa
从 pandera.typing 导入 Series
类 UserModel(pa.DataFrameModel):
id: int = pa.Field(unique=True, ge=0)
username: str = pa.Field(str_matches=r"^[a-zA-Z0-9_]+$")
email: str = pa.Field(
str_matches=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
)
is_active: bool
membership: str = pa.Field(isin=["premium", "free"])
creation_date: Annotated[pd.DatetimeTZDtype, "ns", "UTC"]
# 列/索引检查
@pa.check("username", name="username_length")
def username_length(cls, x: Series[str]) -> Series[bool]:
"""
检查用户名长度在1到20个字符之间
"""
return x.str.len().between(1, 20)
@pa.check("creation_date", name="min_creation_date")
def min_creation_date(cls, x: Series[pd.DatetimeTZDtype]) -> Series[bool]:
"""
确保创建日期在2000年1月1日之后
"""
return x >= dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc)
# 数据框架检查
@pa.dataframe_check
def membership_is_valid(
cls, df: pd.DataFrame, name="membership_is_valid"
) -> Series[bool]:
"""
验证免费会员账户的有效期是否不超过30天
"""
current_time = dt.datetime.now(dt.timezone.utc)
thirty_days = dt.timedelta(days=30)
return (df["membership"] == "premium") | (
(df["membership"] == "free")
& ((current_time - df["creation_date"]) <= thirty_days)
)
记住你在操作整个列对象(Series
),因此在检查中应将操作向量化以提高性能。
别名
当列名由于语言语法原因不能作为Python变量时,Pandera允许为列验证器设置一个别名来匹配数据帧。
class MyModel(pa.DataFrameModel):
# `MyModel` 是一个继承自 `DataFrameModel` 的类,用于定义模型中的别名列。
alias_column: int = pa.Field(..., alias="Alias Column")
# `alias_column` 是一个整数类型的属性,用于表示具有别名 "Alias Column" 的列。
...
严格和转换
当将 strict
设置为 True
时,它会强制经过验证的数据框仅包含在 Pandera 模型中定义的列。另一方面,当启用 coerce
选项时,Pandera 将尝试将列数据转换成与模型数据类型匹配的类型。
class MyModel(pa.DataFrameModel):
...
class Config:
strict = True # 默认为 False
coerce = True # 默认为 False
使用 pa.Field(..., coerce=True)
也可以在字段级别上进行设置强制转换选项。
**懒验证
默认,pandera 在验证检查未通过时会触发错误。这可能会让人感到厌烦,因为它只会显示第一个验证错误,并阻止对其他数据的检查。
在某些情况下,让整个数据框一次性完成验证并收集所有错误比逐一修复并等待验证重新运行要好。这就是所谓的懒验证。
df = pd.DataFrame(...)
# 验证数据框df,启用惰性验证
Mymodel.validate(df, lazy_validation=True)
一个带有数据验证的机器学习生产流水线:
图片通过NightCafe(夜咖啡)创建。
由于大多数机器学习管道都是在Python中训练的,其中表格数据通常被编码为数据框结构,Pandera 是一个非常强大的工具,用于验证其输入和输出。
# pipeline.py
class MLPipeline:
"""通用的机器学习流水线"""
def __init__(self, model_id: str):
self.model_id = model_id
def load_model(self) -> None:
...
def transform_data(self, df: pd.DataFrame) -> pd.DataFrame:
... # <- 这里可能存在无效数据错误
return df_transform
def predict(self, df: pd.DataFrame) -> pd.DataFrame:
self.load_model()
df_transform = self.transform_data(df) # <- 这里可能的数据无效错误
df['score'] = self.model.predict(df_transform) # <- 这里可能的数据无效错误
return df
我们希望避免模型因无效数据引发错误。这意味着我们之前做的所有工作,包括加载模型到内存和处理原始数据,都将白费。这不仅浪费了资源,还阻碍了对其他数据点的评估。
同样,如果模型的输出结构错误,我们的后处理流程(如上传结果至数据库或通过RESTful API返回结果等)就会失败。
使用Pandera定义验证模型后,我们可以利用其用于管道集成的装饰器来进行I/O验证。
# models.py
import pandera as pa
class InputModel(pa.DataFrameModel):
...
class PredictorModel(pa.DataFrameModel):
...
# OutputModel 继承了 InputModel 的所有验证字段
# 同时也包含了 score
class OutputModel(InputModel):
score: float = pa.Field(ge=0, le=1) # 假设模型返回概率
## pipeline.py
import pandera as pa
from .models import InputModel, PredictorModel, OutputModel
class MLPipeline:
"""通用的机器学习流程"""
def __init__(self, model_id: str):
self.model_id = model_id
def load_model(self) -> None:
...
@pa.check_io(df=InputModel.to_schema(), out=PredictorModel.to_schema(), lazy=True)
def data_转换(self, df: pd.DataFrame) -> pd.DataFrame:
...
return df_transform
@pa.check_output(OutputModel.to_schema(), lazy=True)
输入验证 @pa.check_output(OutputModel.to_schema(), lazy=True)
def predict(self, df: pd.DataFrame) -> pd.DataFrame:
self.load_model()
df_transform = self.transform(df)
df['score'] = self.model.predict(df_transform)['score']
return df
因为我们在这个机器学习管道中生成了中间的数据框对象 df_transform
,所以最好也验证一下以避免错误。输入到 predict 方法的数据不会被验证,因为它已经在 _transformdata 中被验证过了。
我们不希望因为某些数据点有错误数据而中断管道。如果有验证错误,应将有问题的数据点暂时搁置,继续用其余数据运行管道。管道不能停!🔥
Pandera 模型可以选择自动移除所有无效数据行。
类 MyModel(pa.DataFrameModel):
...
类 Config:
删除无效行 = True # 注释:删除无效行 = True
然而,不记录就直接忽略所有无效行可能很危险,因为。你需要弄清楚这些数据点为何无效,以便以后能够向客户或数据工程师说明数据质量问题。
这就是为什么我更喜欢创建自己的验证辅助函数,而不是使用pandera的装饰器:
from typing import Tuple
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def log_pandera_errors(exc: pa.errors.SchemaErrors) -> None:
"""
记录 SchemaErrors 异常中的所有错误信息。
"""
for err_type, categories in exc.message.items():
for _, errors in categories.items():
for err in errors:
logger.error(f"{err_type} 错误信息: {err['column']}. {err['error']}")
def handle_invalid(
df: pd.DataFrame, exc: pa.errors.SchemaErrors
) -> Tuple[pd.DataFrame, pd.DataFrame]:
"""
根据 SchemaErrors 异常处理 DataFrame 中的无效数据。
"""
log_pandera_errors(exc)
df_failure = exc.failure_cases
# 检查无法解决的错误
# 即,它们没有特定的行索引
nan_indices = df_failure["index"].isna()
if nan_indices.any():
error_msg = "\n".join(
f" - 列: {row['column']}, 检查: {row['check']}, "
f"失败情况: {row['failure_case']}"
for row in df_failure[nan_indices].to_dict("records")
)
raise ValueError(
f"模式验证失败,无法继续处理:\n{error_msg}\n"
"管道无法继续。请解决后再试。"
)
invalid_idcs = df.index.isin(df_failure["index"].unique())
df_invalid = format_invalid_df(df.loc[invalid_idcs, :], exc)
df_valid = df.iloc[~invalid_idcs]
return df_valid, df_invalid
def validate(
df: pd.DataFrame, model: pa.DataFrameModel
) -> Tuple[pd.DataFrame, pd.DataFrame]:
"""
使用 DataFrameModel 验证 DataFrame 并处理出现的错误。
"""
try:
return model.validate(df, lazy=True), pd.DataFrame()
except pa.errors.SchemaErrors as ex:
return handle_invalid(df, ex)
输出带有某些错误并移除列 id
:
# 错误输出
ERROR:__main__:SCHEMA ERROR: UserModel. 列 'id' 不在数据框中。数据框中的列:['username', 'email', 'membership', 'is_active', 'creation_date']
ERROR:__main__:DATA ERROR: username. 列 'username' 的逐元素验证器编号0:str_matches('^[a-zA-Z0-9_]+
在遇到无法解决的错误时,如果该错误影响了整个列,流程将无法继续。
## 测试一下
最后但同样重要的是,Pandera 模型和框架还包含了一种根据其定义生成示例数据的方法。你需要安装 `hypothesis` 库才能使用它。(详情请参阅 [https://hypothesis.readthedocs.io/en/latest/](https://hypothesis.readthedocs.io/en/latest/))
不过,经过一些测试后,我不推荐它。一旦开始增加限制条件,生成合成数据花费的时间过长,而且生成的数据通常不够多样化(生成的数据未能涵盖整个限制范围,并且存在重复)。我发现更好的替代方案是为每个要测试的模型添加数据生成器——毕竟,一个管道中也没有那么多数据帧需要验证。
class UserModel(pa.DataFrameModel):
...
def sample(size: int = 10) -> pd.DataFrame:
"""一个用于生成有效测试数据的方法"""
current_time = dt.datetime.now(dt.timezone.utc)
# 返回一个包含以下字段的pd.DataFrame对象:
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"] * size, # 将'membership'字段统一设置为'premium',以便顺利通过验证
"creation_date": [current_time] * size,
}
)
# 注:此处的'sample'方法生成的是测试数据,用于模拟实际用户数据。
# 最后来总结一下:结论
数据验证对于每个数据处理管道来说都非常重要,特别是在机器学习领域。Pandera大大简化了这些工作,通过提供一种灵活且高效的基于模型的方法来验证数据框中的数据。
借助 Pandera,你可以定义模型类来确保列类型、范围,甚至是复杂的条件约束。这使得早点发现数据质量问题,确保数据符合预期的标准要求,在数据流向下一步之前。
通过将Pandera集成到机器学习管道中,你可以创建稳健的数据检查,从而帮助避免错误并提升模型输出的可靠性,从而使模型结果更加可靠。
最终用于测试的pandera.DataFrameModel如下:
import pandas as pd
import pandera as pa
from pandera.typing import Series
from typing import Annotated
import datetime as dt
class UserModel(pa.DataFrameModel):
id: int = pa.Field(unique=True, ge=0, coerce=False)
username: str = pa.Field(str_matches=r"^[a-zA-Z0-9_]+$")
email: str = pa.Field(
str_matches=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
)
is_active: bool
membership: str = pa.Field(isin=["premium", "free"])
creation_date: Annotated[pd.DatetimeTZDtype, "ns", "UTC"]
@pa.check("username", name="username_length")
def username_length(cls, x: Series[str]) -> Series[bool]:
"""
检查用户名长度是否介于1到20个字符之间
"""
return x.str.len().between(1, 20)
@pa.check("creation_date", name="min_creation_date")
def min_creation_date(cls, x: Series[pd.DatetimeTZDtype]) -> Series[bool]:
"""
检查创建日期是否在2000年1月1日之后(不含当天)
"""
return x >= dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc)
@pa.dataframe_check
def membership_is_valid(
cls, df: pd.DataFrame, name="membership_is_valid"
) -> Series[bool]:
"""
检查免费会员的账户年龄是否不超过30天
"""
current_time = dt.datetime.now(dt.timezone.utc)
thirty_days = dt.timedelta(days=30)
return (df["membership"] == "premium") | (
(df["membership"] == "free")
& ((current_time - df["creation_date"]) <= thirty_days)
)
class Config:
strict = True
coerce = True
def sample(size: int = 10) -> pd.DataFrame:
"""
用于生成测试数据的方法
"""
current_time = dt.datetime.now(dt.timezone.utc)
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"]
- size, # 为避免日期限制,所有账户都被设置为'premium'
"creation_date": [current_time] * size,
}
)
嗨,我是 Gabriel Furnieles,一位专注于人工智能、数据处理流程和MLOps的应用数学工程师。希望你喜欢这篇文章,并觉得它对你有所帮助,如果喜欢的话,请关注我 Gabriel Furnieles ,并订阅我的新闻通讯,这样你就能直接收到我的文章了 👇
加布里埃尔·弗尼尔斯在Medium上的文章。数学与工程学专家,专长于AI和机器学习。我随便写写……) 失败实例:b%09
ERROR:main:DATA ERROR: email. 列 'email' 的逐元素验证器编号0:strmatches('^[a-zA-Z0-9.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+
In case of an unresolvable error that involves an entire column, the pipeline cannot continue.
TestingLast but not least, Pandera models and schemas also incorporate a method for generating sample data according to their definition. You will need to install [hypothesis](https://hypothesis.readthedocs.io/en/latest/)
library to use it.
However, after testing it with some examples I do not recommend it. As soon as you start adding a few constraints, it takes too long to generate the synthetic data and most of the time it isn’t varied (the generated data do not cover the entire restriction space and repeats itself) The best alternative I found is to add data generators for each model you want to test — after all, there aren’t so many data frames to validate in a pipeline either — .
class UserModel(pa.DataFrameModel):
...
def sample(size: int = 10) -> pd.DataFrame:
"""Added method to generate valid test data manually"""
current_time = dt.datetime.now(dt.timezone.utc)
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"] * size, # All premium to pass checks
"creation_date": [current_time] * size,
}
)
Conclusion
Data validation is vital for every data processing pipeline and especially in Machine Learning. Pandera simplifies a lot of this work by providing a flexible, and efficient model-based approach to validating data in dataframes.
With Pandera, you can define model classes that enforce column types, ranges, and even complex conditional constraints. This makes it easy to catch data quality issues early in the pipeline, ensuring that the data conforms to expected standards before it reaches the next steps.
By integrating Pandera into an ML pipeline, you can create robust data checks that help prevent errors and improve the reliability of model outputs.
Final pandera.DataFrameModel used in the tests:
import pandas as pd
import pandera as pa
from pandera.typing import Series
from typing import Annotated
import datetime as dt
class UserModel(pa.DataFrameModel):
id: int = pa.Field(unique=True, ge=0, coerce=False)
username: str = pa.Field(str_matches=r"^[a-zA-Z0-9_]+$")
email: str = pa.Field(
str_matches=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
)
is_active: bool
membership: str = pa.Field(isin=["premium", "free"])
creation_date: Annotated[pd.DatetimeTZDtype, "ns", "UTC"]
@pa.check("username", name="username_length")
def username_length(cls, x: Series[str]) -> Series[bool]:
"""
Check username length is between 1 and 20 characters
"""
return x.str.len().between(1, 20)
@pa.check("creation_date", name="min_creation_date")
def min_creation_date(cls, x: Series[pd.DatetimeTZDtype]) -> Series[bool]:
"""
Check creation date is after 2000-01-01
"""
return x >= dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc)
@pa.dataframe_check
def membership_is_valid(
cls, df: pd.DataFrame, name="membership_is_valid"
) -> Series[bool]:
"""
Check account age for free memebers is <= 30 days
"""
current_time = dt.datetime.now(dt.timezone.utc)
thirty_days = dt.timedelta(days=30)
return (df["membership"] == "premium") | (
(df["membership"] == "free")
& ((current_time - df["creation_date"]) <= thirty_days)
)
class Config:
strict = True
coerce = True
def sample(size: int = 10) -> pd.DataFrame:
"""Added method to generate valid test data manually"""
current_time = dt.datetime.now(dt.timezone.utc)
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"]
* size, # All premium to avoid date restrictions
"creation_date": [current_time] * size,
}
)
Hi, I’m Gabriel Furnieles, a Mathematical Engineer specializing in Artificial Intelligence, Data Pipelines, and MLOps. I hope you enjoyed the article and found it helpful, if so, please consider following me Gabriel Furnieles ,and subscribing to my newsletter so stories will be sent directly to you 👇
Gabriel Furnieles - MediumRead writing from Gabriel Furnieles on Medium. Mathematical engineer specializing in AI and ML. I write casually on…medium.com) 失败实例:ef.com
ERROR:main:DATA ERROR: UserModel. UserModel 数据框模式 的逐元素验证器编号0:<Check membership_is_valid> 失败实例:c, ef.com, free, True, 2000-12-31 00:00:00+00:00
ValueError: 架构验证失败,无法继续处理:
- 列:UserModel,检查:column_in_dataframe 检查,失败情况:id 列
管道无法继续 😢。请修复后再试
In case of an unresolvable error that involves an entire column, the pipeline cannot continue.
## Testing
Last but not least, Pandera models and schemas also incorporate a method for generating sample data according to their definition. You will need to install `[hypothesis](https://hypothesis.readthedocs.io/en/latest/)` library to use it.
However, after testing it with some examples I do not recommend it. As soon as you start adding a few constraints, it takes too long to generate the synthetic data and most of the time it isn’t varied (the generated data do not cover the entire restriction space and repeats itself) The best alternative I found is to add data generators for each model you want to test — after all, there aren’t so many data frames to validate in a pipeline either — .
class UserModel(pa.DataFrameModel):
...
def sample(size: int = 10) -> pd.DataFrame:
"""Added method to generate valid test data manually"""
current_time = dt.datetime.now(dt.timezone.utc)
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"] * size, # All premium to pass checks
"creation_date": [current_time] * size,
}
)
# Conclusion
Data validation is vital for every data processing pipeline and especially in Machine Learning. Pandera simplifies a lot of this work by providing a flexible, and efficient model-based approach to validating data in dataframes.
With Pandera, you can define model classes that enforce column types, ranges, and even complex conditional constraints. This makes it easy to catch data quality issues early in the pipeline, ensuring that the data conforms to expected standards before it reaches the next steps.
By integrating Pandera into an ML pipeline, you can create robust data checks that help prevent errors and improve the reliability of model outputs.
Final pandera.DataFrameModel used in the tests:
import pandas as pd
import pandera as pa
from pandera.typing import Series
from typing import Annotated
import datetime as dt
class UserModel(pa.DataFrameModel):
id: int = pa.Field(unique=True, ge=0, coerce=False)
username: str = pa.Field(str_matches=r"^[a-zA-Z0-9_]+$")
email: str = pa.Field(
str_matches=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
)
is_active: bool
membership: str = pa.Field(isin=["premium", "free"])
creation_date: Annotated[pd.DatetimeTZDtype, "ns", "UTC"]
@pa.check("username", name="username_length")
def username_length(cls, x: Series[str]) -> Series[bool]:
"""
Check username length is between 1 and 20 characters
"""
return x.str.len().between(1, 20)
@pa.check("creation_date", name="min_creation_date")
def min_creation_date(cls, x: Series[pd.DatetimeTZDtype]) -> Series[bool]:
"""
Check creation date is after 2000-01-01
"""
return x >= dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc)
@pa.dataframe_check
def membership_is_valid(
cls, df: pd.DataFrame, name="membership_is_valid"
) -> Series[bool]:
"""
Check account age for free memebers is <= 30 days
"""
current_time = dt.datetime.now(dt.timezone.utc)
thirty_days = dt.timedelta(days=30)
return (df["membership"] == "premium") | (
(df["membership"] == "free")
& ((current_time - df["creation_date"]) <= thirty_days)
)
class Config:
strict = True
coerce = True
def sample(size: int = 10) -> pd.DataFrame:
"""Added method to generate valid test data manually"""
current_time = dt.datetime.now(dt.timezone.utc)
return pd.DataFrame(
{
"id": range(size),
"username": [f"user_{i}" for i in range(size)],
"email": [f"user_{i}@example.com" for i in range(size)],
"is_active": [True] * size,
"membership": ["premium"]
- size, # All premium to avoid date restrictions
"creation_date": [current_time] * size,
}
)
Hi, I’m Gabriel Furnieles, a Mathematical Engineer specializing in Artificial Intelligence, Data Pipelines, and MLOps. I hope you enjoyed the article and found it helpful, if so, please consider following me Gabriel Furnieles ,and subscribing to my newsletter so stories will be sent directly to you 👇
Gabriel Furnieles - MediumRead writing from Gabriel Furnieles on Medium. Mathematical engineer specializing in AI and ML. I write casually on…medium.com共同学习,写下你的评论
评论加载中...
作者其他优质文章