Repository: nilearn/nilearn

Issue:
Documentation of threshold_img unclear
1.) From the documentation of `threshold_img` it's unclear whether features below threshold will be kept or just set to zero. When I first saw the documentation I thought that `threshold_img` is a feature reduction procedure and will return a feature reduced subset. I had to find out by myself that features below threshold will be set to zero. The used phrase _"will be kept"_ also leads to the false idea that `threshold_img` is a feature reduction procedure.

2.) If I read the source code right `img_data[np.abs(img_data) < cutoff_threshold] = 0.)`, all features below threshold will be set to zero. This logically implies that all other values greater **or equal** to threshold will be kept. Therefore, the sentence 

> _The voxels which have intensities greater than this score will be kept._ 

should be changed to

>  _The voxels which have intensities greater or equal than this score will be kept._

3.) It's very nice that `threshold_img` can also handle percentiles because otherwise you always have to inspect the data first (or look at descriptive stats) to find a good voxel intensity value as threshold. However it would be nice to give an explanation of how the percentile rank is calculated behind the scenes. Again if I read the source code right the given percentile string (e.g. '80%') is converted to a percentile rank using `scipy.stats.scoreatpercentile`. It would be nice to give a reference to this function and to document which keyword-arguments of `scipy.stats.scoreatpercentile` are used when `check_threshold` is called. 



Discussion:
Thx for the feedback. I guess than nobody can improve this better than you ;-) 
Would you open a PR ?
I'm just a bit unsure about your last point: I don't think that the docstring should be too technical. 
Concerning `threshold_img`, is it also a design decision for it to mutate the data instead of returning a copy, in contrast to what the other `image` functions do?

```python
from nilearn import datasets, image
mni = datasets.load_mni152_brain_mask()
image.threshold_img(mni, 1000)
# mni is now thresholded.
```

This should probably be documented as well.
> Concerning threshold_img, is it also a design decision for it to mutate the
> data instead of returning a copy, in contrast to what the other image functions
> do?

No, that's a bug. There should be a "copy=False" argument to control this
behavior.

I'd have thought so. Opening #2091
going through the backlog

I believe this can be closed given the fixing PR was merged.
@Remi-Gau I think the original issue about the docstring of `threshold_img` being unclear has not been addressed. (It seems that another issue was brought up under this and then resolved). If we look at the docstring here https://github.com/nilearn/nilearn/blob/main/nilearn/image/image.py#L863-L871 we can see that the 3 points in the original post have not been taken into account. Would you be interested in opening a PR to improve the docstring?
Will give it a go. 

Resolving patch:
diff --git a/nilearn/image/image.py b/nilearn/image/image.py
--- a/nilearn/image/image.py
+++ b/nilearn/image/image.py
@@ -861,14 +861,19 @@ def threshold_img(
         Image containing statistical or atlas maps which should be thresholded.
 
     threshold : :obj:`float` or :obj:`str`
-        If float, we threshold the image based on image intensities meaning
-        voxels which have intensities greater than this value will be kept.
-        The given value should be within the range of minimum and
-        maximum intensity of the input image.
-        If string, it should finish with percent sign e.g. "80%" and we threshold
-        based on the score obtained using this percentile on the image data. The
-        voxels which have intensities greater than this score will be kept.
+        Voxels with intensities less than the requested threshold
+        will be set to zero.
+        Those with intensities greater or equal than the requested threshold
+        will keep their original value.
+        If float, we threshold the image based on image intensities.
+        The given value should be within the range of minimum and maximum
+        intensity of the input image.
+        If string, it should finish with percent sign e.g. "80%"
+        and we threshold based on the score obtained
+        using this percentile on the image data.
         The given string should be within the range of "0%" to "100%".
+        The percentile rank is computed using
+        :func:`scipy.stats.scoreatpercentile`.
 
     cluster_threshold : :obj:`float`, optional
         Cluster size threshold, in voxels. In the returned thresholded map,


Test patch: