Repository: nilearn/nilearn

Issue:
`permuted_ols` and `non_parametric_inference` are not well documented
<!--Describe your proposed enhancement in detail.-->
`permuted_ols` actually works for one-sample tests, which is not documented.
Also, the outputs are only -log_10( corrected_p_values), which is a bit limited too. It is also ill-documented.

<!--List any pages that would be impacted by the enhancement.-->
### Affected pages



Discussion:
I'd guess we want to update [this page](https://nilearn.github.io/modules/generated/nilearn.mass_univariate.permuted_ols.html) and [this one](https://nilearn.github.io/modules/generated/nilearn.glm.second_level.non_parametric_inference.html), as well as (optionally) the linked examples. 


Other instances worth reviewing:
- [Searchlight user guide](https://nilearn.github.io/stable/decoding/searchlight.html#comparing-to-massively-univariate-analysis-f-score-or-spm)

I also think there could be more information in the documentation about the differences between `permuted_ols`/`non_parametric_inference` and other multiple comparisons correction approaches (e.g., `threshold_stats_img` and `cluster_level_inference`).
In terms of outputs, would corrected z-values be a good addition?
No, we can correct p-values, but we should not correct z-values to avoid confusing users imho.
What other outputs would be useful? It would be great to have some kind of signed statistics, so users don't have to get the sign of the t-statistics and then multiply the -log10 p-values by those signs.
But we have the option to do a signed test, do we ?
This being said, I agree that signed statistics would be handy.
That's true, but the -log10 p-values are not signed, so users need to extract the signs from the t-statistic map.

What about adding an `output_type` parameter, as in `SecondLevelModel.compute_contrast`?

`permuted_ols` should be able to output parameter estimate (`effect_size`), variance (`effect_variance`), uncorrected t-statistic (`stat`), and uncorrected -log10 p-values (`p_value`), along with corrected -log10 p-values (additionally split based on the measure used for correction, in #3181).
Note that currently, as p-values, we return p-values not -log10 p-values. I'd try to be consistent with that.
By contrast, it's OK to return  corrected -log10 p-values, since this is what we do for non-parametric inference.
Now that the TFCE and cluster-level PRs have been merged, `permuted_ols` can return the following outputs:

- `t`
- `logp_max_t`
- `h0_max_t`
- `tfce`
- `logp_max_tfce`
- `h0_max_tfce`
- `size`
- `logp_max_size`
- `h0_max_size`
- `mass`
- `logp_max_mass`
- `h0_max_mass`

Is there anything else we want to return? Do we want to add `effect_size` and `effect_variance`?
Is there anything else we need for this issue, or can it be closed?
The documentation has not been reworked (e.g. the fact that it works for one-sample test).
Oh right, I forgot about that. Thanks for clarifying. So we just need an example or other documentation covering a one-sample test in `permuted_ols`.

Resolving patch:
diff --git a/examples/07_advanced/plot_localizer_mass_univariate_methods.py b/examples/07_advanced/plot_localizer_mass_univariate_methods.py
--- a/examples/07_advanced/plot_localizer_mass_univariate_methods.py
+++ b/examples/07_advanced/plot_localizer_mass_univariate_methods.py
@@ -60,6 +60,7 @@
 ##############################################################################
 # Anova (parametric F-scores)
 from sklearn.feature_selection import f_regression
+
 _, pvals_anova = f_regression(fmri_masked, tested_var, center=True)
 pvals_anova *= fmri_masked.shape[1]
 pvals_anova[np.isnan(pvals_anova)] = 1
@@ -74,8 +75,14 @@
 #
 # This method will produce both voxel-level FWE-corrected -log10 p-values and
 # :term:`TFCE`-based FWE-corrected -log10 p-values.
+#
+# .. note::
+#   :func:`~nilearn.mass_univariate.permuted_ols` can support a wide range
+#   of analysis designs, depending on the ``tested_var``.
+#   For example, if you wished to perform a one-sample test, you could
+#   simply provide an array of ones (e.g., ``np.ones(n_samples)``).
 ols_outputs = permuted_ols(
-    tested_var,
+    tested_var,  # this is equivalent to the design matrix, in array form
     fmri_masked,
     model_intercept=True,
     masker=nifti_masker,
diff --git a/nilearn/glm/second_level/second_level.py b/nilearn/glm/second_level/second_level.py
--- a/nilearn/glm/second_level/second_level.py
+++ b/nilearn/glm/second_level/second_level.py
@@ -638,16 +638,16 @@ def non_parametric_inference(
         If list of Niimg-like objects then this is taken literally as Y
         for the model fit and design_matrix must be provided.
 
-    confounds : :obj:`pandas.DataFrame`, optional
+    confounds : :obj:`pandas.DataFrame` or None, optional
         Must contain a subject_label column. All other columns are
         considered as confounds and included in the model. If
         ``design_matrix`` is provided then this argument is ignored.
         The resulting second level design matrix uses the same column
-        names as in the given :class:`~pandas.DataFrame` for confounds.
+        names as in the given :obj:`~pandas.DataFrame` for confounds.
         At least two columns are expected, ``subject_label`` and at
         least one confound.
 
-    design_matrix : :class:`pandas.DataFrame`, optional
+    design_matrix : :obj:`pandas.DataFrame` or None, optional
         Design matrix to fit the :term:`GLM`. The number of rows
         in the design matrix must agree with the number of maps derived
         from ``second_level_input``.


Test patch:
diff --git a/examples/05_glm_second_level/plot_second_level_one_sample_test.py b/examples/05_glm_second_level/plot_second_level_one_sample_test.py
--- a/examples/05_glm_second_level/plot_second_level_one_sample_test.py
+++ b/examples/05_glm_second_level/plot_second_level_one_sample_test.py
@@ -62,8 +62,11 @@
 ###############################################################################
 # Estimate second level model
 # ---------------------------
-# We define the input maps and the design matrix for the second level model
-# and fit it.
+# We wish to perform a one-sample test.
+# In order to do so, we need to create a design matrix that determines how
+# the analysis will be performed.
+# For a one-sample test, all we need to include in the design matrix is a
+# single column of ones, corresponding to the model intercept.
 import pandas as pd
 
 second_level_input = data['cmaps']
@@ -73,7 +76,7 @@
 )
 
 ###############################################################################
-# Model specification and fit.
+# Next, we specify the model and fit it.
 from nilearn.glm.second_level import SecondLevelModel
 
 second_level_model = SecondLevelModel(smoothing_fwhm=8.0)
@@ -85,7 +88,10 @@
 ###############################################################################
 # To estimate the :term:`contrast` is very simple. We can just provide the
 # column name of the design matrix.
-z_map = second_level_model.compute_contrast(output_type='z_score')
+z_map = second_level_model.compute_contrast(
+    second_level_contrast='intercept',
+    output_type='z_score',
+)
 
 ###############################################################################
 # We threshold the second level contrast at uncorrected p < 0.001 and plot it.
@@ -123,6 +129,20 @@
 ###############################################################################
 # Now, we compute the (corrected) p-values with a permutation test.
 #
+# We will use :func:`~nilearn.glm.second_level.non_parametric_inference` for
+# this step, although :func:`~nilearn.mass_univariate.permuted_ols` could be
+# used as well (pending additional steps to mask and reformat the inputs).
+#
+# .. important::
+#   One key difference between
+#   :obj:`~nilearn.glm.second_level.SecondLevelModel` and
+#   :func:`~nilearn.glm.second_level.non_parametric_inference`/
+#   :func:`~nilearn.mass_univariate.permuted_ols`
+#   is that the one-sample test in non_parametric_inference/permuted_ols
+#   assumes that the distribution is symmetric about 0,
+#   which is is weaker than the SecondLevelModel's assumption that
+#   the null distribution is Gaussian and centered about 0.
+#
 # .. important::
 #   In this example, ``threshold`` is set to 0.001, which enables
 #   cluster-level inference.