Pydre ships with a library of built-in metric functions. When those don't cover your needs, you can write your own. Custom metrics in Pydre allow you to extract meaningful data from driving simulation data. Here's how to create one:
Basic Metric Structure
A custom metric is a function decorated with @registerMetric() that takes a DriveData object as its first parameter and returns a numeric value or collection of values.
Step-by-step guide
1. Create a custom metrics directory
Add a directory for your custom metrics inside your project:
my_project/
├── custom_metrics/
│ └── custom.py
├── data/
│ └── drive_data.dat
└── my_project.toml
2. Write your custom metric
Create a Python file in the directory. Each metric is a function decorated with @registerMetric() that takes a DriveData object as its first parameter and returns a numeric value (or a list of values for multi-column metrics).
from typing import Optional
import polars as pl
import pydre.core
from pydre.core import ColumnsMatchError
from pydre.metrics import registerMetric
from loguru import logger
@registerMetric()
def testMean(
drivedata: pydre.core.DriveData, var: str, cutoff: Optional[float] = None
) -> Optional[float]:
"""Mean of a column, optionally filtered to values above a cutoff.
Parameters:
var: Name of the column to average.
cutoff: If provided, only values >= cutoff are included.
Note: Requires data columns
- var: must be numeric
Returns:
Mean of the (filtered) column, or None if the column is non-numeric.
"""
try:
drivedata.checkColumnsNumeric([var])
except ColumnsMatchError:
logger.warning(f"testMean: column '{var}' is not numeric, returning None")
return None
if cutoff is not None:
return (
drivedata.data.get_column(var)
.filter(drivedata.data.get_column(var) >= cutoff)
.mean()
)
else:
return drivedata.data.get_column(var).mean()
Best Practices:
- Always validate required columns exist before processing
- Use robust error handling with clear log messages
- Return
Nonewhen calculation fails rather than raising exceptions - Include comprehensive docstrings with parameter descriptions
- Use polars operations when possible for performance
4. Configure Your Project File
Include your custom metrics directory in the config section of your project file.
[config]
custom_metrics_dirs = ["custom_metric"]
[metrics.custom_test]
function = "testMean"
var = "XPos"
5. Run Pydre with Your Custom Metrics
Run Pydre with your project file:
uv run pydre -p examples/custom_project/custom_test.toml -d examples/custom_project/data/Experimenter_S1_Tutorial_11002233.dat -o custom.csv
How It Works
- Pydre loads all metrics defined in its core library
- It then searches the directory paths specified in
custom_metrics_dirs - For each Python file in those directories, it dynamically imports the metrics
- The
@registerMetric()decorator automatically registers your custom metrics with pydre - Your metrics become available for use in the project configuration
Tips
- Ensure your custom metrics follow the same pattern as built-in metrics
- Add proper docstrings to document required columns and return values
- Always include error handling for missing columns using
checkColumnsorcheckColumnsNumeric - Use type hints to document your function parameters and return values
With this approach, you can maintain your custom metrics separately from the Pydre codebase while still using them in your projects.
Advanced Metrics
Multiple Return Values
For metrics that return multiple values, specify column names in the decorator:
@registerMetric(columnnames=["value1", "value2"])
def multiValueMetric(drivedata: pydre.core.DriveData) -> list[float]:
# ...
return [value1, value2]
Custom Metric Names
Override the function name for metric registration:
@registerMetric(metricname="betterMetricName")
def internal_function_name(drivedata: pydre.core.DriveData) -> float:
# ...
Custom filters
Custom filters can be created in a similar way to custom metrics. the search path for custom filters is similar to the custom metrics search path: "custom_filters_dirs". Additionally, @registerFilter() instead of @registerMetric() is used as the decorator.
Example
[config]
datafiles = ["data/drive_data.dat"]
outputfile = "results.csv"
custom_metrics_dirs = ["custom_metrics"]
custom_filters_dirs = ["custom_filters"]
Next: Setting up Just