-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
General validator function for checking dimensions and coordinates (#294
) * replace time dim validator with more generic validator * constrain kinematic functions to cartesian coordinates * renamed new validator to validate_dims_coords * add examples in validator docstring * unit tests for the new validator * Apply suggestions from code review do note validate x,y space coordinates specifically. Co-authored-by: Chang Huan Lo <[email protected]> * reuse fixture valid_poses_dataset_uniform_linear_motion * combine two unit tests into one * expose public `compute_time_derivative` function * Refactor test * Update refs to `compute_time_derivative` --------- Co-authored-by: Chang Huan Lo <[email protected]>
- Loading branch information
Showing
5 changed files
with
151 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
"""Validators for data arrays.""" | ||
|
||
import xarray as xr | ||
|
||
from movement.utils.logging import log_error | ||
|
||
|
||
def validate_dims_coords( | ||
data: xr.DataArray, required_dim_coords: dict | ||
) -> None: | ||
"""Validate dimensions and coordinates in a data array. | ||
This function raises a ValueError if the specified dimensions and | ||
coordinates are not present in the input data array. | ||
Parameters | ||
---------- | ||
data : xarray.DataArray | ||
The input data array to validate. | ||
required_dim_coords : dict | ||
A dictionary of required dimensions and their corresponding | ||
coordinate values. If you don't need to specify any | ||
coordinate values, you can pass an empty list. | ||
Examples | ||
-------- | ||
Validate that a data array contains the dimension 'time'. No specific | ||
coordinates are required. | ||
>>> validate_dims_coords(data, {"time": []}) | ||
Validate that a data array contains the dimensions 'time' and 'space', | ||
and that the 'space' dimension contains the coordinates 'x' and 'y'. | ||
>>> validate_dims_coords(data, {"time": [], "space": ["x", "y"]}) | ||
Raises | ||
------ | ||
ValueError | ||
If the input data does not contain the required dimension(s) | ||
and/or the required coordinate(s). | ||
""" | ||
missing_dims = [dim for dim in required_dim_coords if dim not in data.dims] | ||
error_message = "" | ||
if missing_dims: | ||
error_message += ( | ||
f"Input data must contain {missing_dims} as dimensions.\n" | ||
) | ||
missing_coords = [] | ||
for dim, coords in required_dim_coords.items(): | ||
missing_coords = [ | ||
coord for coord in coords if coord not in data.coords.get(dim, []) | ||
] | ||
if missing_coords: | ||
error_message += ( | ||
"Input data must contain " | ||
f"{missing_coords} in the '{dim}' coordinates." | ||
) | ||
if error_message: | ||
raise log_error(ValueError, error_message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import re | ||
from contextlib import nullcontext as does_not_raise | ||
|
||
import pytest | ||
|
||
from movement.validators.arrays import validate_dims_coords | ||
|
||
|
||
def expect_value_error_with_message(error_msg): | ||
"""Expect a ValueError with the specified error message.""" | ||
return pytest.raises(ValueError, match=re.escape(error_msg)) | ||
|
||
|
||
valid_cases = [ | ||
({"time": []}, does_not_raise()), | ||
({"time": [0, 1]}, does_not_raise()), | ||
({"space": ["x", "y"]}, does_not_raise()), | ||
({"time": [], "space": []}, does_not_raise()), | ||
({"time": [], "space": ["x", "y"]}, does_not_raise()), | ||
] # Valid cases (no error) | ||
|
||
invalid_cases = [ | ||
( | ||
{"spacetime": []}, | ||
expect_value_error_with_message( | ||
"Input data must contain ['spacetime'] as dimensions." | ||
), | ||
), | ||
( | ||
{"time": [0, 100], "space": ["x", "y"]}, | ||
expect_value_error_with_message( | ||
"Input data must contain [100] in the 'time' coordinates." | ||
), | ||
), | ||
( | ||
{"space": ["x", "y", "z"]}, | ||
expect_value_error_with_message( | ||
"Input data must contain ['z'] in the 'space' coordinates." | ||
), | ||
), | ||
] # Invalid cases (raise ValueError) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"required_dims_coords, expected_exception", | ||
valid_cases + invalid_cases, | ||
) | ||
def test_validate_dims_coords( | ||
valid_poses_dataset_uniform_linear_motion, # fixture from conftest.py | ||
required_dims_coords, | ||
expected_exception, | ||
): | ||
"""Test validate_dims_coords for both valid and invalid inputs.""" | ||
position_array = valid_poses_dataset_uniform_linear_motion["position"] | ||
with expected_exception: | ||
validate_dims_coords(position_array, required_dims_coords) |