Spaces:
Running
Running
| """Pandera schema validation. | |
| Catch bad data at the boundary, not in the middle of a training run. | |
| """ | |
| from __future__ import annotations | |
| import pandera as pa | |
| from pandera.typing import Series | |
| from ..utils.logging import get_logger | |
| log = get_logger(__name__) | |
| class LoanSchema(pa.DataFrameModel): | |
| """Schema for LendingClub application-time data after normalisation. | |
| We use ``Series[float]`` for nullable numeric columns to avoid the | |
| pandas int-with-NaN trap (nullable ints require Int64 dtype which | |
| is finicky across versions). | |
| """ | |
| id: Series[int] = pa.Field(unique=True, coerce=True) | |
| loan_amnt: Series[float] = pa.Field(ge=500, le=50_000, nullable=True) | |
| term: Series[float] = pa.Field(isin=[36.0, 60.0], nullable=True) | |
| int_rate: Series[float] = pa.Field(ge=0, le=40, nullable=True) | |
| installment: Series[float] = pa.Field(ge=0, nullable=True) | |
| grade: Series[str] = pa.Field(isin=["A", "B", "C", "D", "E", "F", "G"], nullable=True) | |
| annual_inc: Series[float] = pa.Field(ge=0, le=10_000_000, nullable=True) | |
| dti: Series[float] = pa.Field(ge=-1, le=999, nullable=True) | |
| delinq_2yrs: Series[float] = pa.Field(ge=0, le=100, nullable=True) | |
| open_acc: Series[float] = pa.Field(ge=0, le=200, nullable=True) | |
| revol_bal: Series[float] = pa.Field(ge=0, nullable=True) | |
| revol_util: Series[float] = pa.Field(ge=0, le=300, nullable=True) | |
| pub_rec: Series[float] = pa.Field(ge=0, nullable=True) | |
| class Config: | |
| strict = False # allow extra columns (loan_status, etc.) | |
| coerce = True | |
| def validate_schema(df, schema: type[pa.DataFrameModel] = LoanSchema, lazy: bool = True): | |
| """Validate a DataFrame against a Pandera schema. | |
| Args: | |
| df: input DataFrame | |
| schema: Pandera DataFrameModel class | |
| lazy: if True, collect all errors before raising | |
| Returns: | |
| validated DataFrame (Pandera may coerce types) | |
| """ | |
| try: | |
| validated = schema.validate(df, lazy=lazy) | |
| log.info(f"Schema validation passed for {len(df):,} rows") | |
| return validated | |
| except pa.errors.SchemaErrors as exc: | |
| log.error(f"Schema validation failed: {exc.failure_cases.head()}") | |
| raise | |