Repository: NeuroTechX/moabb
Pull request #762: Add tutorial on using mne-features in pipeline
Author: login: alexander-de-ranitz
Created: 2025-04-24T14:59:53Z | Merged: 2025-05-08T12:34:14Z

This tutorial is a minimal example of how to use features from the [mne-feature](https://mne.tools/mne-features/) library in a pipeline. This tutorial follows tutorial 3 closely, except for the pipelines used.

This PR is made as part of the advanced BCI course at Radboud University @PierreGtch


Diff:
diff --git a/docs/source/whats_new.rst b/docs/source/whats_new.rst
index d873f7c24..a5e6ab3c3 100644
--- a/docs/source/whats_new.rst
+++ b/docs/source/whats_new.rst
@@ -27,6 +27,7 @@ Enhancements
 - Adding :func:`moabb.analysis.plotting.dataset_bubble_plot` plus the corresponding tutorial (:gh:`753` by `Pierre Guetschel`_)
 - Adding :func:`moabb.datasets.utils.plot_all_datasets` and update the tutorial (:gh:`758` by `Pierre Guetschel`_)
 - Improve the dataset model cards in each API page (:gh:`765` by `Pierre Guetschel`_)
+- Adding tutorial on using mne-features (:gh:`762` by `Alexander de Ranitz`_, `Luuk Neervens`_, `Charlynn van Osch` and `Bruno Aristimunha`_)
 - Creating tutorial to expose the pre-processing steps (:gh:`771` by `Bruno Aristimunha`_)
 
 Bugs
@@ -553,3 +554,6 @@ API changes
 .. _AFF: https://github.com/allwaysFindFood
 .. _Marco Congedo: https://github.com/Marco-Congedo
 .. _Samuel Boehm: https://github.com/Samuel-Boehm
+.. _Alexander de Ranitz: https://github.com/alexander-de-ranitz
+.. _Luuk Neervens: https://github.com/LuukNeervens
+.. _Charlynn van Osch: https://github.com/charlynnvanosch
diff --git a/examples/advanced_examples/plot_mne_features_pipeline.py b/examples/advanced_examples/plot_mne_features_pipeline.py
new file mode 100644
index 000000000..ae65cba88
--- /dev/null
+++ b/examples/advanced_examples/plot_mne_features_pipeline.py
@@ -0,0 +1,144 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library [1]_. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the `FeatureExtractor <https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html>`__
+# to compute features. Here, we select two simple features:
+# ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+#######################################################################
+# Pipelines with ``FeatureExtractor``
+# ---------------
+# The ``FeatureExtractor`` from mne-features is scikit-learn compatible and
+# can therefore be used directly in our pipelines. Here, these transformer
+# steps perform feature extraction, reducing the dimensionality of the data.
+# We train an LDA classifier to classify our data as left- or right hand
+# based on the extracted signal.
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject. We can see that subjects 1 and 3, the pipeline using
+# only the variance performs best. Perhaps this is because the variance is
+# less sensitive to noise than the peak-to-peak amplitude, as the variance
+# is computed over the whole epoch, whereas the peak-to-peak amplitude
+# only considers the two most extreme data points (which could be outliers).
+# For subject 2, the peak-to-peak amplitude pipeline works best.
+#
+# In general, the pipeline using both peak-to-peak amplitude and variance
+# has a mediocre performance, never beating the best single-feature pipeline
+# in this experiment. This might be because using both
+# variance and peak-to-peak amplitude increases the data dimensionality,
+# without adding a lot of new information, resulting in increased overfitting.
+
+results["subj"] = [str(resi).zfill(2) for resi in results["subject"]]
+g = sns.catplot(
+    kind="bar",
+    x="score",
+    y="subj",
+    hue="pipeline",
+    col="dataset",
+    height=12,
+    aspect=0.5,
+    data=results,
+    orient="h",
+    palette="viridis",
+)
+plt.show()
+
+###############################################################################
+# References
+# -----------
+#
+# .. [1] Schiratti, J. B., Le Douget, J. E., Le Van Quyen, M., Essid, S., & Gramfort, A. (2018, April). An ensemble learning approach to detect epileptic seizures from long intracranial EEG recordings. In 2018 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (pp. 856-860). IEEE.
diff --git a/pyproject.toml b/pyproject.toml
index f1dc2d56e..4ae4822c8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -59,14 +59,15 @@ sphinx_copybutton = { version = "^0.5.2", optional = true }
 sphinxcontrib-bibtex = { version = "^2.6.3", optional = true }
 sphinx-favicon = { version = "^1.0.1", optional = true }
 toeplitzlda = { version = "^0.4", optional = true }
-
+mne-features = { version = "^0.3", optional = true }
+snowballstemmer = { version = "<3", optional = true }
 
 [tool.poetry.extras]
 carbonemission = ["codecarbon"]
 deeplearning = ["braindecode"]
 optuna = ["optuna", "optuna-integration"]
 tests = ["pytest", "pytest-cov", "codecov", "pytest_cases"]
-docs = ["Sphinx", "sphinx-gallery", "sphinx-bootstrap-theme", "pydata-sphinx-theme", "numpydoc", "myst-parser", "tdlda", "sphinx-design", "sphinx-rtd-theme", "sphinx_copybutton", "sphinxcontrib-bibtex", "sphinx-favicon", "toeplitz", "toeplitzlda"]
+docs = ["Sphinx", "sphinx-gallery", "sphinx-bootstrap-theme", "pydata-sphinx-theme", "numpydoc", "myst-parser", "tdlda", "sphinx-design", "sphinx-rtd-theme", "sphinx_copybutton", "sphinxcontrib-bibtex", "sphinx-favicon", "toeplitz", "toeplitzlda", "mne-features", "snowballstemmer"]
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]


Reviews:
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:27:53Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:28:38Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:30:47Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:36:17Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:36:48Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-24T15:46:06Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T17:04:04Z
user: login: bruAristimunha
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T17:04:31Z
user: login: PierreGtch
body: Good job @alexander-de-ranitz ! Just minor comments, then we can merge
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T13:57:07Z
user: login: bruAristimunha
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-08T12:14:19Z

Review comments:
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: ```suggestion
==============================
Pipelines using the mne-features library
==============================
```
created_at: 2025-04-24T15:27:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library
+
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: What is mne-features, and what is computing?
created_at: 2025-04-24T15:28:38Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001, Zhou2016
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# ------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+# mne-feature's FeatureExtractor can be used directly in our pipelines
+# as it implements the fit() and transform() methods
+# (note that fit does not have any effect, but it is implemented for compatibility).
+
+# We can specify which features we want to extract as a list of strings, see
+# https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html#mne_features.feature_extraction.FeatureExtractor
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: More important than commenting on the technical functionality here is explaining in detail exactly what mne-feature will do in this step, and linking with some reference why variance and ptp_amp are important. Linking with some reference, the original mne-features paper has some original citation about this.
created_at: 2025-04-24T15:30:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library
+
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: Some introduction is needed here...

We expected some basic math, like this: https://roche.github.io/neuro-meeglet/background/1_background_wavelets.html

and 

with the same amount of details of this:

https://roche.github.io/neuro-meeglet/tutorials/plot_eeg_covariance.html

created_at: 2025-04-24T15:36:17Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001, Zhou2016
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# ------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+# mne-feature's FeatureExtractor can be used directly in our pipelines
+# as it implements the fit() and transform() methods
+# (note that fit does not have any effect, but it is implemented for compatibility).
+
+# We can specify which features we want to extract as a list of strings, see
+# https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html#mne_features.feature_extraction.FeatureExtractor
+variance = FeatureExtractor(250.0, ["variance"])
+ptp_amp = FeatureExtractor(250.0, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(250.0, ["ptp_amp", "variance"])
+
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001(), Zhou2016()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject of each dataset.
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: any comments about the results?
created_at: 2025-04-24T15:36:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+"""
+==========================
+Pipelines using the mne-features library
+==========================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: You also need to mention that it is necessary to install mne-features outside of moabb, it tells a mini-story telling... moabb is really cool, and we have easy interconnectivity with any library that has a sklearn pipeline.
created_at: 2025-04-24T15:46:06Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,124 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the ``FeatureExtractor`` to compute features. Here, we select
+# two simple features: ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject of each dataset.
+
+results["subj"] = [str(resi).zfill(2) for resi in results["subject"]]
+g = sns.catplot(
+    kind="bar",
+    x="score",
+    y="subj",
+    hue="pipeline",
+    col="dataset",
+    height=12,
+    aspect=0.5,
+    data=results,
+    orient="h",
+    palette="viridis",
+)
+plt.show()
+
+###############################################################################
+# References
+# -----------
+#
+# .. [1] https://mne.tools/mne-features/index.html
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: We use apa reference style...
created_at: 2025-04-29T17:04:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,124 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the ``FeatureExtractor`` to compute features. Here, we select
+# two simple features: ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject of each dataset.
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: bruAristimunha
body: I removed one dataset to speed-up a little the generation
created_at: 2025-04-29T17:04:31Z
author_association: COLLABORATOR
diff_hunk: @@ -27,6 +27,7 @@ Enhancements
 - Adding :func:`moabb.analysis.plotting.dataset_bubble_plot` plus the corresponding tutorial (:gh:`753` by `Pierre Guetschel`_)
 - Adding :func:`moabb.datasets.utils.plot_all_datasets` and update the tutorial (:gh:`758` by `Pierre Guetschel`_)
 - Improve the dataset model cards in each API page (:gh:`765` by `Pierre Guetschel`_)
+- Adding tutorial on using mne-features (:gh:`762` by `Alexander de Ranitz`_ and `Bruno Aristimunha`_)
path: docs/source/whats_new.rst
user: login: PierreGtch
body: You can also add your other group members, depending on who actually contributed
created_at: 2025-05-07T13:48:45Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,143 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library [1]_. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the `FeatureExtractor <https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html>`__
+# to compute features. Here, we select two simple features:
+# ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+#######################################################################
+# Pipelines with ``FeatureExtractor``
+# ---------------
+# The ``FeatureExtractor``from mne-features is scikit-learn compatible and
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: PierreGtch
body: ```suggestion
# The ``FeatureExtractor`` from mne-features is scikit-learn compatible and
```
created_at: 2025-05-07T13:54:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,143 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library [1]_. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the `FeatureExtractor <https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html>`__
+# to compute features. Here, we select two simple features:
+# ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+#######################################################################
+# Pipelines with ``FeatureExtractor``
+# ---------------
+# The ``FeatureExtractor``from mne-features is scikit-learn compatible and
+# can therefore be used directly in our pipelines. Here, these transformer
+# steps perform feature extraction, reducing the dimensionality of the data.
+# We train an LDA classifier to classify our data as left- or right hand
+# based on the extracted signal.
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject. We can see that subjects 1 and 3, the pipeline using
+# only the variance performs best. Perhaps this is because the variance is
+# less sensitive to noise than the peak-to-peak amplitude, as the variance
+# is computed over the whole epoch, whereas the peak-to-peak amplitude
+# only considers the two most extreme data points (which could be outliers).
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: PierreGtch
body: ```suggestion
# only considers the two most extreme data points (which could be outliers).
#
```
created_at: 2025-05-07T13:55:37Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,143 @@
+"""
+==============================
+Pipelines using the mne-features library
+==============================
+
+This example shows how to evaluate a pipeline constructed using the
+mne-features library [1]_. This library provides sklearn compatible
+feature extractors for M/EEG data. These features
+can be used directly in your pipelines.
+
+A list of available features can be found
+in `the docs <https://mne.tools/mne-features/api.html>`_.
+
+Be sure to install mne-features by running ``pip install mne-features``.
+
+"""
+
+# Authors: Alexander de Ranitz <alexanderderanitz@gmail.com>
+#          Luuk Neervens <luuk.neervens@ru.nl>
+#          Charlynn van Osch <charlynn.vanosch@ru.nl>
+#
+# License: BSD (3-clause)
+
+import warnings
+
+import matplotlib.pyplot as plt
+import mne
+import seaborn as sns
+from mne_features.feature_extraction import FeatureExtractor
+from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
+from sklearn.pipeline import make_pipeline
+
+import moabb
+from moabb.datasets import BNCI2014_001
+from moabb.evaluations import WithinSessionEvaluation
+from moabb.paradigms import LeftRightImagery
+
+
+mne.set_log_level("CRITICAL")
+moabb.set_log_level("info")
+warnings.filterwarnings("ignore")
+
+##############################################################################
+# Creating Pipelines using mne-features
+# -------------------------------------
+#
+# Here, we closely follow Tutorial 3, but we create pipelines using features
+# extracted using the mne-features library [1]_. We instantiate the three different
+# classiciation pipelines to be considered in the analysis.
+# See the mne-features docs to learn more about the available features:
+# https://mne.tools/mne-features/api.html#api-documentation
+
+
+#######################################################################
+# Feature Details
+# ---------------
+# We will use the `FeatureExtractor <https://mne.tools/mne-features/generated/mne_features.feature_extraction.FeatureExtractor.html>`__
+# to compute features. Here, we select two simple features:
+# ``variance`` and ``ptp_amp`` (peak-to-peak amplitude).
+#
+# **Variance:**
+# Computed per channel (``c``). It measures the spread of the signal values
+# (samples in vector ``x``) around their average value (``mean(x)``).
+# It involves summing the squared differences between each sample and the mean,
+# then normalizing, at mne-features, it is the number of samples minus 1.
+#
+# **Peak-to-Peak Amplitude (ptp_amp):**
+# Computed per channel (``c``). This is simply the difference between the
+# maximum and minimum signal values found within that channel's data vector ``x``.
+# Formula: ``ptp_amp(c) = max(x) - min(x)``
+
+
+sfreq = 250.0  # sampling frequency used in the datasets below
+
+variance = FeatureExtractor(sfreq, ["variance"])
+ptp_amp = FeatureExtractor(sfreq, ["ptp_amp"])
+
+# We can also extract several features by passing more than one feature.
+both = FeatureExtractor(sfreq, ["ptp_amp", "variance"])
+
+#######################################################################
+# Pipelines with ``FeatureExtractor``
+# ---------------
+# The ``FeatureExtractor``from mne-features is scikit-learn compatible and
+# can therefore be used directly in our pipelines. Here, these transformer
+# steps perform feature extraction, reducing the dimensionality of the data.
+# We train an LDA classifier to classify our data as left- or right hand
+# based on the extracted signal.
+pipelines = {}
+pipelines["var+LDA"] = make_pipeline(variance, LDA())
+pipelines["ptp_amp+LDA"] = make_pipeline(ptp_amp, LDA())
+pipelines["var+ptp_amp+LDA"] = make_pipeline(both, LDA())
+
+##############################################################################
+# The rest is the same as in previous tutorials!
+
+datasets = [BNCI2014_001()]
+subj = [1, 2, 3]
+for d in datasets:
+    d.subject_list = subj
+paradigm = LeftRightImagery()
+evaluation = WithinSessionEvaluation(
+    paradigm=paradigm, datasets=datasets, overwrite=False
+)
+results = evaluation.process(pipelines)
+
+##############################################################################
+# Plotting Results
+# ----------------
+#
+# The following plot shows a comparison of the three classification pipelines
+# for each subject. We can see that subjects 1 and 3, the pipeline using
+# only the variance performs best. Perhaps this is because the variance is
+# less sensitive to noise than the peak-to-peak amplitude, as the variance
+# is computed over the whole epoch, whereas the peak-to-peak amplitude
+# only considers the two most extreme data points (which could be outliers).
+# For subject 2, the peak-to-peak amplitude pipeline works best.
path: examples/advanced_examples/plot_mne_features_pipeline.py
user: login: PierreGtch
body: ```suggestion
# For subject 2, the peak-to-peak amplitude pipeline works best.
#
```
created_at: 2025-05-07T13:56:12Z
author_association: COLLABORATOR

PR comments:
user: login: bruAristimunha
created_at: 2025-04-24T15:50:56Z
body: Hi @alexander-de-ranitz!

It's great to see you here ;) 

I commented on the tutorial and adjusted the settings so you can view the documentation in the CI. The tutorial has a good start! Basically, you need to populate it with more text/figures/information. If you have any course material that links to the features you chose, it would be very welcome to be mentioned in the tutorial.

I am giving you permission to run CI, so you can see when the documentation is generated.

You can check the generated documentation here: https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/downloading-workflow-artifacts

but I strongly suggest you run first locally.
author_association: COLLABORATOR
user: login: alexander-de-ranitz
created_at: 2025-04-25T08:06:39Z
body: @bruAristimunha Thanks for the feedback! I've added slightly more documentation. However, I'm not sure exactly what kind of additional information would be suitable here. I figured this tutorial would be a minimal working example of how to use mne-features, rather than a tutorial on any specific pipeline/dataset. The pipelines used are only trivial examples and not based on any literature. I looked at the original mne-features paper, but since this focuses on epilepsy detection, opposed to standard BCI approaches (P300/MI/etc.), it does not seem very relevant to include it in a tutorial. Similarly, I would think any mathematical details regarding standard EEG features would deserve their own tutorial, rather than being mixed in with this tutorial which is specifically about combining mne-features and MOABB. 

Of course, I am new to MOABB and I don't know what your vision is for these tutorials, so please let me know what approach you think is best and what this tutorial should aim for. If you have any concrete suggestions for details/explanations that should be added, I am happy to add them.
author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-04-25T08:57:24Z
body: Ah, I got it. Math is necessary, and we want new tutorials to be better and better.

Put the formula and explanation of precisely what is being calculated, like, we are computing this across channels or in the temporal dimensions, or something else.

Remember that the formula must be formatted in .rst. It has to be at the same level of detail as the tutorial meeget.

I think this will add more paragraphs to the tutorial. References are nice and necessary, but I think Pierre and I can help you with this point.

The tutorial should also have the angle changed a little. A little less homework and more tutorials for new users. It is not teaching much, but it is a good start. Just remember that whoever is going to consume this material will be a new user, who possibly doesn't understand much about what brain signals are. 

You are very close to reaching the level, you just need to go a little beyond.
author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-04-29T10:58:52Z
body: Do you want some extra suggestion @alexander-de-ranitz?

author_association: COLLABORATOR
user: login: alexander-de-ranitz
created_at: 2025-04-29T12:45:55Z
body: > Do you want some extra suggestion @alexander-de-ranitz?

I think I get the gist, but a I have a one remaining question: do you have any suggestions on which dataset and pipelines to use to best demonstrate the combination of MOABB and mne-features? I am not familiar with most of the more complex features available in mne-features and I do not have any literature at the ready to point me in the right direction. If you (or @PierreGtch) could give me any suggestions (and/or references) for an insightful combination of dataset and pipeline I will work on the necessary documentation and explanations.
author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-04-29T13:03:39Z
body: @PierreGtch, is all your the question :)
author_association: COLLABORATOR
user: login: PierreGtch
created_at: 2025-04-29T15:37:11Z
body: Hi @alexander-de-ranitz 
> which pipeline

For the sake of simplicity, I think you could re-use the pipeline introduced in [this tutorial](https://mne.tools/mne-features/auto_examples/plot_seizure_example.html#sphx-glr-auto-examples-plot-seizure-example-py) (and reference the tutorial).  It's meant for seizure detection, but we could check what it gives on ERP decoding

> which dataset

General rule: it's best to re-use datasets used in other MOABB tutorials to avoid downloading too many datasets when the documentation is built. For example, [this tutorial](https://moabb.neurotechx.com/docs/auto_examples/paradigm_examples/plot_within_session_p300.html#sphx-glr-auto-examples-paradigm-examples-plot-within-session-p300-py) uses the `BNCI2014_009` ERP dataset, so you could also use it.

> I do not have any literature

First thing to do: look at the documentation. If you do this, you will find out that the API page of most non-trivial functions in MNE-features provides a reference to a publication which introduces the function.
That being said, I don't completely agree with @bruAristimunha that putting the equations of these functions is absolutely necessary, but they will definitely help the reader. One equation is often simpler to understand than long textual explanations.

author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-04-29T17:03:40Z
body: Hey guys @PierreGtch and @alexander-de-ranitz,

I pushed the ""math"" that I was asking, solving the other comments it is fine to merge for me.
author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-05-05T07:42:40Z
body: Hey @alexander-de-ranitz,

Do you need help to finish the tutorial? 
author_association: COLLABORATOR
user: login: alexander-de-ranitz
created_at: 2025-05-07T11:36:16Z
body: @bruAristimunha I have updated the documentation and reference and it should be complete now. Please let me know if any further additions are needed.
author_association: COLLABORATOR
user: login: bruAristimunha
created_at: 2025-05-08T12:15:00Z
body: thank you for the contribution @alexander-de-ranitz 
author_association: COLLABORATOR
user: login: PierreGtch
created_at: 2025-05-08T13:32:03Z
body: Congrats @alexander-de-ranitz @LuukNeervens @charlynnvanosch!! 🎉
author_association: COLLABORATOR