How to Validate DataFrames With pandera
pandera lets you declare a schema for a pandas DataFrame - column types, nullability, regex patterns, numeric ranges - and validate the whole thing in one call. With lazy=True you get every failing row and rule at once, not just the first one.
What pandera does
pandera is a pandas-native schema validator. You declare a DataFrameSchema that maps each
column to its expected type and one or more Check constraints. When you call
schema.validate(df), pandera compares every value in the DataFrame against those rules and
raises SchemaErrors on the first violation - or, with lazy=True, collects all violations
before raising.
This is the difference between a data-quality gate that stops a bad batch versus one that just points at the first bad row and leaves you hunting for more.
Declaring a schema
import pandas as pd
import pandera.pandas as pa
schema = pa.DataFrameSchema(
{
"email": pa.Column(
str,
nullable=False,
checks=pa.Check.str_matches(r"^[^@]+@[^@]+\.[^@]+$"),
),
"age": pa.Column(
int,
checks=pa.Check.in_range(18, 120),
),
"order_total": pa.Column(
float,
checks=pa.Check.ge(0),
),
},
coerce=True, # cast CSV strings to the declared dtype
)
coerce=True is important when reading from CSV: pandas reads ambiguous columns as object
(string), so without coercion the dtype check on age (declared int) would fail immediately.
Validating lazily and handling failures
import sys
df = pd.read_csv("customers.csv")
try:
schema.validate(df, lazy=True)
except pa.errors.SchemaErrors as e:
print("validation failed:")
for _, row in e.failure_cases.iterrows():
print(f" FAIL {row['column']} / {row['check']}: {row['failure_case']}")
sys.exit(1)
print("all checks passed")
sys.exit(0)
e.failure_cases is a DataFrame with columns column, check, failure_case, and index -
so you can log, alert, or write the bad rows to a quarantine table rather than losing context.
Key checks at a glance
| Constraint | pandera |
|---|---|
| non-null | nullable=False on pa.Column |
| regex pattern | pa.Check.str_matches(r"...") |
| numeric range | pa.Check.in_range(min, max) |
| greater-or-equal | pa.Check.ge(0) |
| allow-list | pa.Check.isin(["a", "b", "c"]) |
Where this fits in a pipeline
A validate.py script that exits non-zero on failure becomes a hard gate in any ETL:
Airflow's BashOperator or PythonOperator marks the task failed; a shell pipeline stops
on set -e; a CI check blocks the merge. The bad batch never reaches the load step.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Declaring a pa.DataFrameSchema with type, nullable, and check constraints per column
- Using lazy=True to surface all validation failures in one SchemaErrors raise
- Iterating e.failure_cases to log or quarantine bad rows and exit non-zero
FAQ
What is pandera used for?
pandera validates pandas (and Polars) DataFrames against a declared schema - column types, nullability, and value constraints like ranges, regex patterns, and allowlists. It raises SchemaErrors with a structured failure report when validation fails.
What does lazy=True do in pandera?
By default pandera raises on the first failing row. With lazy=True it runs every check on every row, then raises a single SchemaErrors that contains all failures in the failure_cases DataFrame - so you see every problem at once.
How do I validate CSV data with pandera?
Read the CSV with pd.read_csv, declare a pa.DataFrameSchema with coerce=True (to cast string columns to their declared dtype), and call schema.validate(df, lazy=True). Catch pa.errors.SchemaErrors and inspect e.failure_cases for the details.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →