Skip to content

Commit e273cac

Browse files
Merge branch 'main' into enh/save_movie
2 parents 3fe5b88 + 4ba386b commit e273cac

File tree

22 files changed

+413
-191
lines changed

22 files changed

+413
-191
lines changed

doc/_static/style.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ iframe.sg_report {
4545
display: block;
4646
border-style: solid;
4747
}
48+
/* gallery thumbnail size */
49+
.sphx-glr-thumbcontainer {
50+
min-width: 160px;
51+
height: 250px;
52+
}
4853

4954
/* ******************************** make HTML'd pandas dataframes scrollable */
5055
output_html {

doc/changes/latest.inc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Current (0.23.dev0)
1919

2020
.. |New Contributor| replace:: **New Contributor**
2121

22+
.. |Sumalyo Datta| replace:: **Sumalyo Datta**
23+
2224
.. |Anna Padee| replace:: **Anna Padee**
2325

2426
.. |Richard Koehler| replace:: **Richard Koehler**
@@ -55,6 +57,8 @@ Current (0.23.dev0)
5557

5658
Enhancements
5759
~~~~~~~~~~~~
60+
- Add HTML representation for `~mne.Epochs` in Jupyter Notebooks (:gh:`9174` by |Valerii Chirkov|_)
61+
5862
- Speed up :func:`mne.viz.plot_ica_properties` by refactoring (:gh:`9174` **by new contributor** |Valerii Chirkov|_)
5963

6064
- Add ``apply_function`` method to epochs and evoked objects (:gh:`9088` **by new contributor** |Erica Peterson|_ and `Victoria Peterson`_)
@@ -153,8 +157,14 @@ Enhancements
153157

154158
- Add parameter ``theme`` to :class:`mne.viz.Brain` for optional Dark-Mode (:gh:`9149` by `Martin Schulz`_, `Guillaume Favelier`_)
155159

160+
- Add first_samp support for raw simulations with `mne.simulation.simulate_raw` and `mne.simulation.SourceSimulator` (:gh:`9166` by `Steven Bierer`_)
161+
156162
Bugs
157163
~~~~
164+
- Fix bug with :func:`mne.io.read_raw_edf` where µV was not correctly recognized (:gh:`9187` **by new contributor** |Sumalyo Datta|_)
165+
166+
- Fix bug with :func:`mne.viz.plot_compare_evokeds` did not check type of combine. (:gh:`9151` **by new contributor** |Matteo Anelli|_)
167+
158168
- Fix bug with :func:`mne.viz.plot_evoked_topo` where ``ylim`` was only being applied to the first channel in the dataset (:gh:`9162` **by new contributor** |Ram Pari|_ )
159169

160170
- Fix bug with :func:`mne.Epochs.plot_image` allowing interactive zoom to work properly (:gh:`9152` by **by new contributor** |Maggie Clarke|_ and `Daniel McCloy`_)

doc/changes/names.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,5 @@
383383
.. _Ram Pari: https://github.com/ramkpari
384384
385385
.. _Erica Peterson: https://github.com/nordme
386+
387+
.. _Sumalyo Datta: https://github.com/Sumalyo

examples/preprocessing/plot_run_ica.py

Lines changed: 0 additions & 64 deletions
This file was deleted.

examples/simulation/plot_source_simulator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class to generate source estimates and raw data. It is meant to be a brief
6060
# of the event, the second is not used, and the third is the event id. Here the
6161
# events occur every 200 samples.
6262
n_events = 50
63-
events = np.zeros((n_events, 3))
63+
events = np.zeros((n_events, 3), int)
6464
events[:, 0] = 100 + 200 * np.arange(n_events) # Events sample.
6565
events[:, 2] = 1 # All events have the sample id.
6666

mne/data/html_templates.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,28 @@
7575
</tr>
7676
</table>
7777
""")
78+
79+
epochs_template = Template("""
80+
<table class="table table-hover">
81+
<tr>
82+
<th>Number of events</th>
83+
<td>{{len(epochs.events)}}</td>
84+
</tr>
85+
<tr>
86+
<th>Events</th>
87+
{{if events is not None}}
88+
<td>{{events}}</td>
89+
{{else}}
90+
<td>Not available</td>
91+
{{endif}}
92+
</tr>
93+
<tr>
94+
<th>Time range</th>
95+
<td>{{f'{epochs.tmin:.3f} – {epochs.tmax:.3f} sec'}}</td>
96+
</tr>
97+
<tr>
98+
<th>Baseline</th>
99+
<td>{{baseline}}</td>
100+
</tr>
101+
</table>
102+
""")

mne/epochs.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import json
1717
import operator
1818
import os.path as op
19-
import warnings
2019

2120
import numpy as np
2221

@@ -57,8 +56,9 @@
5756
_check_combine, ShiftTimeMixin, _build_data_frame,
5857
_check_pandas_index_arguments, _convert_times,
5958
_scale_dataframe_data, _check_time_format, object_size,
60-
_on_missing, _validate_type)
59+
_on_missing, _validate_type, _ensure_events)
6160
from .utils.docs import fill_doc
61+
from .data.html_templates import epochs_template
6262

6363

6464
def _pack_reject_params(epochs):
@@ -397,16 +397,7 @@ def __init__(self, info, data, events, event_id=None, tmin=-0.2, tmax=0.5,
397397
self.verbose = verbose
398398

399399
if events is not None: # RtEpochs can have events=None
400-
events_type = type(events)
401-
with warnings.catch_warnings(record=True):
402-
warnings.simplefilter('ignore') # deprecation for object array
403-
events = np.asarray(events)
404-
if not np.issubdtype(events.dtype, np.integer):
405-
raise TypeError('events should be a NumPy array of integers, '
406-
f'got {events_type}')
407-
if events.ndim != 2 or events.shape[1] != 3:
408-
raise ValueError(
409-
f'events must be of shape (N, 3), got {events.shape}')
400+
events = _ensure_events(events)
410401
events_max = events.max()
411402
if events_max > INT32_MAX:
412403
raise ValueError(
@@ -1599,6 +1590,31 @@ def __repr__(self):
15991590
class_name = 'Epochs' if class_name == 'BaseEpochs' else class_name
16001591
return '<%s | %s>' % (class_name, s)
16011592

1593+
def _repr_html_(self):
1594+
if self.baseline is None:
1595+
baseline = 'off'
1596+
else:
1597+
baseline = tuple([f'{b:.3f}' for b in self.baseline])
1598+
baseline = f'{baseline[0]}{baseline[1]} sec'
1599+
1600+
if isinstance(self.event_id, dict):
1601+
events = ''
1602+
for k, v in sorted(self.event_id.items()):
1603+
n_events = sum(self.events[:, 2] == v)
1604+
events += f'{k}: {n_events}<br>'
1605+
elif isinstance(self.event_id, list):
1606+
events = ''
1607+
for k in self.event_id:
1608+
n_events = sum(self.events[:, 2] == k)
1609+
events += f'{k}: {n_events}<br>'
1610+
elif isinstance(self.event_id, int):
1611+
n_events = len(self.events[:, 2])
1612+
events = f'{self.event_id}: {n_events}<br>'
1613+
else:
1614+
events = None
1615+
return epochs_template.substitute(epochs=self, baseline=baseline,
1616+
events=events)
1617+
16021618
@verbose
16031619
def crop(self, tmin=None, tmax=None, include_tmax=True, verbose=None):
16041620
"""Crop a time interval from the epochs.

mne/io/edf/edf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,8 @@ def _read_edf_header(fname, exclude):
613613
for i, unit in enumerate(units):
614614
if i in exclude:
615615
continue
616-
if unit == 'uV':
616+
# allow both μ (greek mu) and µ (micro symbol) codepoints
617+
if unit in ('\u03BCV', '\u00B5V', 'uV'):
617618
edf_info['units'].append(1e-6)
618619
elif unit == 'mV':
619620
edf_info['units'].append(1e-3)

mne/report.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,7 @@ def _render_epochs(self, epo_fname, image_format, data_path):
20722072
html = image_template.substitute(
20732073
img=img, id=global_id, div_klass='epochs', img_klass='epochs',
20742074
caption=caption, show=show, image_format=image_format)
2075+
html += epochs._repr_html_()
20752076
return html
20762077

20772078
def _render_cov(self, cov_fname, info_fname, image_format, data_path,

mne/simulation/raw.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from ..utils import (logger, verbose, check_random_state, _pl, _validate_type,
3434
_check_preload)
3535
from ..parallel import check_n_jobs
36+
from .source import SourceSimulator
3637

3738

3839
def _check_cov(info, cov):
@@ -110,8 +111,11 @@ def _check_head_pos(head_pos, info, first_samp, times=None):
110111
raise RuntimeError('All position times must be <= t_end (%0.1f '
111112
'sec), found %s/%s bad values (is this a split '
112113
'file?)' % (times[-1], bad.sum(), len(bad)))
114+
# If it starts close to zero, make it zero (else unique(offset) fails)
115+
if len(ts) > 0 and ts[0] < (0.5 / info['sfreq']):
116+
ts[0] = 0.
113117
# If it doesn't start at zero, insert one at t=0
114-
if len(ts) == 0 or ts[0] > 0:
118+
elif len(ts) == 0 or ts[0] > 0:
115119
ts = np.r_[[0.], ts]
116120
dev_head_ts.insert(0, info['dev_head_t']['trans'])
117121
dev_head_ts = [{'trans': d, 'to': info['dev_head_t']['to'],
@@ -140,7 +144,7 @@ def simulate_raw(info, stc=None, trans=None, src=None, bem=None, head_pos=None,
140144
141145
.. versionchanged:: 0.18
142146
Support for :class:`mne.Info`.
143-
stc : iterable | SourceEstimate
147+
stc : iterable | SourceEstimate | SourceSimulator
144148
The source estimates to use to simulate data. Each must have the same
145149
sample rate as the raw data, and the vertices of all stcs in the
146150
iterable must match. Each entry in the iterable can also be a tuple of
@@ -149,7 +153,8 @@ def simulate_raw(info, stc=None, trans=None, src=None, bem=None, head_pos=None,
149153
See Notes for details.
150154
151155
.. versionchanged:: 0.18
152-
Support for tuple, and iterable of tuple or SourceEstimate.
156+
Support for tuple, iterable of tuple or `~mne.SourceEstimate`,
157+
or `~mne.simulation.SourceSimulator`.
153158
trans : dict | str | None
154159
Either a transformation filename (usually made using mne_analyze)
155160
or an info dict (usually opened using read_trans()).
@@ -271,8 +276,10 @@ def simulate_raw(info, stc=None, trans=None, src=None, bem=None, head_pos=None,
271276
logger.info('Setting up raw simulation: %s position%s, "%s" interpolation'
272277
% (len(dev_head_ts), _pl(dev_head_ts), interp))
273278

279+
if isinstance(stc, SourceSimulator) and stc.first_samp != first_samp:
280+
logger.info('SourceSimulator first_samp does not match argument.')
281+
274282
stc_enum, stc_counted, verts = _check_stc_iterable(stc, info)
275-
# del stc
276283
if forward is not None:
277284
forward = restrict_forward_to_stc(forward, verts)
278285
src = forward['src']

0 commit comments

Comments
 (0)