Repository: nilearn/nilearn

Issue:
Rework the GLM documentation (nilearn.stats)
The GLM documentation (docs/glm) is currently a course on the GLM. It does not introduce in any way how to run an analysis with nilearn. While it is important that the narrative documentation stays high level, it still needs to introduce the concepts and objects of nilearn that are useful for the topic, as well as point to the most important examples to study.

Proposed plan:

* [ ] Add a subsection that goes before "An introduction to fMRI statistical analysis using GLMs" call "Models for GLM analysis" that explain briefly the first and the second level, the corresponding objects, and point to the corresponding tutorial examples
* [ ] Transform one of the second-level examples into a tutorial that introduces second-level modeling. Probably plot_thresholding.py (though we will have to change the name). It should stay simple.


Discussion:
great suggestion, waiting for a candidate ;-)
My take on this was in #2467 (that I'll close as superseeded by this):

Basic functions to mention at the very least in the narrative documentation:

- [ ] plotting event file (upcoming)
- [ ] nilearn.stats.first_level_model.make_first_level_design_matrix
- [ ] nilearn.reporting.plot_design_matrix
- [ ] nilearn.stats.second_level_model

Maybe also a comparaison with glover / spm processing parameters ?

In any case it should be expanded to reflect at least the most important available examples in 9.5 and 9.6.

Resolving patch:
diff --git a/examples/04_glm_first_level_models/plot_events_file.py b/examples/04_glm_first_level_models/plot_events_file.py
deleted file mode 100644
--- a/examples/04_glm_first_level_models/plot_events_file.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""Generation and plot of an events file : the neurospin/localizer events.
-==========================================================================
-
-The protocol described is the simplified version of the so-called 
-"archi standard" localizer event sequence.  
-See Pinel et al., BMC neuroscience 2007 for reference.
-"""
-
-print(__doc__)
-
-#########################################################################
-# Define the onset times in seconds. Those are typically extracted
-# from the stimulation software used.
-import numpy as np
-onset = np.array([
-    0., 2.4, 8.7, 11.4, 15., 18., 20.7, 23.7, 26.7, 29.7, 33., 35.4, 39.,
-    41.7, 44.7, 48., 56.4, 59.7, 62.4, 69., 71.4, 75., 83.4, 87., 89.7,
-    96., 108., 116.7, 119.4, 122.7, 125.4, 131.4, 135., 137.7, 140.4,
-    143.4, 146.7, 149.4, 153., 156., 159., 162., 164.4, 167.7, 170.4,
-    173.7, 176.7, 188.4, 191.7, 195., 198., 201., 203.7, 207., 210.,
-    212.7, 215.7, 218.7, 221.4, 224.7, 227.7, 230.7, 234., 236.7, 246.,
-    248.4, 251.7, 254.7, 257.4, 260.4, 264., 266.7, 269.7, 275.4, 278.4,
-    284.4, 288., 291., 293.4, 296.7])
-
-#########################################################################
-# Associated trial types: these are numbered between 0 and 5, hence
-# correspond to 6 different conditions.
-trial_idx = np.array(
-    [3, 3, 0, 2, 5, 3, 5, 2, 3, 5, 1, 2, 4, 4, 2, 2, 4, 0, 2, 3, 3, 4, 2,
-     2, 5, 1, 2, 3, 5, 1, 3, 4, 2, 2, 1, 2, 5, 0, 3, 1, 4, 2, 3, 4, 2, 2,
-     0, 0, 2, 4, 3, 3, 1, 1, 1, 3, 3, 0, 3, 0, 3, 2, 3, 5, 4, 0, 2, 2, 2,
-     3, 1, 0, 0, 3, 1, 5, 4, 3, 5, 5])
-
-#########################################################################
-# We may want to map these indices to explicit condition names.
-# For that, we define a list of 10 strings.
-condition_ids = ['horizontal checkerboard',
-                 'vertical checkerboard',
-                 'auditory instructions',
-                 'visual instructions',
-                 'visual sentence',
-                 'auditory sentence']
-
-trial_type = np.array([condition_ids[i] for i in trial_idx])
-
-#########################################################################
-# We also define a duration (required by BIDS conventions).
-duration = np.ones_like(onset)
-
-
-#########################################################################
-# Form an event dataframe from these information.
-import pandas as pd
-events = pd.DataFrame({'trial_type': trial_type,
-                       'onset': onset,
-                       'duration': duration})
-
-#########################################################################
-# Export them to a tsv file.
-tsvfile = 'localizer_events.tsv'
-events.to_csv(tsvfile, sep='\t', index=False)
-print("Created the events file in %s " % tsvfile)
-
-
-#########################################################################
-# Plot the event dataframe.
-import matplotlib.pyplot as plt
-from nilearn.reporting import plot_event
-plot_event(events, figsize=(12, 4))
-plt.show()


Test patch:
diff --git a/examples/05_glm_second_level/plot_second_level_paired_sample_test.py b/examples/05_glm_second_level/plot_second_level_paired_sample_test.py
new file mode 100644
--- /dev/null
+++ b/examples/05_glm_second_level/plot_second_level_paired_sample_test.py
@@ -0,0 +1,92 @@
+"""
+Second-level fMRI model: a paired-sample test
+=============================================
+
+Full step-by-step example of fitting a GLM to perform a second level analysis
+in experimental data and visualizing the results.
+
+More specifically:
+
+1. A sample of n=16 visual activity fMRIs are downloaded.
+2. A paired one-sample t-test is applied to the brain maps in order to see the effect
+   of the contrast difference across subjects.
+
+The contrast is between responses to vertical versus horizontal
+checkerboards that are retinotopically distinct. At the individual
+level, these stimuli are sometimes used to map the borders of primary
+visual areas. At the group level, such a mapping is not possible. Yet,
+we may observe some significant effects in these areas.
+
+"""
+
+import pandas as pd
+from nilearn import plotting
+from nilearn.datasets import fetch_localizer_contrasts
+
+#########################################################################
+# Fetch dataset
+# --------------
+# We download a list of left vs right button press contrasts from a
+# localizer dataset.
+n_subjects = 16
+sample_vertical = fetch_localizer_contrasts(
+    ["vertical checkerboard"], n_subjects, get_tmaps=True)
+sample_horizontal = fetch_localizer_contrasts(
+    ["horizontal checkerboard"], n_subjects, get_tmaps=True)
+
+# What remains implicit here is that there is a one-to-one
+# correspondence between the two samples: the first image of both
+# samples comes from subject S1, the second from subject S2 etc.
+
+############################################################################
+# Estimate second level model
+# ---------------------------
+# We define the input maps and the design matrix for the second level model
+# and fit it.
+second_level_input = sample_vertical['cmaps'] + sample_horizontal['cmaps']
+
+############################################################################
+# Next, we model the effect of conditions (sample 1 vs sample 2).
+import numpy as np
+condition_effect = np.hstack(([1] * n_subjects, [- 1] * n_subjects))
+
+############################################################################
+# Subsequently, we can model the subject effect: each subject is observed in sample 1 and sample 2.
+subject_effect = np.vstack((np.eye(n_subjects), np.eye(n_subjects)))
+subjects = ['S%02d' % i for i in range(1, n_subjects + 1)]
+
+############################################################################
+# We then assemble those in a design matrix and
+design_matrix = pd.DataFrame(
+    np.hstack((condition_effect[:, np.newaxis], subject_effect)),
+    columns=['vertical vs horizontal'] + subjects)
+
+############################################################################
+# plot the design_matrix.
+from nilearn.reporting import plot_design_matrix
+plot_design_matrix(design_matrix)
+
+############################################################################
+# We formally specify the analysis model and fit it.
+from nilearn.glm.second_level import SecondLevelModel
+second_level_model = SecondLevelModel().fit(
+    second_level_input, design_matrix=design_matrix)
+
+##########################################################################
+# Estimating the contrast is very simple. We can just provide the column
+# name of the design matrix.
+z_map = second_level_model.compute_contrast('vertical vs horizontal',
+                                            output_type='z_score')
+
+###########################################################################
+# We threshold the second level contrast and plot it.
+threshold = 3.1  # correponds to  p < .001, uncorrected
+display = plotting.plot_glass_brain(
+    z_map, threshold=threshold, colorbar=True, plot_abs=False,
+    title='vertical vs horizontal checkerboard (unc p<0.001')
+
+plotting.show()
+
+###########################################################################
+# Unsurprisingly, we see activity in the primary visual cortex, both positive
+# and negative.