Repository: nilearn/nilearn

Issue:
plot_surf_roi should raise error when negative or floating-point values are passed
plot_surf_roi is for showing atlas region indices, so it is meant to deal with positive integers only.
we should probably raise an error when negative or floating-point values are passed to this function

_Originally posted by @jeromedockes in https://github.com/nilearn/nilearn/issues/4082#issuecomment-1787355353_
            


Discussion:
> _Originally posted by @jeromedockes in https://github.com/nilearn/nilearn/issues/4082#issuecomment-1787355353_

oops sorry Yasmin we said that I would open an issue and I didn't! thanks for taking care of it :)

No problem :)
I can take this one if no one is working on it right now
@michellewang sure that'd be great thanks! 

Resolving patch:
diff --git a/nilearn/plotting/surf_plotting.py b/nilearn/plotting/surf_plotting.py
--- a/nilearn/plotting/surf_plotting.py
+++ b/nilearn/plotting/surf_plotting.py
@@ -1628,6 +1628,7 @@ def plot_surf_roi(surf_mesh,
     # messages in case of wrong inputs
 
     roi = load_surf_data(roi_map)
+    idx_not_na = ~np.isnan(roi)
     if vmin is None:
         vmin = np.nanmin(roi)
     if vmax is None:
@@ -1644,6 +1645,24 @@ def plot_surf_roi(surf_mesh,
                          'ROI you can convert them into a ROI map like this:\n'
                          'roi_map = np.zeros(n_vertices)\n'
                          'roi_map[roi_idx] = 1')
+    if (roi < 0).any():
+        # TODO raise ValueError in release 0.13
+        warn(
+            (
+                'Negative values in roi_map will no longer be allowed in'
+                ' Nilearn version 0.13'
+            ),
+            DeprecationWarning,
+        )
+    if not np.array_equal(roi[idx_not_na], roi[idx_not_na].astype(int)):
+        # TODO raise ValueError in release 0.13
+        warn(
+            (
+                'Non-integer values in roi_map will no longer be allowed in'
+                ' Nilearn version 0.13'
+            ),
+            DeprecationWarning,
+        )
 
     if cbar_tick_format == "auto":
         cbar_tick_format = "." if engine == "plotly" else "%i"


Test patch:
diff --git a/nilearn/plotting/tests/test_surf_plotting.py b/nilearn/plotting/tests/test_surf_plotting.py
--- a/nilearn/plotting/tests/test_surf_plotting.py
+++ b/nilearn/plotting/tests/test_surf_plotting.py
@@ -707,7 +707,7 @@ def _generate_data_test_surf_roi():
     roi_idx = _rng().integers(0, mesh[0].shape[0], size=10)
     roi_map = np.zeros(mesh[0].shape[0])
     roi_map[roi_idx] = 1
-    parcellation = _rng().uniform(size=mesh[0].shape[0])
+    parcellation = _rng().integers(100, size=mesh[0].shape[0]).astype(float)
     return mesh, roi_map, parcellation
 
 
@@ -784,13 +784,38 @@ def test_plot_surf_roi_matplotlib_specific():
 def test_plot_surf_roi_error(engine, rng):
     if not is_plotly_installed() and engine == "plotly":
         pytest.skip('Plotly is not installed; required for this test.')
-    mesh = generate_surf()
+    mesh, roi_map, _ = _generate_data_test_surf_roi()
+
+    # too many axes
+    with pytest.raises(
+        ValueError,
+            match="roi_map can only have one dimension but has"):
+        plot_surf_roi(
+            mesh, roi_map=np.array([roi_map, roi_map]), engine=engine)
+
+    # wrong number of vertices
     roi_idx = rng.integers(0, mesh[0].shape[0], size=5)
     with pytest.raises(
             ValueError,
             match='roi_map does not have the same number of vertices'):
         plot_surf_roi(mesh, roi_map=roi_idx, engine=engine)
 
+    # negative value in roi map
+    roi_map[0] = -1
+    with pytest.warns(
+        DeprecationWarning,
+        match="Negative values in roi_map will no longer be allowed",
+    ):
+        plot_surf_roi(mesh, roi_map=roi_map, engine=engine)
+
+    # float value in roi map
+    roi_map[0] = 1.2
+    with pytest.warns(
+        DeprecationWarning,
+        match="Non-integer values in roi_map will no longer be allowed",
+    ):
+        plot_surf_roi(mesh, roi_map=roi_map, engine=engine)
+
 
 @pytest.mark.skipif(not is_plotly_installed(),
                     reason=("This test only runs if Plotly is installed."))