Python library designed to validate Pandas and PySpark DataFrames using customizable, reusable expectations
Project description
🎯 DataFrameExpectations
DataFrameExpectations is a Python library designed to validate Pandas and PySpark DataFrames using customizable, reusable expectations. It simplifies testing in data pipelines and end-to-end workflows by providing a standardized framework for DataFrame validation.
Instead of using different validation approaches for DataFrames, this library provides a standardized solution for this use case. As a result, any contributions made here—such as adding new expectations—can be leveraged by all users of the library.
📚 View Documentation | 📋 List of Expectations
Installation:
pip install dataframe-expectations
Requirements
- Python 3.10+
- pandas >= 1.5.0
- pydantic >= 2.12.4
- pyspark >= 3.3.0
- tabulate >= 0.8.9
Development setup
To set up the development environment:
# 1. Clone the repository
git clone https://github.com/getyourguide/dataframe-expectations.git
cd dataframe-expectations
# 2. Install UV package manager
pip install uv
# 3. Install development dependencies (this will automatically create a virtual environment)
uv sync --group dev
# 4. (Optional) To explicitly activate the virtual environment:
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# 5. Run tests (this will run the tests in the virtual environment)
uv run pytest tests/ --cov=dataframe_expectations
Using the library
Basic usage with Pandas:
from dataframe_expectations.suite import DataFrameExpectationsSuite
import pandas as pd
# Build a suite with expectations
suite = (
DataFrameExpectationsSuite()
.expect_min_rows(min_rows=3)
.expect_max_rows(max_rows=10)
.expect_value_greater_than(column_name="age", value=18)
.expect_value_less_than(column_name="salary", value=100000)
.expect_value_not_null(column_name="name")
)
# Create a runner
runner = suite.build()
# Validate a DataFrame
df = pd.DataFrame({
"age": [25, 15, 45, 22],
"name": ["Alice", "Bob", "Charlie", "Diana"],
"salary": [50000, 60000, 80000, 45000]
})
runner.run(df)
PySpark example:
from dataframe_expectations.suite import DataFrameExpectationsSuite
from pyspark.sql import SparkSession
# Initialize Spark session
spark = SparkSession.builder.appName("example").getOrCreate()
# Build a validation suite (same API as Pandas!)
suite = (
DataFrameExpectationsSuite()
.expect_min_rows(min_rows=3)
.expect_max_rows(max_rows=10)
.expect_value_greater_than(column_name="age", value=18)
.expect_value_less_than(column_name="salary", value=100000)
.expect_value_not_null(column_name="name")
)
# Build the runner
runner = suite.build()
# Create a PySpark DataFrame
data = [
{"age": 25, "name": "Alice", "salary": 50000},
{"age": 15, "name": "Bob", "salary": 60000},
{"age": 45, "name": "Charlie", "salary": 80000},
{"age": 22, "name": "Diana", "salary": 45000}
]
df = spark.createDataFrame(data)
# Validate
runner.run(df)
Decorator pattern for automatic validation:
from dataframe_expectations.suite import DataFrameExpectationsSuite
from pyspark.sql import SparkSession
# Initialize Spark session
spark = SparkSession.builder.appName("example").getOrCreate()
suite = (
DataFrameExpectationsSuite()
.expect_min_rows(min_rows=3)
.expect_max_rows(max_rows=10)
.expect_value_greater_than(column_name="age", value=18)
.expect_value_less_than(column_name="salary", value=100000)
.expect_value_not_null(column_name="name")
)
# Build the runner
runner = suite.build()
# Apply decorator to automatically validate function output
@runner.validate
def load_employee_data():
"""Load and return employee data - automatically validated."""
return spark.createDataFrame(
[
{"age": 25, "name": "Alice", "salary": 50000},
{"age": 15, "name": "Bob", "salary": 60000},
{"age": 45, "name": "Charlie", "salary": 80000},
{"age": 22, "name": "Diana", "salary": 45000}
]
)
# Function execution automatically validates the returned DataFrame
df = load_employee_data() # Raises DataFrameExpectationsSuiteFailure if validation fails
# Allow functions that may return None
@runner.validate(allow_none=True)
def conditional_load(should_load: bool):
"""Conditionally load data - validation only runs when DataFrame is returned."""
if should_load:
return spark.createDataFrame([{"age": 25, "name": "Alice", "salary": 50000}])
return None # No validation when None is returned
Output:
========================== Running expectations suite ==========================
ExpectationMinRows (DataFrame contains at least 3 rows) ... OK
ExpectationMaxRows (DataFrame contains at most 10 rows) ... OK
ExpectationValueGreaterThan ('age' is greater than 18) ... FAIL
ExpectationValueLessThan ('salary' is less than 100000) ... OK
ExpectationValueNotNull ('name' is not null) ... OK
============================ 4 success, 1 failures =============================
ExpectationSuiteFailure: (1/5) expectations failed.
================================================================================
List of violations:
--------------------------------------------------------------------------------
[Failed 1/1] ExpectationValueGreaterThan ('age' is greater than 18): Found 1 row(s) where 'age' is not greater than 18.
Some examples of violations:
+-----+------+--------+
| age | name | salary |
+-----+------+--------+
| 15 | Bob | 60000 |
+-----+------+--------+
================================================================================
How to contribute?
Contributions are welcome! You can enhance the library by adding new expectations, refining existing ones, or improving the testing framework.
Versioning
This project follows Semantic Versioning (SemVer) and uses Release Please for automated version management.
Versions are automatically determined based on Conventional Commits:
feat:- New feature → MINOR version bump (0.1.0 → 0.2.0)fix:- Bug fix → PATCH version bump (0.1.0 → 0.1.1)feat!:orBREAKING CHANGE:- Breaking change → MAJOR version bump (0.1.0 → 1.0.0)chore:,docs:,style:,refactor:,test:,ci:- No version bump
Example commits:
git commit -m "feat: add new expectation for null values"
git commit -m "fix: correct validation logic in expect_value_greater_than"
git commit -m "feat!: remove deprecated API methods"
When changes are pushed to the main branch, Release Please automatically:
- Creates or updates a Release PR with version bump and changelog
- When merged, creates a GitHub Release and publishes to PyPI
No manual version updates needed - just use conventional commit messages!
Security
For security issues please contact security@getyourguide.com.
Legal
dataframe-expectations is licensed under the Apache License, Version 2.0. See LICENSE for the full text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dataframe_expectations-0.4.0.tar.gz.
File metadata
- Download URL: dataframe_expectations-0.4.0.tar.gz
- Upload date:
- Size: 35.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3236d56ca459a81b8b71fb7fac6eeed4b33f27648c2a729fd15ac46e1f3d2ab9
|
|
| MD5 |
a5714c1e790412b501217308227d3b49
|
|
| BLAKE2b-256 |
bb4a1c216948948b28c35a1e6c45e1a7f29f6cf5ef9fdcbba942fe507dd4e2ca
|
Provenance
The following attestation bundles were made for dataframe_expectations-0.4.0.tar.gz:
Publisher:
publish.yaml on getyourguide/dataframe-expectations
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dataframe_expectations-0.4.0.tar.gz -
Subject digest:
3236d56ca459a81b8b71fb7fac6eeed4b33f27648c2a729fd15ac46e1f3d2ab9 - Sigstore transparency entry: 687257659
- Sigstore integration time:
-
Permalink:
getyourguide/dataframe-expectations@a110fafe2ef71b68c3bef9b4d7bafca130caaa17 -
Branch / Tag:
refs/tags/v0.4.0_temp - Owner: https://github.com/getyourguide
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@a110fafe2ef71b68c3bef9b4d7bafca130caaa17 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dataframe_expectations-0.4.0-py3-none-any.whl.
File metadata
- Download URL: dataframe_expectations-0.4.0-py3-none-any.whl
- Upload date:
- Size: 38.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc0bf0e76e245e2a932a19a1cbe34aa2cb6eab4b615d1438da5053e135495e11
|
|
| MD5 |
23a5deaab4585834eb0c43a97fd21198
|
|
| BLAKE2b-256 |
bd7c2078cf54f56bfe696c1607feba0c5649981f14a2584432b98cb6a4fe5cbe
|
Provenance
The following attestation bundles were made for dataframe_expectations-0.4.0-py3-none-any.whl:
Publisher:
publish.yaml on getyourguide/dataframe-expectations
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dataframe_expectations-0.4.0-py3-none-any.whl -
Subject digest:
fc0bf0e76e245e2a932a19a1cbe34aa2cb6eab4b615d1438da5053e135495e11 - Sigstore transparency entry: 687257665
- Sigstore integration time:
-
Permalink:
getyourguide/dataframe-expectations@a110fafe2ef71b68c3bef9b4d7bafca130caaa17 -
Branch / Tag:
refs/tags/v0.4.0_temp - Owner: https://github.com/getyourguide
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@a110fafe2ef71b68c3bef9b4d7bafca130caaa17 -
Trigger Event:
push
-
Statement type: