From faf61a780e9c9384408b26e760f2d6922c143087 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Thu, 26 Feb 2026 06:00:06 +0000 Subject: [PATCH 1/2] ENH: Add ICA sources plot to Report.add_ica --- mne/report/report.py | 44 +++++++++++++++++++++++++++++++++ mne/report/tests/test_report.py | 32 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/mne/report/report.py b/mne/report/report.py index 071d15e3cee..2f6ef72b59a 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -2020,6 +2020,22 @@ def _add_ica_components(self, *, ica, picks, image_format, section, tags, replac replace=replace, ) + def _add_ica_sources( + self, *, ica, inst, picks, image_format, section, tags, replace + ): + with use_browser_backend("matplotlib"): + fig = ica.plot_sources(inst=inst, picks=picks, show=False) + self._add_figure( + fig=fig, + title="Sources", + caption=None, + image_format=image_format, + tags=tags, + section=section, + replace=replace, + own_figure=True, + ) + def _add_ica( self, *, @@ -2036,6 +2052,7 @@ def _add_ica( tags, n_jobs, replace, + plot_sources=False, ): if _path_like(ica): ica = read_ica(ica) @@ -2166,6 +2183,25 @@ def _add_ica( replace=replace, ) + # Sources plot + if plot_sources: + if inst is None: + warn( + "Cannot plot ICA sources because inst=None. " + "Please pass a Raw, Epochs, or Evoked instance to " + "add_ica() to enable source plotting." + ) + else: + self._add_ica_sources( + ica=ica, + inst=inst, + picks=picks, + image_format=image_format, + section=section, + tags=tags, + replace=replace, + ) + @fill_doc def add_ica( self, @@ -2181,6 +2217,7 @@ def add_ica( n_jobs=None, tags=("ica",), replace=False, + plot_sources=False, ): """Add (a fitted) `~mne.preprocessing.ICA` to the report. @@ -2207,6 +2244,12 @@ def add_ica( %(n_jobs)s %(tags_report)s %(replace_report)s + plot_sources : bool + Whether to add a plot of the ICA source time-courses using + :meth:`mne.preprocessing.ICA.plot_sources`. Requires ``inst`` + to be provided. Defaults to ``False``. + + .. versionadded:: 1.9 Notes ----- @@ -2227,6 +2270,7 @@ def add_ica( section=title, n_jobs=n_jobs, replace=replace, + plot_sources=plot_sources, ) def remove(self, *, title=None, tags=None, remove_all=False): diff --git a/mne/report/tests/test_report.py b/mne/report/tests/test_report.py index 5fa47d63914..6492283fdcc 100644 --- a/mne/report/tests/test_report.py +++ b/mne/report/tests/test_report.py @@ -1042,6 +1042,38 @@ def test_manual_report_2d(tmp_path, invisible_fig): picks=[0], ) r.add_ica(ica=ica, title="my ica with picks=None", inst=epochs_baseline, picks=None) + + # plot_sources=True with a valid inst should add a "Sources" subsection + r.add_ica( + ica=ica, + title="my ica with sources raw", + inst=raw, + picks=[0], + plot_sources=True, + ) + assert "Sources" in r._content[-1].html + + # plot_sources=True with an epochs inst should also work + r.add_ica( + ica=ica, + title="my ica with sources epochs", + inst=epochs_baseline, + picks=[0], + plot_sources=True, + ) + assert "Sources" in r._content[-1].html + + # plot_sources=True with inst=None should warn and not raise + with pytest.warns( + RuntimeWarning, match="Cannot plot ICA sources because inst=None" + ): + r.add_ica( + ica=ica, + title="my ica sources no inst", + inst=None, + plot_sources=True, + ) + r.add_covariance(cov=cov, info=raw_fname, title="my cov") r.add_forward( forward=fwd_fname, From 37f1afe17cb3826d9cb0ba7edaf467c06c1fbb61 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 27 Feb 2026 03:50:42 +0000 Subject: [PATCH 2/2] add changelog entry --- doc/changes/dev/13697.newfeature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changes/dev/13697.newfeature.rst diff --git a/doc/changes/dev/13697.newfeature.rst b/doc/changes/dev/13697.newfeature.rst new file mode 100644 index 00000000000..2a902531d35 --- /dev/null +++ b/doc/changes/dev/13697.newfeature.rst @@ -0,0 +1 @@ +Add ICA source timecourse plots to :func:`mne.Report.add_ica`, by `Aniket Singh Yadav`_. \ No newline at end of file