diff --git a/CHANGELOG.md b/CHANGELOG.md index b407d934f..b6d676a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Upcoming +### New Checks + +* Added `check_rate_is_not_zero` for ensuring non-zero rate value of `TimeSeries` that has more than one frame. [#389](https://github.com/NeurodataWithoutBorders/nwbinspector/issues/389) + # v0.4.30 ### Fixes diff --git a/docs/best_practices/time_series.rst b/docs/best_practices/time_series.rst index 2c673a186..e40355145 100644 --- a/docs/best_practices/time_series.rst +++ b/docs/best_practices/time_series.rst @@ -157,3 +157,14 @@ Unknown Resolution If the ``resolution`` of a :ref:`nwb-schema:sec-TimeSeries` is unknown, use ``-1.0`` or ``NaN`` to indicate this. Check function: :py::meth:`~nwbinspector.checks.time_series.check_resolution` + + + +.. _best_practice_non_zero_rate: + +Zero Rate +~~~~~~~~~ + +If the ``data`` field of :ref:`nwb-schema:sec-TimeSeries` has more than one frame, and according to :ref:`best_practice_data_orientation` this axis ought to be time, then the ``rate`` field should not be ``0.0``. + +Check function: :py::meth:`~nwbinspector.checks.time_series.check_rate_is_not_zero` diff --git a/src/nwbinspector/checks/time_series.py b/src/nwbinspector/checks/time_series.py index f1ef94170..05b958c18 100644 --- a/src/nwbinspector/checks/time_series.py +++ b/src/nwbinspector/checks/time_series.py @@ -129,3 +129,14 @@ def check_resolution(time_series: TimeSeries): return InspectorMessage( message=f"'resolution' should use -1.0 or NaN for unknown instead of {time_series.resolution}." ) + + +@register_check(importance=Importance.CRITICAL, neurodata_type=TimeSeries) +def check_rate_is_not_zero(time_series: TimeSeries): + if time_series.data is None: + return + data_shape = get_data_shape(time_series.data) + if time_series.rate == 0.0 and data_shape[0] > 1: + return InspectorMessage( + f"{time_series.name} has a sampling rate value of 0.0Hz but the series has more than one frame." + ) diff --git a/tests/unit_tests/test_time_series.py b/tests/unit_tests/test_time_series.py index 3285acc69..fe06bcd31 100644 --- a/tests/unit_tests/test_time_series.py +++ b/tests/unit_tests/test_time_series.py @@ -15,6 +15,7 @@ check_missing_unit, check_resolution, check_timestamp_of_the_first_sample_is_not_negative, + check_rate_is_not_zero, ) from nwbinspector.tools import make_minimal_nwbfile from nwbinspector.testing import check_streaming_tests_enabled @@ -199,6 +200,28 @@ def test_check_timestamps_empty_timestamps(): ) +def test_check_rate_is_not_zero_pass(): + time_series = pynwb.TimeSeries(name="test", unit="test_units", data=[1, 2, 3], rate=4.0) + assert check_rate_is_not_zero(time_series) is None + + +def test_check_rate_is_not_zero_single_frame_pass(): + time_series = pynwb.TimeSeries(name="test", unit="test_units", data=[1], rate=0.0) + assert check_rate_is_not_zero(time_series) is None + + +def test_check_rate_is_not_zero_fail(): + time_series = pynwb.TimeSeries(name="TimeSeriesTest", unit="n.a.", data=[1, 2, 3], rate=0.0) + assert check_rate_is_not_zero(time_series) == InspectorMessage( + message="TimeSeriesTest has a sampling rate value of 0.0Hz but the series has more than one frame.", + importance=Importance.CRITICAL, + check_function_name="check_rate_is_not_zero", + object_type="TimeSeries", + object_name="TimeSeriesTest", + location="/", + ) + + def test_pass_check_timestamps_ascending_pass(): time_series = pynwb.TimeSeries(name="test_time_series", unit="test_units", data=[1, 2, 3], timestamps=[1, 2, 3]) assert check_timestamps_ascending(time_series) is None