Pandera for Polars
Create executable Polars dataframe contracts whose backend, validation depth,
coercion, failure aggregation, and pipeline boundary are explicit.
Boundary
Use this skill only for Pandera's Polars backend. Import it as
pandera.polars; pandas, Ibis, PySpark, and Narwhals-backed behavior differs.
Use the Polars skill for transformation semantics and this skill for runtime
dataframe contracts. Do not replace ordinary Python object validation with a
one-row dataframe schema.
Know the objects and overloaded words
| Object |
Runtime meaning |
Use it for |
DataFrameSchema |
An executable schema object containing Polars column and dataframe checks. |
Dynamic/programmatic schemas and schema composition. |
Column |
A named column contract: dtype, nullability, requirement, uniqueness, coercion, and checks. |
Per-column structural and value rules. |
Check |
A predicate contract evaluated by the backend. |
Domain constraints not captured by dtype/nullability. |
DataFrameModel |
A class-declared schema compiled from annotations, Fields, checks, and config. |
Reusable named contracts with type-checker-friendly declarations. |
Field |
Declarative column constraints inside a DataFrameModel. |
Built-in comparisons, membership, aliases, nullable/unique behavior. |
PolarsData |
Custom-check input holding a LazyFrame and optional column key. |
Native vectorized Polars checks. |
SchemaError / SchemaErrors |
One validation failure or an aggregate of failures. |
Machine-readable failure handling and diagnostics. |
Two kinds of “lazy” must remain separate:
pl.LazyFrame is a deferred Polars query. Pandera's native Polars validation
checks schema-level properties by default and does not automatically execute
all data-level checks on an uncollected plan.
schema.validate(..., lazy=True) requests accumulation of multiple validation
failures before raising; it does not make eager validation computationally
lazy.
Read the schema and validation model before
choosing a schema style or claiming that values were checked.
Ordered workflow
- Recover the data contract: accepted frame type, ordered/required columns,
exact dtypes, nullable fields, uniqueness, extra-column policy, allowed
coercions, cross-column invariants, and failure interface.
- Import
pandera.polars as pa. Confirm the installed Pandera and Polars
versions before copying a backend feature or signature.
- Choose
DataFrameSchema for dynamic composition or DataFrameModel for a
stable named contract. Do not maintain both as independent sources of truth.
- Encode structural rules first, then built-in vectorized checks, then the
smallest native custom check that remains.
- Choose validation depth from the input object. If data-level checks are
required for a
LazyFrame, collect or otherwise establish a supported
execution boundary; do not report schema-only validation as full validation.
- Choose
coerce, strict, and lazy independently. Each changes a different
contract.
- Validate at ingress, after an untrusted/shape-changing stage, or before
egress—not after every expression by habit.
- Test one failure for every rule and inspect
failure_cases/error categories,
not exception prose alone.
Decision table
| Need |
Use |
Do not confuse it with |
| Programmatic/reusable schema value |
pa.DataFrameSchema |
A Python class instance model. |
| Declarative named dataframe contract |
pa.DataFrameModel |
Pydantic BaseModel. |
| Reject unspecified columns |
strict=True |
required=True, which concerns declared columns. |
| Drop unspecified columns intentionally |
Installed strict="filter" support |
Silent schema drift; test passthrough loss. |
| Convert compatible inputs |
coerce=True at the chosen scope |
Validation-only behavior; coercion mutates the returned representation. |
| Gather all failures |
validate(..., lazy=True) |
A Polars LazyFrame. |
| Vectorized custom column rule |
Check receiving PolarsData and returning Boolean LazyFrame output |
Python element callbacks. |
| Cross-column invariant |
Dataframe-level native check |
A column check that cannot see the needed peer fields. |
Read the operation map for schemas, models, checks,
decorators, and pipeline placement.
Canonical strict contract
import pandera.polars as pa
import polars as pl
ORDERS = pa.DataFrameSchema(
{
"order_id": pa.Column(
pl.Int64,
checks=pa.Check.ge(1),
nullable=False,
unique=True,
),
"country": pa.Column(
pl.String,
checks=pa.Check.isin(["DE", "FR", "NL"]),
nullable=False,
),
"amount": pa.Column(
pl.Float64,
checks=pa.Check.ge(0),
nullable=True,
),
},
strict=True,
coerce=False,
)
def validate_orders(frame: pl.DataFrame) -> pl.DataFrame:
return ORDERS.validate(frame, lazy=True)
This rejects extra columns, does not silently coerce identifiers or amounts,
checks all data-level rules on an eager frame, and aggregates failures. If the
boundary intentionally normalizes compatible types, turn on coercion and test
the returned dtypes and failed dirty values; do not use coercion to make an
unknown schema “pass.”
Custom checks without Python row paths
import pandera.polars as pa
import polars as pl
from pandera.polars import PolarsData
def start_before_end(data: PolarsData) -> pl.LazyFrame:
return data.lazyframe.select(pl.col("start") <= pl.col("end"))
INTERVALS = pa.DataFrameSchema(
{
"start": pa.Column(pl.Datetime),
"end": pa.Column(pl.Datetime),
},
checks=pa.Check(start_before_end),
)
A native custom check returns a Boolean LazyFrame shape accepted by the
backend. Avoid element_wise=True for a vectorizable condition; the Polars
backend implements elementwise Python callbacks through a slower row path.
High-risk rules
- Backend imports are part of correctness. Do not use top-level or
pandera.pandas examples in Polars code.
nullable, required, unique, strict, and coerce are independent.
State each required policy rather than relying on defaults.
- A schema validates its declared contract, not business completeness. Add
cross-column checks for relational invariants and tests for duplicate/null
combinations where needed.
- DataFrame validation can return a coerced/filtered frame. Use the returned
value; do not validate and then continue with the unvalidated original.
- For a
LazyFrame, establish whether schema-only validation is acceptable. If
row values must be proven, validate an eager boundary or a currently
documented supported full-data path.
- Treat validation errors as structured evidence. Preserve useful failure cases
without leaking sensitive source values in logs or API responses.
- Feature support differs by backend. Do not copy pandas-only index, groupby
check, parser, hypothesis, synthesis, or custom-registration patterns into a
Polars schema without current evidence.
Run the backend inspector, read version and backend grounding, and use the
validation test matrix.
Completion gate
Do not declare completion until the import selects the Polars backend; the
schema style has one source of truth; exact dtypes, required/null/unique/extra
and coercion policies are asserted; eager versus lazy-frame validation depth is
tested; vectorizable rules stay native; the returned validated frame is used;
every rule has a failing example; aggregated failures are asserted structurally;
and installed-version checks cover backend-specific syntax. Report any
schema-only LazyFrame validation or skipped dependency execution explicitly.
References
- Schema and validation model
- Operation map
- Version and backend grounding
- Testing Pandera Polars contracts
1---2name: pandera-polars3description: Creates executable Polars dataframe contracts using Pandera's Polars backend for runtime validation of schemas, columns, and checks.4---56# Pandera for Polars78Create executable Polars dataframe contracts whose backend, validation depth,9coercion, failure aggregation, and pipeline boundary are explicit.1011## Boundary1213Use this skill only for Pandera's Polars backend. Import it as14`pandera.polars`; pandas, Ibis, PySpark, and Narwhals-backed behavior differs.15Use the Polars skill for transformation semantics and this skill for runtime16dataframe contracts. Do not replace ordinary Python object validation with a17one-row dataframe schema.1819## Know the objects and overloaded words2021| Object | Runtime meaning | Use it for |22|---|---|---|23| `DataFrameSchema` | An executable schema object containing Polars column and dataframe checks. | Dynamic/programmatic schemas and schema composition. |24| `Column` | A named column contract: dtype, nullability, requirement, uniqueness, coercion, and checks. | Per-column structural and value rules. |25| `Check` | A predicate contract evaluated by the backend. | Domain constraints not captured by dtype/nullability. |26| `DataFrameModel` | A class-declared schema compiled from annotations, `Field`s, checks, and config. | Reusable named contracts with type-checker-friendly declarations. |27| `Field` | Declarative column constraints inside a `DataFrameModel`. | Built-in comparisons, membership, aliases, nullable/unique behavior. |28| `PolarsData` | Custom-check input holding a `LazyFrame` and optional column key. | Native vectorized Polars checks. |29| `SchemaError` / `SchemaErrors` | One validation failure or an aggregate of failures. | Machine-readable failure handling and diagnostics. |3031Two kinds of “lazy” must remain separate:3233- `pl.LazyFrame` is a deferred Polars query. Pandera's native Polars validation34 checks schema-level properties by default and does not automatically execute35 all data-level checks on an uncollected plan.36- `schema.validate(..., lazy=True)` requests accumulation of multiple validation37 failures before raising; it does not make eager validation computationally38 lazy.3940Read [the schema and validation model](references/object-model.md) before41choosing a schema style or claiming that values were checked.4243## Ordered workflow44451. Recover the data contract: accepted frame type, ordered/required columns,46 exact dtypes, nullable fields, uniqueness, extra-column policy, allowed47 coercions, cross-column invariants, and failure interface.482. Import `pandera.polars as pa`. Confirm the installed Pandera and Polars49 versions before copying a backend feature or signature.503. Choose `DataFrameSchema` for dynamic composition or `DataFrameModel` for a51 stable named contract. Do not maintain both as independent sources of truth.524. Encode structural rules first, then built-in vectorized checks, then the53 smallest native custom check that remains.545. Choose validation depth from the input object. If data-level checks are55 required for a `LazyFrame`, collect or otherwise establish a supported56 execution boundary; do not report schema-only validation as full validation.576. Choose `coerce`, `strict`, and `lazy` independently. Each changes a different58 contract.597. Validate at ingress, after an untrusted/shape-changing stage, or before60 egress—not after every expression by habit.618. Test one failure for every rule and inspect `failure_cases`/error categories,62 not exception prose alone.6364## Decision table6566| Need | Use | Do not confuse it with |67|---|---|---|68| Programmatic/reusable schema value | `pa.DataFrameSchema` | A Python class instance model. |69| Declarative named dataframe contract | `pa.DataFrameModel` | Pydantic `BaseModel`. |70| Reject unspecified columns | `strict=True` | `required=True`, which concerns declared columns. |71| Drop unspecified columns intentionally | Installed `strict="filter"` support | Silent schema drift; test passthrough loss. |72| Convert compatible inputs | `coerce=True` at the chosen scope | Validation-only behavior; coercion mutates the returned representation. |73| Gather all failures | `validate(..., lazy=True)` | A Polars `LazyFrame`. |74| Vectorized custom column rule | `Check` receiving `PolarsData` and returning Boolean `LazyFrame` output | Python element callbacks. |75| Cross-column invariant | Dataframe-level native check | A column check that cannot see the needed peer fields. |7677Read [the operation map](references/operations.md) for schemas, models, checks,78decorators, and pipeline placement.7980## Canonical strict contract8182```python83import pandera.polars as pa84import polars as pl858687ORDERS = pa.DataFrameSchema(88 {89 "order_id": pa.Column(90 pl.Int64,91 checks=pa.Check.ge(1),92 nullable=False,93 unique=True,94 ),95 "country": pa.Column(96 pl.String,97 checks=pa.Check.isin(["DE", "FR", "NL"]),98 nullable=False,99 ),100 "amount": pa.Column(101 pl.Float64,102 checks=pa.Check.ge(0),103 nullable=True,104 ),105 },106 strict=True,107 coerce=False,108)109110111def validate_orders(frame: pl.DataFrame) -> pl.DataFrame:112 return ORDERS.validate(frame, lazy=True)113```114115This rejects extra columns, does not silently coerce identifiers or amounts,116checks all data-level rules on an eager frame, and aggregates failures. If the117boundary intentionally normalizes compatible types, turn on coercion and test118the returned dtypes and failed dirty values; do not use coercion to make an119unknown schema “pass.”120121## Custom checks without Python row paths122123```python124import pandera.polars as pa125import polars as pl126from pandera.polars import PolarsData127128129def start_before_end(data: PolarsData) -> pl.LazyFrame:130 return data.lazyframe.select(pl.col("start") <= pl.col("end"))131132133INTERVALS = pa.DataFrameSchema(134 {135 "start": pa.Column(pl.Datetime),136 "end": pa.Column(pl.Datetime),137 },138 checks=pa.Check(start_before_end),139)140```141142A native custom check returns a Boolean `LazyFrame` shape accepted by the143backend. Avoid `element_wise=True` for a vectorizable condition; the Polars144backend implements elementwise Python callbacks through a slower row path.145146## High-risk rules147148- Backend imports are part of correctness. Do not use top-level or149 `pandera.pandas` examples in Polars code.150- `nullable`, `required`, `unique`, `strict`, and `coerce` are independent.151 State each required policy rather than relying on defaults.152- A schema validates its declared contract, not business completeness. Add153 cross-column checks for relational invariants and tests for duplicate/null154 combinations where needed.155- DataFrame validation can return a coerced/filtered frame. Use the returned156 value; do not validate and then continue with the unvalidated original.157- For a `LazyFrame`, establish whether schema-only validation is acceptable. If158 row values must be proven, validate an eager boundary or a currently159 documented supported full-data path.160- Treat validation errors as structured evidence. Preserve useful failure cases161 without leaking sensitive source values in logs or API responses.162- Feature support differs by backend. Do not copy pandas-only index, groupby163 check, parser, hypothesis, synthesis, or custom-registration patterns into a164 Polars schema without current evidence.165166Run [the backend inspector](scripts/inspect_pandera_polars.py), read [version and backend grounding](references/version-grounding.md), and use [the167validation test matrix](references/testing.md).168169## Completion gate170171Do not declare completion until the import selects the Polars backend; the172schema style has one source of truth; exact dtypes, required/null/unique/extra173and coercion policies are asserted; eager versus lazy-frame validation depth is174tested; vectorizable rules stay native; the returned validated frame is used;175every rule has a failing example; aggregated failures are asserted structurally;176and installed-version checks cover backend-specific syntax. Report any177schema-only LazyFrame validation or skipped dependency execution explicitly.178179## References180181- [Schema and validation model](references/object-model.md)182- [Operation map](references/operations.md)183- [Version and backend grounding](references/version-grounding.md)184- [Testing Pandera Polars contracts](references/testing.md)