{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n\n# Getting started with mne.Report\n\n:class:`mne.Report` is a way to create interactive HTML summaries of your data.\nThese reports can show many different visualizations for one or multiple participants.\nA common use case is creating diagnostic summaries to check data\nquality at different stages in the processing pipeline. The report can show\nthings like plots of data before and after each preprocessing step, epoch\nrejection statistics, MRI slices with overlaid BEM shells, all the way up to\nplots of estimated cortical activity.\n\nCompared to a Jupyter notebook, :class:`mne.Report` is easier to deploy, as the\nHTML pages it generates are self-contained and do not require a running Python\nenvironment. However, it is less flexible as you can't change code and re-run\nsomething directly within the browser. This tutorial covers the basics of\nbuilding a report. As usual, we will start by importing the modules and data we need:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Authors: The MNE-Python contributors.\n# License: BSD-3-Clause\n# Copyright the MNE-Python contributors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import tempfile\nfrom pathlib import Path\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport scipy.ndimage\n\nimport mne\n\ndata_path = Path(mne.datasets.sample.data_path(verbose=False))\nsample_dir = data_path / \"MEG\" / \"sample\"\nsubjects_dir = data_path / \"subjects\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The basic process for creating an HTML report is to instantiate the\n:class:`~mne.Report` class and then use one or more of its many methods to\nadd content, one element at a time.\n\nYou may also use the :meth:`~mne.Report.parse_folder` method to select\nparticular files to include in the report. But more on that later.\n\n.. sidebar: Viewing the report\n\n On successful creation of the report, the :meth:`~mne.Report.save` method\n will open the HTML in a new tab in your browser. To disable this, use the\n ``open_browser=False`` parameter of :meth:`~mne.Report.save`.\n\n## Adding `~mne.io.Raw` data\n\nRaw data can be added via the :meth:`mne.Report.add_raw` method. It can\noperate with a path to a raw file and `~mne.io.Raw` objects, and will\nproduce \u2013 among other output \u2013 a slider that allows you to scrub through 10\nequally-spaced 1-second segments of the data:\n\n

Warning

In the following example, we crop the raw data to 60 seconds merely to\n speed up processing; this is not usually recommended!

\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "raw_path = sample_dir / \"sample_audvis_filt-0-40_raw.fif\"\nraw = mne.io.read_raw(raw_path)\nraw.pick(picks=[\"eeg\", \"eog\", \"stim\"]).crop(tmax=60).load_data()\n\nreport = mne.Report(title=\"Raw example\")\n# This method also accepts a path, e.g., raw=raw_path\nreport.add_raw(raw=raw, title=\"Raw\", psd=False) # omit PSD plot\nreport.save(\"report_raw.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding events\n\nEvents can be added via :meth:`mne.Report.add_events`. You also need to\nsupply the sampling frequency used during the recording; this information is\nused to generate a meaningful time axis.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "events_path = sample_dir / \"sample_audvis_filt-0-40_raw-eve.fif\"\nevents = mne.find_events(raw=raw)\nsfreq = raw.info[\"sfreq\"]\n\nreport = mne.Report(title=\"Events example\")\nreport.add_events(events=events_path, title=\"Events from Path\", sfreq=sfreq)\nreport.add_events(events=events, title='Events from \"events\"', sfreq=sfreq)\nreport.save(\"report_events.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding :class:`~mne.Epochs`\n\nEpochs can be added via :meth:`mne.Report.add_epochs`. Note that although\nthis method accepts a path to an epochs file too, in the following example\nwe only add epochs that we create on the fly from raw data. To demonstrate\nthe representation of epochs metadata, we'll add some of that, too.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "event_id = {\n \"auditory/left\": 1,\n \"auditory/right\": 2,\n \"visual/left\": 3,\n \"visual/right\": 4,\n \"face\": 5,\n \"buttonpress\": 32,\n}\n\nmetadata, _, _ = mne.epochs.make_metadata(\n events=events, event_id=event_id, tmin=-0.2, tmax=0.5, sfreq=raw.info[\"sfreq\"]\n)\nepochs = mne.Epochs(raw=raw, events=events, event_id=event_id, metadata=metadata)\n\nreport = mne.Report(title=\"Epochs example\")\nreport.add_epochs(epochs=epochs, title='Epochs from \"epochs\"')\nreport.save(\"report_epochs.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding `~mne.Evoked`\n\nEvoked data can be added via :meth:`mne.Report.add_evokeds`. By default, the\n``Evoked.comment`` attribute of each evoked will be used as a title. We can\nspecify custom titles via the ``titles`` parameter. Again, this method\nalso accepts the path to an evoked file stored on disk; in the following\nexample, however, we load the evokeds manually first, since we only want to\nadd a subset of them to the report. The evokeds are not baseline-corrected,\nso we apply baseline correction, too. Lastly, by providing an (optional)\nnoise covariance, we can add plots evokeds that were \"whitened\" using this\ncovariance matrix.\n\nBy default, this method will produce topographic plots at 21 equally-spaced time\npoints (or fewer, if the data contains fewer time points). We can adjust this\nvia the ``n_time_points`` parameter.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "evoked_path = sample_dir / \"sample_audvis-ave.fif\"\ncov_path = sample_dir / \"sample_audvis-cov.fif\"\n\nevokeds = mne.read_evokeds(evoked_path, baseline=(None, 0))\nevokeds_subset = evokeds[:2] # The first two\nfor evoked in evokeds_subset:\n evoked.pick(\"eeg\") # just for speed of plotting\n\nreport = mne.Report(title=\"Evoked example\")\nreport.add_evokeds(\n evokeds=evokeds_subset,\n titles=[\"evoked 1\", \"evoked 2\"], # Manually specify titles\n noise_cov=cov_path,\n n_time_points=5,\n)\nreport.save(\"report_evoked.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding `~mne.Covariance`\n\n(Noise) covariance objects can be added via\n:meth:`mne.Report.add_covariance`. The method accepts `~mne.Covariance`\nobjects and the path to a file on disk. It also expects us to pass an\n`~mne.Info` object or the path to a file to read the measurement info from,\nas well as a title.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cov_path = sample_dir / \"sample_audvis-cov.fif\"\n\nreport = mne.Report(title=\"Covariance example\")\nreport.add_covariance(cov=cov_path, info=raw_path, title=\"Covariance\")\nreport.save(\"report_cov.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding `~mne.Projection` vectors\n\n`~mne.Projection` vectors can be added via\n:meth:`mne.Report.add_projs`. The method requires an `~mne.Info` object\n(or the path to one) and a title. Projectors found in the `~mne.Info` will\nbe visualized. You may also supply a list of `~mne.Projection` objects or\na path to projectors stored on disk. In this case, the channel information\nis read from the `~mne.Info`, but projectors potentially included will be\nignored; instead, only the explicitly passed projectors will be plotted.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ecg_proj_path = sample_dir / \"sample_audvis_ecg-proj.fif\"\nreport = mne.Report(title=\"Projectors example\")\nreport.add_projs(info=raw_path, title=\"Projs from info\")\nreport.add_projs(info=raw_path, projs=ecg_proj_path, title=\"ECG projs from path\")\nreport.save(\"report_projs.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding `~mne.preprocessing.ICA`\n\n`~mne.preprocessing.ICA` objects can be added via\n:meth:`mne.Report.add_ica`. Aside from the parameters ``ica`` (that accepts\nan `~mne.preprocessing.ICA` instance or a path to an ICA object stored on\ndisk) and the ``title``, there is a third required parameter, ``inst``.\n``inst`` is used to specify a `~mne.io.Raw` or `~mne.Epochs` object for\nproducing ICA property plots and overlay plots demonstrating\nthe effects of ICA cleaning. If, instead, you only want to generate ICA\ncomponent topography plots, explicitly pass ``inst=None``.\n\n

Note

:meth:`mne.Report.add_ica` only works with fitted ICAs.

\n\nYou can optionally specify for which components to produce topography and\nproperties plots by passing ``picks``. By default, all components will be\nshown. It is also possible to pass evoked signals based on ECG and EOG events\nvia ``ecg_evoked`` and ``eog_evoked``. This allows you directly see the\neffects of ICA component removal on these artifactual signals.\nArtifact detection scores produced by\n:meth:`~mne.preprocessing.ICA.find_bads_ecg`\nand :meth:`~mne.preprocessing.ICA.find_bads_eog` can be passed via the\n``ecg_scores`` and ``eog_scores`` parameters, respectively, producing\nvisualizations of the scores for each ICA component.\n\nLastly, by passing ``n_jobs``, you may largely speed up the generation of\nthe properties plots by enabling parallel execution.\n\n

Warning

In the following example, we request a small number of ICA components\n to estimate, set the threshold for assuming ICA convergence to a very\n liberal value, and only visualize 2 of the components. All of this is\n done to largely reduce the processing time of this tutorial, and is\n usually **not** recommended for an actual data analysis.

\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ica = mne.preprocessing.ICA(\n n_components=5, # fit 5 ICA components\n fit_params=dict(tol=0.01), # assume very early on that ICA has converged\n)\n\nica.fit(inst=raw)\n\n# create epochs based on EOG events, find EOG artifacts in the data via pattern\n# matching, and exclude the EOG-related ICA components\neog_epochs = mne.preprocessing.create_eog_epochs(raw=raw)\neog_components, eog_scores = ica.find_bads_eog(\n inst=eog_epochs,\n ch_name=\"EEG 001\", # a channel close to the eye\n threshold=1, # lower than the default threshold\n)\nica.exclude = eog_components\n\nreport = mne.Report(title=\"ICA example\")\nreport.add_ica(\n ica=ica,\n title=\"ICA cleaning\",\n picks=ica.exclude, # plot the excluded EOG components\n inst=raw,\n eog_evoked=eog_epochs.average(),\n eog_scores=eog_scores,\n n_jobs=None, # could be increased!\n)\nreport.save(\"report_ica.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding MRI with BEM\n\nMRI slices with superimposed traces of the boundary element model (BEM)\nsurfaces can be added via :meth:`mne.Report.add_bem`. All you need to pass is\nthe FreeSurfer subject name and subjects directory, and a title. To reduce\nthe resulting file size, you may pass the ``decim`` parameter to only include\nevery n-th volume slice, and ``width`` to specify the width of the resulting\nfigures in pixels.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"BEM example\")\nreport.add_bem(\n subject=\"sample\",\n subjects_dir=subjects_dir,\n title=\"MRI & BEM\",\n decim=40,\n width=256,\n)\nreport.save(\"report_mri_and_bem.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding coregistration\n\nThe sensor alignment (``head -> mri`` transformation obtained by\n\"coregistration\") can be visualized via :meth:`mne.Report.add_trans`. The\nmethod expects the transformation either as a `~mne.transforms.Transform`\nobject or as a path to a ``trans.fif`` file, the FreeSurfer subject name and\nsubjects directory, and a title. The ``alpha`` parameter can be used to\ncontrol the transparency of the head, where a value of 1 means fully opaque.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "trans_path = sample_dir / \"sample_audvis_raw-trans.fif\"\n\nreport = mne.Report(title=\"Coregistration example\")\nreport.add_trans(\n trans=trans_path,\n info=raw_path,\n subject=\"sample\",\n subjects_dir=subjects_dir,\n alpha=1.0,\n title=\"Coregistration\",\n)\nreport.save(\"report_coregistration.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding a `~mne.Forward` solution\n\nForward solutions (\"leadfields\") can be added by passing a `~mne.Forward`\nobject or the path to a forward solution stored on disk to\nmeth:`mne.Report.add_forward`.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fwd_path = sample_dir / \"sample_audvis-meg-oct-6-fwd.fif\"\n\nreport = mne.Report(title=\"Forward solution example\")\nreport.add_forward(forward=fwd_path, title=\"Forward solution\")\nreport.save(\"report_forward_sol.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding an `~mne.minimum_norm.InverseOperator`\n\nAn inverse operator can be added via :meth:`mne.Report.add_inverse_operator`.\nThe method expects an `~mne.minimum_norm.InverseOperator` object or a path to\none stored on disk, and a title.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "inverse_op_path = sample_dir / \"sample_audvis-meg-oct-6-meg-inv.fif\"\n\nreport = mne.Report(title=\"Inverse operator example\")\nreport.add_inverse_operator(inverse_operator=inverse_op_path, title=\"Inverse operator\")\nreport.save(\"report_inverse_op.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding a `~mne.SourceEstimate`\n\nAn inverse solution (also called source estimate or source time course, STC)\ncan be added via :meth:`mne.Report.add_stc`. The\nmethod expects an `~mne.SourceEstimate`, the corresponding FreeSurfer subject\nname and subjects directory, and a title. By default, it will produce\nsnapshots at 51 equally-spaced time points (or fewer, if the data contains\nfewer time points). We can adjust this via the ``n_time_points`` parameter.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "stc_path = sample_dir / \"sample_audvis-meg\"\n\nreport = mne.Report(title=\"Source estimate example\")\nreport.add_stc(\n stc=stc_path,\n subject=\"sample\",\n subjects_dir=subjects_dir,\n title=\"Source estimate\",\n n_time_points=2, # few for speed\n)\nreport.save(\"report_inverse_sol.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding source code (e.g., a Python script)\n\nIt is possible to add code or scripts (e.g., the scripts you used for\nanalysis) to the report via :meth:`mne.Report.add_code`. The code blocks will\nbe automatically syntax-highlighted. You may pass a string with the\nrespective code snippet, or the path to a file. If you pass a path, it\n**must** be a `pathlib.Path` object (and not a string), otherwise it will be\ntreated as a code literal.\n\nOptionally, you can specify which programming language to assume for syntax\nhighlighting by passing the ``language`` parameter. By default, we'll assume\nthe provided code is Python.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "mne_init_py_path = Path(mne.__file__) # __init__.py in the MNE-Python root\nmne_init_py_content = mne_init_py_path.read_text(encoding=\"utf-8\")\n\nreport = mne.Report(title=\"Code example\")\nreport.add_code(code=mne_init_py_path, title=\"Code from Path\")\nreport.add_code(code=mne_init_py_content, title=\"Code from string\")\n\nreport.save(\"report_code.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding custom figures\n\nCustom Matplotlib figures can be added via :meth:`~mne.Report.add_figure`.\nRequired parameters are the figure and a title. Optionally, may add a caption\nto appear below the figure. You can also specify the image format of the\nimage file that will be generated from the figure, so it can be embedded in\nthe HTML report.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "x = np.linspace(start=0, stop=10, num=100)\ny = x**2\n\nfig, ax = plt.subplots()\nax.plot(x, y, ls=\"--\", lw=2, color=\"blue\", label=\"my function\")\nax.set_xlabel(\"x\")\nax.set_ylabel(\"f(x)\")\nax.legend()\n\nreport = mne.Report(title=\"Figure example\")\nreport.add_figure(\n fig=fig,\n title=\"A custom figure\",\n caption=\"A blue dashed line reaches up into the sky \u2026\",\n image_format=\"PNG\",\n)\nreport.save(\"report_custom_figure.html\", overwrite=True)\nplt.close(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple figures can be grouped into a single section via the ``section``\nparameter.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig_1, ax_1 = plt.subplots()\nax_1.plot([1, 2, 3])\n\nfig_2, ax_2 = plt.subplots()\nax_2.plot([3, 2, 1])\n\nsection = \"Section example\"\n\nreport = mne.Report(title=\"Figure section example\")\nreport.add_figure(fig=fig_1, title=\"Figure 1\", section=section, tags=\"fig-1\")\nreport.add_figure(fig=fig_2, title=\"Figure 2\", section=section, tags=\"fig-2\")\nreport.save(\"report_custom_figure_sections.html\", overwrite=True)\nplt.close(fig_1)\nplt.close(fig_2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The :meth:`mne.Report.add_figure` method can also add multiple figures at\nonce. In this case, a slider will appear, allowing users to intuitively\nbrowse the figures. To make this work, you need to provide a collection o\nfigures, a title, and optionally a collection of captions.\n\nIn the following example, we will read the MNE logo as a Matplotlib figure\nand rotate it with different angles. Each rotated figure and its respective\ncaption will be added to a list, which is then used to create the slider.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "mne_logo_path = Path(mne.__file__).parent / \"icons\" / \"mne_icon-cropped.png\"\nfig_array = plt.imread(mne_logo_path)\nrotation_angles = np.linspace(start=0, stop=360, num=8, endpoint=False)\n\nfigs = []\ncaptions = []\nfor angle in rotation_angles:\n # Rotate and remove some rounding errors to avoid Matplotlib warnings\n fig_array_rotated = scipy.ndimage.rotate(input=fig_array, angle=angle)\n fig_array_rotated = fig_array_rotated.clip(min=0, max=1)\n\n # Create the figure\n fig, ax = plt.subplots(figsize=(3, 3), layout=\"constrained\")\n ax.imshow(fig_array_rotated)\n ax.set_axis_off()\n\n # Store figure and caption\n figs.append(fig)\n captions.append(f\"Rotation angle: {round(angle, 1)}\u00b0\")\n\nreport = mne.Report(title=\"Multiple figures example\")\nreport.add_figure(fig=figs, title=\"Fun with figures! \ud83e\udd73\", caption=captions)\nreport.save(\"report_custom_figures.html\", overwrite=True)\nfor fig in figs:\n plt.close(fig)\ndel figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding image files\n\nExisting images (e.g., photos, screenshots, sketches etc.) can be added\nto the report via :meth:`mne.Report.add_image`. Supported image formats\ninclude JPEG, PNG, GIF, and SVG (and possibly others). Like with Matplotlib\nfigures, you can specify a caption to appear below the image.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"Image example\")\nreport.add_image(\n image=mne_logo_path, title=\"MNE\", caption=\"Powered by \ud83e\udde0 \ud83e\udde0 \ud83e\udde0 around the world!\"\n)\nreport.save(\"report_custom_image.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with tags\n\nEach ``add_*`` method accepts a keyword parameter ``tags``, which can be\nused to pass one or more tags to associate with the respective content\nelements. By default, each ``add_*`` method adds a tag describing the data\ntype, e.g., ``evoked`` or ``source-estimate``. When viewing the HTML report,\nthe ``Filter by tags`` dropdown menu can be used to interactively show or\nhide content with specific tags. This allows you e.g. to only view\n``evoked`` or ``participant-001`` data, should you have added those tags.\nVisible tags will appear with blue, and hidden tags with gray background\ncolor.\n\nTo toggle the visibility of **all** tags, use the respective checkbox in the\n``Filter by tags`` dropdown menu, or press :kbd:`T`.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"Tags example\")\nreport.add_image(\n image=mne_logo_path,\n title=\"MNE Logo\",\n tags=(\"image\", \"mne\", \"logo\", \"open-source\"),\n)\nreport.save(\"report_tags.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Editing a saved report\n\nSaving to HTML is a write-only operation, meaning that we cannot read an\n``.html`` file back as a :class:`~mne.Report` object. In order to be able\nto edit a report once it's no longer in-memory in an active Python session,\nsave it as an HDF5 file instead of HTML:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"Saved report example\", verbose=True)\nreport.add_image(image=mne_logo_path, title=\"MNE 1\")\nreport.save(\"report_partial.hdf5\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The saved report can be read back and modified or amended. This allows the\npossibility to e.g. run multiple scripts in a processing pipeline, where each\nscript adds new content to an existing report.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report_from_disk = mne.open_report(\"report_partial.hdf5\")\nreport_from_disk.add_image(image=mne_logo_path, title=\"MNE 2\")\nreport_from_disk.save(\"report_partial.hdf5\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make this even easier, :class:`mne.Report` can be used as a\ncontext manager (note the ``with`` statement)`):\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "with mne.open_report(\"report_partial.hdf5\") as report:\n report.add_image(image=mne_logo_path, title=\"MNE 3\")\n report.save(\"report_final.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the context manager, the updated report is also automatically saved\nback to :file:`report.h5` upon leaving the block.\n\n## Adding an entire folder of files\n\nWe also provide a way to add an entire **folder** of files to the report at\nonce, without having to invoke the individual ``add_*`` methods outlined\nabove for each file. This approach, while convenient, provides less\nflexibility with respect to content ordering, tags, titles, etc.\n\nBefore getting started with :class:`mne.Report`, make sure the files you want\nto render follow the filename conventions defined by MNE:\n\n.. cssclass:: table-bordered\n.. rst-class:: midvalign\n\n=================================== =========================================\nData object Filename convention (ends with)\n=================================== =========================================\n`~mne.io.Raw` ``-raw.fif(.gz)``, ``-raw_sss.fif(.gz)``,\n ``-raw_tsss.fif(.gz)``,\n ``_meg.fif(.gz)``, ``_eeg.fif(.gz)``,\n ``_ieeg.fif(.gz)``\nevents ``-eve.fif(.gz)``\n`~mne.Epochs` ``-epo.fif(.gz)``\n`~mne.Evoked` ``-ave.fif(.gz)``\n`~mne.Covariance` ``-cov.fif(.gz)``\n`~mne.Projection` ``-proj.fif(.gz)``\n`~mne.transforms.Transform` ``-trans.fif(.gz)``\n`~mne.Forward` ``-fwd.fif(.gz)``\n`~mne.minimum_norm.InverseOperator` ``-inv.fif(.gz)``\n`~mne.SourceEstimate` ``-lh.stc``, ``-rh.stc``\n=================================== =========================================\n\nAlternatively, the dash ``-`` in the filename may be replaced with an\nunderscore ``_`` (except for the ``.stc`` files).\n\nFor our first example, we'll generate a barebones report for all the\n:file:`.fif` files containing raw data in the sample dataset, by passing the\npattern ``*raw.fif`` to :meth:`~mne.Report.parse_folder`. We'll omit the\n``subject`` and ``subjects_dir`` parameters from the :class:`~mne.Report`\nconstructor, but we'll also pass ``render_bem=False`` to the\n:meth:`~mne.Report.parse_folder` method \u2014 otherwise we would get a warning\nabout not being able to render MRI and ``trans`` files without knowing the\nsubject. To save some processing time in this tutorial, we're also going to\ndisable rendering of the butterfly plots for the `~mne.io.Raw` data by\npassing ``raw_butterfly=False``.\n\nWhich files are included depends on both the ``pattern`` parameter passed to\n:meth:`~mne.Report.parse_folder` and also the ``subject`` and\n``subjects_dir`` parameters provided to the :class:`~mne.Report` constructor.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"parse_folder example\")\nreport.parse_folder(\n data_path=data_path, pattern=\"*raw.fif\", render_bem=False, raw_butterfly=False\n)\nreport.save(\"report_parse_folder_basic.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, the power spectral density and SSP projectors of the\n:class:`~mne.io.Raw` files are not shown to speed up report generation. You\ncan add them by passing ``raw_psd=True`` and ``projs=True`` to the\n:class:`~mne.Report` constructor. Like in the previous example, we're going\nto omit the butterfly plots by passing ``raw_butterfly=False``. Lastly, let's\nalso refine our pattern to select only the filtered raw recording (omitting\nthe unfiltered data and the empty-room noise recordings).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pattern = \"sample_audvis_filt-0-40_raw.fif\"\nreport = mne.Report(title=\"parse_folder example 2\", raw_psd=True, projs=True)\nreport.parse_folder(\n data_path=data_path, pattern=pattern, render_bem=False, raw_butterfly=False\n)\nreport.save(\"report_parse_folder_raw_psd_projs.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This time we'll pass a specific ``subject`` and ``subjects_dir`` (even though\nthere's only one subject in the sample dataset) and remove our\n``render_bem=False`` parameter so we can see the MRI slices, with BEM\ncontours overlaid on top if available. Since this is computationally\nexpensive, we'll also pass the ``mri_decim`` parameter for the benefit of our\ndocumentation servers, and skip processing the :file:`.fif` files.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(\n title=\"parse_folder example 3\", subject=\"sample\", subjects_dir=subjects_dir\n)\nreport.parse_folder(data_path=data_path, pattern=\"\", mri_decim=40)\nreport.save(\"report_parse_folder_mri_bem.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's look at how :class:`~mne.Report` handles :class:`~mne.Evoked`\ndata (we will skip the MRIs to save computation time).\n\nThe MNE sample dataset we're using in this example has **not** been\nbaseline-corrected; so let's apply baseline correction this now for the\nreport!\n\nTo request baseline correction, pass a ``baseline`` argument to\n`~mne.Report`, which should be a tuple with the starting and ending time of\nthe baseline period. For more details, see the documentation on\n`~mne.Evoked.apply_baseline`. Here, we will apply baseline correction for a\nbaseline period from the beginning of the time interval to time point zero.\n\nLastly, we want to render the \"whitened\" evoked data, too. Whitening\nrequires us to specify the path to a covariance matrix file via the\n``cov_fname`` parameter of `~mne.Report`.\n\nNow, let's put all of this together! Here we use a temporary directory\nfor speed so we can render a single Evoked instance, using just EEG\nchannels.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "baseline = (None, 0)\ncov_fname = sample_dir / \"sample_audvis-cov.fif\"\npattern = \"sample_audvis-ave.fif\"\nevoked = mne.read_evokeds(sample_dir / pattern)[0].pick(\"eeg\").decimate(4)\nreport = mne.Report(\n title=\"parse_folder example 4\", baseline=baseline, cov_fname=cov_fname\n)\nwith tempfile.TemporaryDirectory() as path:\n evoked.save(Path(path) / pattern)\n report.parse_folder(\n path, pattern=pattern, render_bem=False, n_time_points_evokeds=5\n )\nreport.save(\"report_parse_folder_evoked.html\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding custom HTML (e.g., a description text)\n\nThe :meth:`~mne.Report.add_html` method allows you to add custom HTML to\nyour report. This feature can be very convenient to add short descriptions,\nlists, or reminders to your report (among many other things you can think\nof encoding in HTML).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "report = mne.Report(title=\"Report on hypothesis 1\")\n\nmy_html = \"\"\"\n

We have the following hypothesis:

\n
    \n
  1. There is a difference between images showing man-made vs. natural\nenvironments
  2. \n
  3. This difference manifests itself most strongly in the amplitude of the\nN1 ERP component
  4. \n
\n

Below we show several plots and tests of the data.

\n\"\"\"\n\nreport.add_html(title=\"Hypothesis\", html=my_html)\nreport.save(\"report_add_html.html\", overwrite=True)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }