Note
Click here to download the full example code
Getting started with mne.Report
¶
mne.Report
is a way to create interactive HTML summaries of your data. These
reports can show many different visualizations of one or multiple subject’s
data. A common use case is creating diagnostic summaries to check data quality
at different stages in the processing pipeline. The report can show things like
plots of data before and after each preprocessing step, epoch rejection
statistics, MRI slices with overlaid BEM shells, all the way up to plots of
estimated cortical activity.
Compared to a Jupyter notebook, mne.Report
is easier to deploy (the HTML
pages it generates are self-contained and do not require a running Python
environment) but less flexible (you can’t change code and re-run something
directly within the browser). This tutorial covers the basics of building a
Report
. As usual, we’ll start by importing the modules and data we need:
from pathlib import Path
import tempfile
import numpy as np
import scipy.ndimage
import matplotlib.pyplot as plt
import mne
data_path = Path(mne.datasets.sample.data_path(verbose=False))
sample_dir = data_path / 'MEG' / 'sample'
subjects_dir = data_path / 'subjects'
Before getting started with mne.Report
, make sure the files you want
to render follow the filename conventions defined by MNE:
Data object |
Filename convention (ends with) |
---|---|
|
|
events |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alternatively, the dash -
in the filename may be replaced with an
underscore _
.
The basic process for creating an HTML report is to instantiate the
Report
class and then use one or more of its many methods to
add content, one element at a time.
You may also use the parse_folder()
method to select
particular files to include in the report. But more on that later.
Adding Raw
data¶
Raw data can be added via the mne.Report.add_raw()
method. It can
operate with a path to a raw file and Raw
objects, and will
produce – among other output – a slider that allows you to scrub through 10
equally-spaced 1-second segments of the data:
Warning
In the following example, we crop the raw data and crop to 60 seconds just to speed up processing, this is not usually recommended!
raw_path = sample_dir / 'sample_audvis_filt-0-40_raw.fif'
raw = mne.io.read_raw(raw_path)
raw.pick_types(eeg=True, eog=True).crop(tmax=60).load_data()
report = mne.Report(title='Raw example')
# This method also accepts a path, e.g., raw=raw_path
report.add_raw(raw=raw, title='Raw', psd=False) # omit PSD plot
report.save('report_raw.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Removing projector <Projection | PCA-v1, active : False, n_channels : 102>
Removing projector <Projection | PCA-v2, active : False, n_channels : 102>
Removing projector <Projection | PCA-v3, active : False, n_channels : 102>
Reading 0 ... 9009 = 0.000 ... 59.999 secs...
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_raw.html
Adding events¶
Events can be added via mne.Report.add_events()
. You also need to
supply the sampling frequency used during the recording; this information
is used to generate a meaningful time axis.
events_path = sample_dir / 'sample_audvis_filt-0-40_raw-eve.fif'
events = mne.read_events(events_path)
sfreq = raw.info['sfreq']
report = mne.Report(title='Events example')
report.add_events(events=events_path, title='Events from Path', sfreq=sfreq)
report.add_events(events=events, title='Events from "events"', sfreq=sfreq)
report.save('report_events.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_events.html
Adding Epochs
¶
Epochs can be added via mne.Report.add_epochs()
. Note that although
this method accepts a path to an epochs file too, in the following example
we only add epochs that we create on the fly from raw data.
epochs = mne.Epochs(raw=raw, events=events)
report = mne.Report(title='Epochs example')
report.add_epochs(epochs=epochs, title='Epochs from "epochs"')
report.save('report_epochs.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Not setting metadata
Not setting metadata
319 matching events found
Setting baseline interval to [-0.19979521315838786, 0.0] sec
Applying baseline correction (mode: mean)
Created an SSP operator (subspace dimension = 1)
1 projection items activated
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Loading data for 319 events and 106 original time points ...
233 bad epochs dropped
Using multitaper spectrum estimation with 7 DPSS windows
Saving report to : /home/circleci/project/tutorials/intro/report_epochs.html
Adding Evoked
¶
Evoked data can be added via mne.Report.add_evokeds()
. By default, the
Evoked.comment
attribute of each evoked will be used as a title. We can
specify custom titles via the titles
parameter. Again, this method
also accepts the path to an evoked file stored on disk; in the following
example, however, we load the evokeds manually first, since we only want to
add a subset of them to the report. The evokeds are not baseline-corrected,
so we apply baseline correction, too. Lastly, by providing an (optional)
noise covariance, we can add plots evokeds that were “whitened” using this
covariance matrix.
By default, this method will produce snapshots at 21 equally-spaced time
points (or fewer, if the data contains fewer time points). We can adjust this
via the n_time_points
parameter.
evoked_path = sample_dir / 'sample_audvis-ave.fif'
cov_path = sample_dir / 'sample_audvis-cov.fif'
evokeds = mne.read_evokeds(evoked_path, baseline=(None, 0))
evokeds_subset = evokeds[:2] # The first two
for evoked in evokeds_subset:
evoked.pick('eeg') # just for speed of plotting
report = mne.Report(title='Evoked example')
report.add_evokeds(
evokeds=evokeds_subset,
titles=['evoked 1', # Manually specify titles
'evoked 2'],
noise_cov=cov_path,
n_time_points=5
)
report.save('report_evoked.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Reading /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis-ave.fif ...
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Left Auditory)
0 CTF compensation matrices available
nave = 55 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
Applying baseline correction (mode: mean)
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Right Auditory)
0 CTF compensation matrices available
nave = 61 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
Applying baseline correction (mode: mean)
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Left visual)
0 CTF compensation matrices available
nave = 67 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
Applying baseline correction (mode: mean)
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Right visual)
0 CTF compensation matrices available
nave = 58 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
Applying baseline correction (mode: mean)
Removing projector <Projection | PCA-v1, active : True, n_channels : 102>
Removing projector <Projection | PCA-v2, active : True, n_channels : 102>
Removing projector <Projection | PCA-v3, active : True, n_channels : 102>
Removing projector <Projection | PCA-v1, active : True, n_channels : 102>
Removing projector <Projection | PCA-v2, active : True, n_channels : 102>
Removing projector <Projection | PCA-v3, active : True, n_channels : 102>
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
366 x 366 full covariance (kind = 1) found.
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
combining channels using "gfp"
Computing rank from covariance with rank=None
Using tolerance 4.7e-14 (2.2e-16 eps * 59 dim * 3.6 max singular value)
Estimated rank (eeg): 58
EEG: rank 58 computed from 59 data channels with 1 projector
Created an SSP operator (subspace dimension = 1)
Computing rank from covariance with rank={'eeg': 58}
Setting small EEG eigenvalues to zero (without PCA)
Created the whitener using a noise covariance matrix with rank 58 (1 small eigenvalues omitted)
combining channels using "gfp"
Computing rank from covariance with rank=None
Using tolerance 4.7e-14 (2.2e-16 eps * 59 dim * 3.6 max singular value)
Estimated rank (eeg): 58
EEG: rank 58 computed from 59 data channels with 1 projector
Created an SSP operator (subspace dimension = 1)
Computing rank from covariance with rank={'eeg': 58}
Setting small EEG eigenvalues to zero (without PCA)
Created the whitener using a noise covariance matrix with rank 58 (1 small eigenvalues omitted)
Saving report to : /home/circleci/project/tutorials/intro/report_evoked.html
Adding Covariance
¶
(Noise) covariance objects can be added via
mne.Report.add_covariance()
. The method accepts Covariance
objects and the path to a file on disk. It also expects us to pass an
Info
object or the path to a file to read the measurement info from,
as well as a title.
cov_path = sample_dir / 'sample_audvis-cov.fif'
report = mne.Report(title='Covariance example')
report.add_covariance(cov=cov_path, info=raw_path, title='Covariance')
report.save('report_cov.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
366 x 366 full covariance (kind = 1) found.
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Computing rank from covariance with rank=None
Using tolerance 2.5e-14 (2.2e-16 eps * 102 dim * 1.1 max singular value)
Estimated rank (mag): 102
MAG: rank 102 computed from 102 data channels with 0 projectors
Computing rank from covariance with rank=None
Using tolerance 2.6e-12 (2.2e-16 eps * 204 dim * 56 max singular value)
Estimated rank (grad): 204
GRAD: rank 204 computed from 204 data channels with 0 projectors
Computing rank from covariance with rank=None
Using tolerance 4.8e-14 (2.2e-16 eps * 60 dim * 3.6 max singular value)
Estimated rank (eeg): 60
EEG: rank 60 computed from 60 data channels with 0 projectors
Saving report to : /home/circleci/project/tutorials/intro/report_cov.html
Adding Projection
vectors¶
Projection
vectors can be added via
mne.Report.add_projs()
. The method requires an Info
object
(or the path to one) and a title. Projectors found in the Info
will
be visualized. You may also supply a list of Projection
objects or
a path to projectors stored on disk. In this case, the channel information
is read from the Info
, but projectors potentially included will be
ignored; instead, only the explicitly passed projectors will be plotted.
ecg_proj_path = sample_dir / 'sample_audvis_ecg-proj.fif'
eog_proj_path = sample_dir / 'sample_audvis_eog-proj.fif'
report = mne.Report(title='Projectors example')
report.add_projs(info=raw_path, title='Projs from info')
report.add_projs(info=raw_path, projs=ecg_proj_path,
title='ECG projs from path')
report.add_projs(info=raw_path, projs=eog_proj_path,
title='EOG projs from path')
report.save('report_projs.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Read a total of 6 projection items:
ECG-planar-999--0.200-0.400-PCA-01 (1 x 203) idle
ECG-planar-999--0.200-0.400-PCA-02 (1 x 203) idle
ECG-axial-999--0.200-0.400-PCA-01 (1 x 102) idle
ECG-axial-999--0.200-0.400-PCA-02 (1 x 102) idle
ECG-eeg-999--0.200-0.400-PCA-01 (1 x 59) idle
ECG-eeg-999--0.200-0.400-PCA-02 (1 x 59) idle
Read a total of 6 projection items:
EOG-planar-998--0.200-0.200-PCA-01 (1 x 203) idle
EOG-planar-998--0.200-0.200-PCA-02 (1 x 203) idle
EOG-axial-998--0.200-0.200-PCA-01 (1 x 102) idle
EOG-axial-998--0.200-0.200-PCA-02 (1 x 102) idle
EOG-eeg-998--0.200-0.200-PCA-01 (1 x 59) idle
EOG-eeg-998--0.200-0.200-PCA-02 (1 x 59) idle
Saving report to : /home/circleci/project/tutorials/intro/report_projs.html
Adding ICA
¶
ICA
objects can be added via
mne.Report.add_ica()
. Aside from the parameters ica
(that accepts
an ICA
instance or a path to an ICA object stored on
disk) and the title
, there is a third required parameter, inst
.
inst
is used to specify a Raw
or Epochs
object for
producing ICA property plots and overlay plots demonstrating
the effects of ICA cleaning. If, instead, you only want to generate ICA
component topography plots, explicitly pass inst=None
.
Note
mne.Report.add_ica()
only works with fitted ICAs.
You can optionally specify for which components to produce topography and
properties plots by passing picks
. By default, all components will be
shown. It is also possible to pass evoked signals based on ECG and EOG events
via ecg_evoked
and eog_evoked
. This allows you directly see the
effects of ICA component removal on these artifactual signals.
Artifact detection scores produced by
find_bads_ecg()
and find_bads_eog()
can be passed via the
ecg_scores
and eog_scores
parameters, respectively, producing
visualizations of the scores for each ICA component.
Lastly, by passing n_jobs
, you may largely speed up the generation of
the properties plots by enabling parallel execution.
Warning
In the following example, we request a small number of ICA components to estimate, set the threshold for assuming ICA convergence to a very liberal value, and only visualize 2 of the components. All of this is done to largely reduce the processing time of this tutorial, and is usually not recommended for an actual data analysis.
ica = mne.preprocessing.ICA(
n_components=5, # fit 5 ICA components
fit_params=dict(tol=0.01) # assume very early on that ICA has converged
)
ica.fit(inst=raw)
# create epochs based on EOG events, find EOG artifacts in the data via pattern
# matching, and exclude the EOG-related ICA components
eog_epochs = mne.preprocessing.create_eog_epochs(raw=raw)
eog_components, eog_scores = ica.find_bads_eog(
inst=eog_epochs,
ch_name='EEG 001', # a channel close to the eye
threshold=1 # lower than the default threshold
)
ica.exclude = eog_components
report = mne.Report(title='ICA example')
report.add_ica(
ica=ica,
title='ICA cleaning',
picks=[0, 1], # only plot the first two components
inst=raw,
eog_evoked=eog_epochs.average(),
eog_scores=eog_scores,
n_jobs=1 # could be increased!
)
report.save('report_ica.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Fitting ICA to data using 59 channels (please be patient, this may take a while)
Selecting by number: 5 components
Fitting ICA took 0.1s.
Using EOG channel: EOG 061
EOG channel index for this subject is: [59]
Filtering the data to remove DC offset to help distinguish blinks from saccades
Setting up band-pass filter from 1 - 10 Hz
FIR filter parameters
---------------------
Designing a two-pass forward and reverse, zero-phase, non-causal bandpass filter:
- Windowed frequency-domain design (firwin2) method
- Hann window
- Lower passband edge: 1.00
- Lower transition bandwidth: 0.50 Hz (-12 dB cutoff frequency: 0.75 Hz)
- Upper passband edge: 10.00 Hz
- Upper transition bandwidth: 0.50 Hz (-12 dB cutoff frequency: 10.25 Hz)
- Filter length: 1502 samples (10.003 sec)
Now detecting blinks and generating corresponding events
Found 10 significant peaks
Number of EOG events detected: 10
Not setting metadata
Not setting metadata
10 matching events found
No baseline correction applied
Created an SSP operator (subspace dimension = 1)
Loading data for 10 events and 151 original time points ...
0 bad epochs dropped
Using EOG channel: EEG 001
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Applying ICA to Raw instance
Transforming to ICA space (5 components)
Zeroing out 1 ICA component
Projecting back using 59 PCA components
Using multitaper spectrum estimation with 7 DPSS windows
Not setting metadata
Not setting metadata
30 matching events found
No baseline correction applied
0 projection items activated
0 bad epochs dropped
Using multitaper spectrum estimation with 7 DPSS windows
Not setting metadata
Not setting metadata
30 matching events found
No baseline correction applied
0 projection items activated
0 bad epochs dropped
Saving report to : /home/circleci/project/tutorials/intro/report_ica.html
Adding MRI with BEM¶
MRI slices with superimposed traces of the boundary element model (BEM)
surfaces can be added via mne.Report.add_bem()
. All you need to pass is
the FreeSurfer subject name and subjects directory, and a title. To reduce
the resulting file size, you may pass the decim
parameter to only include
every n-th volume slice.
report = mne.Report(title='BEM example')
report.add_bem(
subject='sample', subjects_dir=subjects_dir, title='MRI & BEM',
decim=20
)
report.save('report_mri_and_bem.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/inner_skull.surf
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/outer_skull.surf
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/outer_skin.surf
Saving report to : /home/circleci/project/tutorials/intro/report_mri_and_bem.html
Adding coregistration¶
The head -> mri
transformation (obtained by “coregistration”) can be
visualized via mne.Report.add_trans()
. The method expects the
transformation either as a Transform
object or as a path to
a trans.fif
file, the FreeSurfer subject name and subjects directory, and
a title.
trans_path = sample_dir / 'sample_audvis_raw-trans.fif'
report = mne.Report(title='Coregistration example')
report.add_trans(
trans=trans_path, info=raw_path, subject='sample',
subjects_dir=subjects_dir, title='Coregistration'
)
report.save('report_coregistration.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Getting helmet for system 306m
Using lh.seghead for head surface.
Channel types:: grad: 203, mag: 102, eeg: 59
Using surface from /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/sample-head.fif.
Saving report to : /home/circleci/project/tutorials/intro/report_coregistration.html
Adding a Forward
solution¶
Forward solutions (“leadfields”) can be added by passing a Forward
object or the path to a forward solution stored on disk to
meth:mne.Report.add_forward
.
fwd_path = sample_dir / 'sample_audvis-meg-oct-6-fwd.fif'
report = mne.Report(title='Forward solution example')
report.add_forward(forward=fwd_path, title='Forward solution')
report.save('report_forward_sol.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Reading forward solution from /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis-meg-oct-6-fwd.fif...
Reading a source space...
Computing patch statistics...
Patch information added...
Distance information added...
[done]
Reading a source space...
Computing patch statistics...
Patch information added...
Distance information added...
[done]
2 source spaces read
Desired named matrix (kind = 3523) not available
Read MEG forward solution (7498 sources, 306 channels, free orientations)
Source spaces transformed to the forward solution coordinate frame
Saving report to : /home/circleci/project/tutorials/intro/report_forward_sol.html
Adding an InverseOperator
¶
An inverse operator can be added via mne.Report.add_inverse_operator()
.
The method expects an InverseOperator
object or a path to
one stored on disk, and a title.
inverse_op_path = sample_dir / 'sample_audvis-meg-oct-6-meg-inv.fif'
report = mne.Report(title='Inverse operator example')
report.add_inverse_operator(
inverse_operator=inverse_op_path, title='Inverse operator'
)
report.save('report_inverse_op.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Reading inverse operator decomposition from /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif...
Reading inverse operator info...
[done]
Reading inverse operator decomposition...
[done]
305 x 305 full covariance (kind = 1) found.
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Noise covariance matrix read.
22494 x 22494 diagonal covariance (kind = 2) found.
Source covariance matrix read.
22494 x 22494 diagonal covariance (kind = 6) found.
Orientation priors read.
22494 x 22494 diagonal covariance (kind = 5) found.
Depth priors read.
Did not find the desired covariance matrix (kind = 3)
Reading a source space...
Computing patch statistics...
Patch information added...
Distance information added...
[done]
Reading a source space...
Computing patch statistics...
Patch information added...
Distance information added...
[done]
2 source spaces read
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Source spaces transformed to the inverse solution coordinate frame
Saving report to : /home/circleci/project/tutorials/intro/report_inverse_op.html
Adding a SourceEstimate
¶
An inverse solution (also called source estimate or source time course, STC)
can be added via mne.Report.add_stc()
. The
method expects an SourceEstimate
, the corresponding FreeSurfer subject
name and subjects directory, and a title. By default, it will produce
snapshots at 51 equally-spaced time points (or fewer, if the data contains
fewer time points). We can adjust this via the n_time_points
parameter.
stc_path = sample_dir / 'sample_audvis-meg'
report = mne.Report(title='Source estimate example')
report.add_stc(
stc=stc_path, subject='sample', subjects_dir=subjects_dir,
title='Source estimate', n_time_points=2 # few for speed
)
report.save('report_inverse_sol.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Using control points [ 5.01632618 6.06303297 21.63565434]
Saving report to : /home/circleci/project/tutorials/intro/report_inverse_sol.html
Adding source code (e.g., a Python script)¶
It is possible to add code or scripts (e.g., the scripts you used for
analysis) to the report via mne.Report.add_code()
. The code blocks will
be automatically syntax-highlighted. You may pass a string with the
respective code snippet, or the path to a file. If you pass a path, it
must be a pathlib.Path
object (and not a string), otherwise it will be
treated as a code literal.
Optionally, you can specify which programming language to assume for syntax
highlighting by passing the language
parameter. By default, we’ll assume
the provided code is Python.
mne_init_py_path = Path(mne.__file__) # __init__.py in the MNE-Python root
mne_init_py_content = mne_init_py_path.read_text(encoding='utf-8')
report = mne.Report(title='Code example')
report.add_code(
code=mne_init_py_path,
title="Code from Path"
)
report.add_code(
code=mne_init_py_content,
title="Code from string"
)
report.save('report_code.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_code.html
Adding custom figures¶
Custom Matplotlib figures can be added via add_figure()
.
Required parameters are the figure and a title. Optionally, may add a caption
to appear below the figure. You can also specify the image format of the
image file that will be generated from the figure, so it can be embedded in
the HTML report.
x = np.linspace(start=0, stop=10, num=100)
y = x**2
fig, ax = plt.subplots()
ax.plot(x, y, ls='--', lw=2, color='blue', label='my function')
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
ax.legend()
report = mne.Report(title='Figure example')
report.add_figure(
fig=fig, title='A custom figure',
caption='A blue dashed line reaches up into the sky …',
image_format='PNG'
)
report.save('report_custom_figure.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_custom_figure.html
The mne.Report.add_figure()
method can add multiple figures at once. In
this case, a slider will appear, allowing users to intuitively browse the
figures. To make this work, you need to provide a collection of figures,
a title, and optionally a collection of captions.
In the following example, we will read the MNE logo as a Matplotlib figure and rotate it with different angles. Each rotated figure and its respective caption will be added to a list, which is then used to create the slider.
mne_logo_path = Path(mne.__file__).parent / 'icons' / 'mne_icon-cropped.png'
fig_array = plt.imread(mne_logo_path)
rotation_angles = np.linspace(start=0, stop=360, num=17)
figs = []
captions = []
for angle in rotation_angles:
# Rotate and remove some rounding errors to avoid Matplotlib warnings
fig_array_rotated = scipy.ndimage.rotate(input=fig_array, angle=angle)
fig_array_rotated = fig_array_rotated.clip(min=0, max=1)
# Create the figure
fig, ax = plt.subplots()
ax.imshow(fig_array_rotated)
ax.set_axis_off()
# Store figure and caption
figs.append(fig)
captions.append(f'Rotation angle: {round(angle, 1)}°')
report = mne.Report(title='Multiple figures example')
report.add_figure(fig=figs, title='Fun with figures! 🥳', caption=captions)
report.save('report_custom_figures.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_custom_figures.html
Adding image files¶
Existing images (e.g., photos, screenshots, sketches etc.) can be added
to the report via mne.Report.add_image()
. Supported image formats
include JPEG, PNG, GIF, and SVG (and possibly others). Like with Matplotlib
figures, you can specify a caption to appear below the image.
report = mne.Report(title='Image example')
report.add_image(
image=mne_logo_path, title='MNE',
caption='Powered by 🧠 🧠 🧠 around the world!'
)
report.save('report_custom_image.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_custom_image.html
Editing a saved report¶
Saving to HTML is a write-only operation, meaning that we cannot read an
.html
file back as a Report
object. In order to be able
to edit a report once it’s no longer in-memory in an active Python session,
save it as an HDF5 file instead of HTML:
report = mne.Report(title='Saved report example', verbose=True)
report.add_image(image=mne_logo_path, title='MNE 1')
report.save('report_partial.hdf5', overwrite=True)
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_partial.hdf5
The saved report can be read back and modified or amended. This allows the possibility to e.g. run multiple scripts in a processing pipeline, where each script adds new content to an existing report.
report_from_disk = mne.open_report('report_partial.hdf5')
report_from_disk.add_image(image=mne_logo_path, title='MNE 2')
report_from_disk.save('report_partial.hdf5', overwrite=True)
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Overwriting existing file.
Saving report to : /home/circleci/project/tutorials/intro/report_partial.hdf5
To make this even easier, mne.Report
can be used as a
context manager (note the with
statement)`):
with mne.open_report('report_partial.hdf5') as report:
report.add_image(image=mne_logo_path, title='MNE 3')
report.save('report_final.html', overwrite=True)
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Saving report to : /home/circleci/project/tutorials/intro/report_final.html
Overwriting existing file.
Saving report to : /home/circleci/project/tutorials/intro/report_partial.hdf5
With the context manager, the updated report is also automatically saved
back to report.h5
upon leaving the block.
Adding an entire folder of files¶
We also provide a way to add an entire folder of files to the report at
once, without having to invoke the individual add_*
methods outlined
above for each file. This approach, while convenient, provides less
flexibility with respect to content ordering, tags, titles, etc.
For our first example, we’ll generate a barebones report for all the
.fif
files containing raw data in the sample dataset, by passing the
pattern *raw.fif
to parse_folder()
. We’ll omit the
subject
and subjects_dir
parameters from the Report
constructor, but we’ll also pass render_bem=False
to the
parse_folder()
method — otherwise we would get a warning
about not being able to render MRI and trans
files without knowing the
subject. To save some processing time in this tutorial, we’re also going to
disable rendering of the butterfly plots for the Raw
data by
passing raw_butterfly=False
.
Which files are included depends on both the pattern
parameter passed to
parse_folder()
and also the subject
and
subjects_dir
parameters provided to the Report
constructor.
report = mne.Report(title='parse_folder example')
report.parse_folder(
data_path=data_path, pattern='*raw.fif', render_bem=False,
raw_butterfly=False
)
report.save('report_parse_folder_basic.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/ernoise_raw.fif...
Isotrak not found
Read a total of 3 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Range : 19800 ... 85867 = 32.966 ... 142.965 secs
Ready.
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_raw.fif...
Read a total of 3 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Range : 25800 ... 192599 = 42.956 ... 320.670 secs
Ready.
Iterating over 3 potential files (this may take some
Rendering : /home/circleci/mne_data/MNE-sample-data/MEG/sample/ernoise_raw.fif
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/ernoise_raw.fif...
Isotrak not found
Read a total of 3 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Range : 19800 ... 85867 = 32.966 ... 142.965 secs
Ready.
Rendering : /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Rendering : /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_raw.fif
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_raw.fif...
Read a total of 3 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Range : 25800 ... 192599 = 42.956 ... 320.670 secs
Ready.
Saving report to : /home/circleci/project/tutorials/intro/report_parse_folder_basic.html
By default, the power spectral density and SSP projectors of the
Raw
files are not shown to speed up report generation. You
can add them by passing raw_psd=True
and projs=True
to the
Report
constructor. Like in the previous example, we’re going
to omit the butterfly plots by passing raw_butterfly=False
. Lastly, let’s
also refine our pattern to select only the filtered raw recording (omitting
the unfiltered data and the empty-room noise recordings).
pattern = 'sample_audvis_filt-0-40_raw.fif'
report = mne.Report(title='parse_folder example 2', raw_psd=True, projs=True)
report.parse_folder(
data_path=data_path, pattern=pattern, render_bem=False, raw_butterfly=False
)
report.save('report_parse_folder_raw_psd_projs.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Iterating over 1 potential files (this may take some
Rendering : /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Effective window size : 13.639 (s)
Effective window size : 13.639 (s)
Effective window size : 13.639 (s)
Saving report to : /home/circleci/project/tutorials/intro/report_parse_folder_raw_psd_projs.html
This time we’ll pass a specific subject
and subjects_dir
(even though
there’s only one subject in the sample dataset) and remove our
render_bem=False
parameter so we can see the MRI slices, with BEM
contours overlaid on top if available. Since this is computationally
expensive, we’ll also pass the mri_decim
parameter for the benefit of our
documentation servers, and skip processing the .fif
files.
report = mne.Report(
title='parse_folder example 3', subject='sample', subjects_dir=subjects_dir
)
report.parse_folder(data_path=data_path, pattern='', mri_decim=25)
report.save('report_parse_folder_mri_bem.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Iterating over 0 potential files (this may take some
Rendering BEM
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/inner_skull.surf
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/outer_skull.surf
Using surface: /home/circleci/mne_data/MNE-sample-data/subjects/sample/bem/outer_skin.surf
Saving report to : /home/circleci/project/tutorials/intro/report_parse_folder_mri_bem.html
Now let’s look at how Report
handles Evoked
data (we will skip the MRIs to save computation time).
The MNE sample dataset we’re using in this example has not been baseline-corrected; so let’s apply baseline correction this now for the report!
To request baseline correction, pass a baseline
argument to
Report
, which should be a tuple with the starting and ending time of
the baseline period. For more details, see the documentation on
apply_baseline
. Here, we will apply baseline correction for a
baseline period from the beginning of the time interval to time point zero.
Lastly, we want to render the “whitened” evoked data, too. Whitening
requires us to specify the path to a covariance matrix file via the
cov_fname
parameter of Report
.
Now, let’s put all of this together! Here we use a temporary directory for speed so we can render a single Evoked instance, using just EEG channels.
baseline = (None, 0)
cov_fname = sample_dir / 'sample_audvis-cov.fif'
pattern = 'sample_audvis-no-filter-ave.fif'
evoked = mne.read_evokeds(sample_dir / pattern)[0]
report = mne.Report(
title='parse_folder example 4', baseline=baseline, cov_fname=cov_fname
)
with tempfile.TemporaryDirectory() as path:
evoked.save(Path(path) / pattern)
report.parse_folder(
path, pattern=pattern, render_bem=False, n_time_points_evokeds=5
)
report.save('report_parse_folder_evoked.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Reading /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis-no-filter-ave.fif ...
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Left Auditory)
0 CTF compensation matrices available
nave = 55 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Right Auditory)
0 CTF compensation matrices available
nave = 59 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Left visual)
0 CTF compensation matrices available
nave = 64 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Right visual)
0 CTF compensation matrices available
nave = 56 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
366 x 366 full covariance (kind = 1) found.
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Iterating over 1 potential files (this may take some
Rendering : /tmp/tmp_4muqump/sample_audvis-no-filter-ave.fif
Reading /tmp/tmp_4muqump/sample_audvis-no-filter-ave.fif ...
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Found the data of interest:
t = -199.80 ... 499.49 ms (Left Auditory)
0 CTF compensation matrices available
nave = 55 - aspect type = 100
Projections have already been applied. Setting proj attribute to True.
No baseline correction applied
combining channels using "gfp"
combining channels using "gfp"
combining channels using "gfp"
Computing rank from covariance with rank=None
Using tolerance 4.7e-14 (2.2e-16 eps * 59 dim * 3.6 max singular value)
Estimated rank (eeg): 58
EEG: rank 58 computed from 59 data channels with 1 projector
Computing rank from covariance with rank=None
Using tolerance 1.8e-13 (2.2e-16 eps * 203 dim * 3.9 max singular value)
Estimated rank (grad): 203
GRAD: rank 203 computed from 203 data channels with 0 projectors
Computing rank from covariance with rank=None
Using tolerance 2.5e-14 (2.2e-16 eps * 102 dim * 1.1 max singular value)
Estimated rank (mag): 99
MAG: rank 99 computed from 102 data channels with 3 projectors
Created an SSP operator (subspace dimension = 4)
Computing rank from covariance with rank={'eeg': 58, 'grad': 203, 'mag': 99, 'meg': 302}
Setting small MEG eigenvalues to zero (without PCA)
Setting small EEG eigenvalues to zero (without PCA)
Created the whitener using a noise covariance matrix with rank 360 (4 small eigenvalues omitted)
Saving report to : /home/circleci/project/tutorials/intro/report_parse_folder_evoked.html
If you want to actually view the noise covariance in the report, make sure
it is captured by the pattern passed to parse_folder()
, and
also include a source for an Info
object (any of the
Raw
, Epochs
or Evoked
.fif
files that contain subject data also contain the measurement
information and should work):
pattern = 'sample_audvis-cov.fif'
info_fname = sample_dir / 'sample_audvis-ave.fif'
report = mne.Report(title='parse_folder example 5', info_fname=info_fname)
report.parse_folder(
data_path, pattern=pattern, render_bem=False, n_time_points_evokeds=5
)
report.save('report_parse_folder_cov.html', overwrite=True)
The HTML document written by mne.Report.save()
:
Out:
Embedding : jquery-3.6.0.min.js
Embedding : bootstrap.bundle.min.js
Embedding : bootstrap.min.css
Embedding : highlightjs/highlight.min.js
Embedding : highlightjs/atom-one-dark-reasonable.min.css
Iterating over 1 potential files (this may take some
Rendering : /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis-cov.fif
366 x 366 full covariance (kind = 1) found.
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Read a total of 4 projection items:
PCA-v1 (1 x 102) active
PCA-v2 (1 x 102) active
PCA-v3 (1 x 102) active
Average EEG reference (1 x 60) active
Computing rank from covariance with rank=None
Using tolerance 2.5e-14 (2.2e-16 eps * 102 dim * 1.1 max singular value)
Estimated rank (mag): 102
MAG: rank 102 computed from 102 data channels with 0 projectors
Computing rank from covariance with rank=None
Using tolerance 2.6e-12 (2.2e-16 eps * 204 dim * 56 max singular value)
Estimated rank (grad): 204
GRAD: rank 204 computed from 204 data channels with 0 projectors
Computing rank from covariance with rank=None
Using tolerance 4.8e-14 (2.2e-16 eps * 60 dim * 3.6 max singular value)
Estimated rank (eeg): 60
EEG: rank 60 computed from 60 data channels with 0 projectors
Saving report to : /home/circleci/project/tutorials/intro/report_parse_folder_cov.html
Total running time of the script: ( 1 minutes 5.306 seconds)
Estimated memory usage: 168 MB