df-eval
A lightweight expression evaluation engine for pandas DataFrames, supporting schema-driven derived columns and external lookups.
Overview
df-eval is a Python library that provides a flexible and efficient way to evaluate expressions on pandas DataFrames. It's designed for scenarios where you need to:
- Apply complex transformations to DataFrames using string expressions
- Define schemas of derived columns that depend on existing columns
- Register custom functions (UDFs) and constants for use in expressions
- Use safe, allow-listed functions (abs, log, exp, sqrt, clip, where, isna, fillna)
- Handle dependencies between derived columns with automatic topological ordering
- Perform lookups from external data sources (files, databases, HTTP APIs)
- Track provenance of derived columns
- Maintain clean, readable code for data transformations
Features
- Safe Expression Evaluation: Allow-listed vectorized functions for secure evaluation
- UDF and Constant Registry: Register custom functions and constants
- Schema-Driven Columns: Define multiple derived columns with automatic dependency resolution
- Topological Ordering: Automatically resolve dependencies between columns
- Cycle Detection: Detect and report circular dependencies
- Dtype Casting: Specify output types for derived columns
- Provenance Tracking: Track the origin and dependencies of derived columns
- Lookup Functionality: Resolve values from external sources with caching
- Type-Safe: Built with Python 3.11+ type hints
- Well-Tested: Comprehensive test suite with 95%+ coverage
- Well-Documented: Full documentation with Sphinx
- Backend Seam: Designed for future Arrow/Polars support
Installation
pip install df-eval
For development:
git clone https://github.com/elphick/df-eval.git
cd df-eval
uv sync
Quick Start
Basic Expression Evaluation
import pandas as pd
from df_eval import Engine
# Create a DataFrame
df = pd.DataFrame({
"a": [1, 2, 3],
"b": [4, 5, 6]
})
# Create an engine
engine = Engine()
# Evaluate an expression
result = engine.evaluate(df, "a + b")
print(result) # [5, 7, 9]
Schema-Driven Derived Columns
# Define a schema with dependent columns
schema = {
"sum": "a + b",
"product": "a * b",
"ratio": "a / b",
"ratio_2dp": "round(a / b, 2)",
"ratio_bucket": "floor((a / b) * 10)"
}
df_with_derived = engine.apply_schema(df, schema)
print(df_with_derived)
You can also use a mapping spec for richer per-column options, including
decimals (rounding) and alias (rename from an incoming source column):
schema = {
"price_2dp": {"expr": "price", "decimals": 2},
"price": {"alias": "legacy_price"},
}
rounded = engine.apply_schema(df, schema)
Pandera Metadata Integration
With df-eval[pandera], you can define df-eval operations directly in Pandera
column metadata and run them with apply_pandera_schema.
drop=True is useful for helper columns that must exist during evaluation but
should not be returned in the final DataFrame.
import pandas as pd
import pandera.pandas as pa
from df_eval import Engine
schema = pa.DataFrameSchema(
{
"price": pa.Column(float, coerce=True),
"qty": pa.Column(int, coerce=True),
"subtotal": pa.Column(
float,
coerce=True,
metadata={"df-eval": {"expr": "price * qty", "drop": True}},
),
"taxed_total": pa.Column(
float,
coerce=True,
metadata={"df-eval": {"expr": "subtotal * 1.1", "decimals": 2}},
),
}
)
df = pd.DataFrame({"price": [10.0], "qty": [3]})
result = Engine().apply_pandera_schema(df, schema)
print(result.columns.tolist()) # ['price', 'qty', 'taxed_total']
Using Allow-Listed Safe Functions
# Use safe, allow-listed functions
schema = {
"abs_a": "abs(a)",
"log_b": "log(b)",
"sqrt_sum": "sqrt(a + b)",
"clipped": "clip(a, 0, 2)"
}
result = engine.apply_schema(df, schema)
Register Custom Functions (UDFs)
# Register a custom function
def custom_transform(x):
return x ** 2 + 10
engine.register_function("transform", custom_transform)
# Use it in expressions
result = engine.evaluate(df, "transform(a)")
Built-in Functions
The library provides several allow-listed safe functions:
abs(x): Absolute valuelog(x): Natural logarithm (handles negative values safely)exp(x): Exponential function (handles overflow safely)sqrt(x): Square root (handles negative values safely)round(x, decimals=0): Round to a fixed number of decimal placesceil(x): Ceiling valuefloor(x): Floor valueclip(x, min, max): Clip values to a rangewhere(condition, x, y): Conditional selectionisna(x): Check for NaN/None valuesfillna(x, value): Fill NaN/None with a valuesafe_divide(a, b): Division with NaN for divide-by-zerocoalesce(*args): Return first non-null value
Documentation
For comprehensive documentation including advanced usage, API reference, and more examples, visit the full documentation.
Requirements
- Python 3.11 or higher
- pandas >= 2.0.0
- numpy >= 1.26.0
Development
Running Tests
uv run pytest
Building Documentation
cd docs
uv run sphinx-build -b html . _build/html
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Metadata
Release files for df-eval 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| df_eval-0.4.1.tar.gz | 168.9 kB | With provenance | Details for df_eval-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| df_eval-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for df_eval-0.4.1-py3-none-any.whl |
Total release size: 199.3 kB
Release files / df_eval-0.4.1.tar.gz
| Download URL | df_eval-0.4.1.tar.gz |
|---|---|
| Size | 168.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
71986ea31a1326f45f0bc631b407589fbfa49e6541c94f028cfe1593828e18b9
|
|
BLAKE2b-256 checksum How to use checksums |
d52d8157a74160e6d5535dbc9e37d6b5895353ee42dda9e22913f817ac35b938
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logRelease files / df_eval-0.4.1-py3-none-any.whl
| Download URL | df_eval-0.4.1-py3-none-any.whl |
|---|---|
| Size | 30.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
57a769ea013d66fa493eadc85200b2495863c8173c0679ec294550482c7e9c2d
|
|
BLAKE2b-256 checksum How to use checksums |
b738316af149d66ef68db249c12060709a4ed305642afb90bce09d9c83e2330d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logSecurity for df-eval 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for df_eval-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.