Repository: NeurodataWithoutBorders/nwbinspector

Issue:
[Bug]: Inaccurate critical error for 2D SpikeEventSeries
### What happened?

According to the [nwb-schema](https://github.com/NeurodataWithoutBorders/nwb-schema/blob/d5a931d2cc0ae059d917705b7583136d756a2c8b/core/nwb.ecephys.yaml#L68-L84), a SpikeEventSeries can have dimensions [num events, num samples] when it contains data from a single electrode. The other option is to have dimensions [num events, num channels, num samples] if it has data from multiple electrodes. 

However if an NWB file contains a 2D SpikeEventSeries, the inspector will return a critical error that the second dimension does not match the length of electrodes:

```
0  CRITICAL
===========

0.0  createESandSES.nwb: check_electrical_series_dims - 'SpikeEventSeries' object at location '/acquisition/spikedata1'
       Message: The second dimension of data does not match the length of electrodes. Your data may be transposed.

```


I think the check should be updated to warn about potentially transposed data only if a SpikeEventSeries has 3 dimensions.

### Operating System

macOS

### Python Version

None

### Were you streaming with ROS3?

None

### Package Versions

<details> Copy and paste your output here </details>

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/NeurodataWithoutBorders/nwbinspector/blob/dev/.github/CODE_OF_CONDUCT.rst)
- [X] Have you ensured this bug was not already [reported](https://github.com/neurodatawithoutborders/nwbinspector/issues)?
- [X] To the best of your ability, have you ensured this is a bug within the code that checks the NWBFile, rather than a bug in the NWBFile reader (e.g., [PyNWB](https://github.com/NeurodataWithoutBorders/pynwb) or [MatNWB](https://github.com/NeurodataWithoutBorders/matnwb))?

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 321c789..4123f96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,13 @@
+# v0.6.4 (Upcoming)
+
+### Fixes
+* Fix dimensionality check for SpikeEventSeries validation [#581](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/581)
+
+
 # v0.6.3 (March 13, 2025)
 
 ### Improvements
-* Added check for negative rates in TimeSeries [#423](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/XXX)
+* Added check for negative rates in TimeSeries [#423](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/423)
 * Added check for ascending spike times in Units table [#575](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/575)
 * Added support for PyNWB 3.0 [#557](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/557)
 * Added support for Python 3.13 [#564](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/564)
diff --git a/src/nwbinspector/checks/_ecephys.py b/src/nwbinspector/checks/_ecephys.py
index dd0a3e9..bbbde60 100644
--- a/src/nwbinspector/checks/_ecephys.py
+++ b/src/nwbinspector/checks/_ecephys.py
@@ -3,7 +3,7 @@
 from typing import Optional
 
 import numpy as np
-from pynwb.ecephys import ElectricalSeries
+from pynwb.ecephys import ElectricalSeries, SpikeEventSeries
 from pynwb.misc import Units
 
 from .._registration import Importance, InspectorMessage, register_check
@@ -39,6 +39,26 @@ def check_electrical_series_dims(electrical_series: ElectricalSeries) -> Optiona
     electrodes = electrical_series.electrodes
 
     data_shape = get_data_shape(data, strict_no_data_load=True)
+
+    # For SpikeEventSeries, only perform the check for 3D data
+    if isinstance(electrical_series, SpikeEventSeries):
+        if data_shape and len(data_shape) == 3 and data_shape[1] != len(electrodes.data):
+            if data_shape[0] == len(electrodes.data):
+                return InspectorMessage(
+                    message=(
+                        "The second dimension of data does not match the length of electrodes, "
+                        "but instead the first does. Data is oriented incorrectly and should be transposed."
+                    )
+                )
+            return InspectorMessage(
+                message=(
+                    "The second dimension of data does not match the length of electrodes. Your data may be transposed."
+                )
+            )
+        # Do not warn for 2D SpikeEventSeries
+        return None
+
+    # For other ElectricalSeries, keep the original logic
     if data_shape and len(data_shape) == 2 and data_shape[1] != len(electrodes.data):
         if data_shape[0] == len(electrodes.data):
             return InspectorMessage(
@@ -49,7 +69,7 @@ def check_electrical_series_dims(electrical_series: ElectricalSeries) -> Optiona
             )
         return InspectorMessage(
             message=(
-                "The second dimension of data does not match the length of electrodes. Your " "data may be transposed."
+                "The second dimension of data does not match the length of electrodes. Your data may be transposed."
             )
         )
 


Test patch:
diff --git a/tests/unit_tests/test_ecephys.py b/tests/unit_tests/test_ecephys.py
index 5887aee..6f4c22f 100644
--- a/tests/unit_tests/test_ecephys.py
+++ b/tests/unit_tests/test_ecephys.py
@@ -5,7 +5,7 @@ from uuid import uuid4
 import numpy as np
 from hdmf.common.table import DynamicTable, DynamicTableRegion
 from pynwb import NWBFile
-from pynwb.ecephys import ElectricalSeries
+from pynwb.ecephys import ElectricalSeries, SpikeEventSeries
 from pynwb.misc import Units
 
 from nwbinspector import Importance, InspectorMessage
@@ -156,6 +156,46 @@ class TestCheckElectricalSeries(TestCase):
         )
 
 
+def test_spikeeventseries_dims_check():
+    """
+    Test that 2D SpikeEventSeries does not trigger a warning,
+    but 3D SpikeEventSeries with mismatched electrodes does.
+    """
+
+    nwbfile = NWBFile(session_description="", identifier=str(uuid4()), session_start_time=datetime.now().astimezone())
+    device = nwbfile.create_device(name="dev")
+    group = nwbfile.create_electrode_group(name="electrode_group", description="desc", location="loc", device=device)
+    for _ in range(3):
+        nwbfile.add_electrode(x=3.0, y=3.0, z=3.0, imp=-1.0, location="unknown", filtering="unknown", group=group)
+    electrodes = nwbfile.create_electrode_table_region(region=[0, 1, 2], description="three elecs")
+
+    # 2D data: [num_events, num_samples] (should NOT trigger warning)
+    ses_2d = SpikeEventSeries(
+        name="spike_events_2d",
+        data=np.zeros((10, 5)),
+        electrodes=electrodes,
+        timestamps=[0.1 * i for i in range(10)],
+    )
+    assert check_electrical_series_dims(ses_2d) is None
+
+    # 3D data: [num_events, num_channels, num_samples] with mismatched num_channels (should trigger warning)
+    ses_3d = SpikeEventSeries(
+        name="spike_events_3d",
+        data=np.zeros((10, 4, 5)),  # 4 != 3 electrodes
+        electrodes=electrodes,
+        timestamps=[0.1 * i for i in range(10)],
+    )
+    result = check_electrical_series_dims(ses_3d)
+    assert result == InspectorMessage(
+        message=("The second dimension of data does not match the length of electrodes. Your data may be transposed."),
+        importance=Importance.CRITICAL,
+        check_function_name="check_electrical_series_dims",
+        object_type="SpikeEventSeries",
+        object_name="spike_events_3d",
+        location="/",
+    )
+
+
 def test_check_spike_times_not_in_unobserved_interval_pass():
     units_table = Units(name="TestUnits")
     units_table.add_unit(spike_times=[1, 2, 3], obs_intervals=[[0, 2.5], [2.7, 3.5]])