Skip to content

Commit

Permalink
[MRG+2] Save name attribute of Epochs. (#4031)
Browse files Browse the repository at this point in the history
* [MRG] Save name attribute of Epochs.

* Deprecate name.

* Fixes.

* Fixes.

* Generator to list.

* Address comments.

* Move logic to epochs.average.

* Move logic.

* name property.

* Fixes.

* Remove save.

* Fix.

* fix realtime epochs class

* udpate what's new

* misc

* misc
  • Loading branch information
jaeilepp authored and larsoner committed Mar 22, 2017
1 parent 96d8b48 commit 8b1087a
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 24 deletions.
1 change: 1 addition & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ API

- :func:`mne.io.read_raw_egi` now names channels with pattern 'E<idx>'. This behavior can be changed with parameter ``channel_naming`` by `Jaakko Leppakangas`_

- the `name`` parameter in :class:`mne.Epochs` is deprecated, by `Jaakko Leppakangas`_

.. _changes_0_13:

Expand Down
60 changes: 46 additions & 14 deletions mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,17 @@ class BaseEpochs(ProjMixin, ContainsMixin, UpdateChannelsMixin,

def __init__(self, info, data, events, event_id=None, tmin=-0.2, tmax=0.5,
baseline=(None, 0), raw=None,
picks=None, name='Unknown', reject=None, flat=None,
picks=None, name=None, reject=None, flat=None,
decim=1, reject_tmin=None, reject_tmax=None, detrend=None,
add_eeg_ref=False, proj=True, on_missing='error',
preload_at_end=False, selection=None, drop_log=None,
filename=None, verbose=None): # noqa: D102
self.verbose = verbose
self.name = name
if name is not None:
warn('name is deprecated and will be removed in 0.15.')
else:
name = 'Unknown'
self._name = name

if on_missing not in ['error', 'warning', 'ignore']:
raise ValueError('on_missing must be one of: error, '
Expand Down Expand Up @@ -823,15 +827,28 @@ def _compute_mean_or_stderr(self, picks, mode='ave'):
else:
kind = 'standard_error'
data /= np.sqrt(n_events)

if self._name not in ['Unknown', None]:
comment = self._name
else:
if len(self.event_id) == 1:
comment = next(iter(self.event_id.keys()))
else:
count = np.bincount(self.events[:, 2])
comments = list()
for key, value in self.event_id.items():
comments.append('%.2f * %s' % (
float(count[value]) / len(self.events), key))
comment = ' + '.join(comments)
return self._evoked_from_epoch_data(data, self.info, picks, n_events,
kind)
kind, comment)

def _evoked_from_epoch_data(self, data, info, picks, n_events, kind):
def _evoked_from_epoch_data(self, data, info, picks, n_events, kind,
comment):
"""Create an evoked object from epoch data."""
info = deepcopy(info)
evoked = EvokedArray(data, info, tmin=self.times[0],
comment=self.name, nave=n_events, kind=kind,
verbose=self.verbose)
evoked = EvokedArray(data, info, tmin=self.times[0], comment=comment,
nave=n_events, kind=kind, verbose=self.verbose)
# XXX: above constructor doesn't recreate the times object precisely
evoked.times = self.times.copy()

Expand Down Expand Up @@ -1287,6 +1304,18 @@ def tmax(self):
"""Last time point."""
return self.times[-1]

@property
def name(self):
"""Name for the epoch set."""
warn('name attribute is deprecated and will be removed in 0.15.')
return self._name

@name.setter
def name(self, name):
"""Name for the epoch set."""
warn('name attribute is deprecated and will be removed in 0.15.')
self._name = name

def __repr__(self):
"""Build string representation."""
s = 'n_events : %s ' % len(self.events)
Expand Down Expand Up @@ -1363,7 +1392,7 @@ def __getitem__(self, item):
if isinstance(item, (list, tuple)) and \
isinstance(item[0], string_types):
select = epochs._keys_to_idx(item)
epochs.name = '+'.join(item)
epochs._name = '+'.join(item)
else:
select = item if isinstance(item, slice) else np.atleast_1d(item)

Expand Down Expand Up @@ -1761,7 +1790,7 @@ class Epochs(BaseEpochs):
picks : array-like of int | None (default)
Indices of channels to include (if None, all channels are used).
name : string
Comment that describes the Epochs data created.
Comment that describes the Epochs data created. Deprecated.
preload : boolean
Load all epochs from disk when creating the object
or wait before accessing each epoch (more memory
Expand Down Expand Up @@ -1878,7 +1907,7 @@ class Epochs(BaseEpochs):

@verbose
def __init__(self, raw, events, event_id=None, tmin=-0.2, tmax=0.5,
baseline=(None, 0), picks=None, name='Unknown', preload=False,
baseline=(None, 0), picks=None, name=None, preload=False,
reject=None, flat=None, proj=True, decim=1, reject_tmin=None,
reject_tmax=None, detrend=None, add_eeg_ref=None,
on_missing='error', reject_by_annotation=True,
Expand Down Expand Up @@ -2579,7 +2608,7 @@ def _check_merge_epochs(epochs_list):


@verbose
def add_channels_epochs(epochs_list, name='Unknown', add_eeg_ref=False,
def add_channels_epochs(epochs_list, name=None, add_eeg_ref=False,
verbose=None):
"""Concatenate channels, info and data from two Epochs objects.
Expand All @@ -2588,7 +2617,7 @@ def add_channels_epochs(epochs_list, name='Unknown', add_eeg_ref=False,
epochs_list : list of Epochs
Epochs object to concatenate.
name : str
Comment that describes the Epochs data created.
Comment that describes the Epochs data created. Deprecated.
add_eeg_ref : bool
If True, an EEG average reference will be added (unless there is
no EEG in the data). This parameter will be removed in 0.15. Use
Expand All @@ -2603,6 +2632,8 @@ def add_channels_epochs(epochs_list, name='Unknown', add_eeg_ref=False,
epochs : instance of Epochs
Concatenated epochs.
"""
if name is not None:
warn('name is deprecated and will be removed in 0.15.')
add_eeg_ref = _dep_eeg_ref(add_eeg_ref)
if not all(e.preload for e in epochs_list):
raise ValueError('All epochs must be preloaded.')
Expand Down Expand Up @@ -2634,7 +2665,7 @@ def add_channels_epochs(epochs_list, name='Unknown', add_eeg_ref=False,
epochs = epochs_list[0].copy()
epochs.info = info
epochs.picks = None
epochs.name = name
epochs._name = name
epochs.verbose = verbose
epochs.events = events
epochs.preload = True
Expand Down Expand Up @@ -2970,7 +3001,8 @@ def average_movements(epochs, head_pos=None, orig_sfreq=None, picks=None,
data[meg_picks] = np.dot(mapping, data[good_picks])
info_to['dev_head_t'] = recon_trans # set the reconstruction transform
evoked = epochs._evoked_from_epoch_data(data, info_to, picks,
n_events=count, kind='average')
n_events=count, kind='average',
comment=epochs._name)
_remove_meg_projs(evoked) # remove MEG projectors, they won't apply now
logger.info('Created Evoked dataset from %s epochs' % (count,))
return (evoked, mapping) if return_mapping else evoked
Expand Down
2 changes: 1 addition & 1 deletion mne/io/eeglab/tests/test_eeglab.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
@requires_version('scipy', '0.12')
@testing.requires_testing_data
def test_io_set():
"""Test importing EEGLAB .set files"""
"""Test importing EEGLAB .set files."""
from scipy import io
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
Expand Down
4 changes: 2 additions & 2 deletions mne/realtime/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class RtEpochs(BaseEpochs):
picks : array-like of int | None (default)
Indices of channels to include (if None, all channels are used).
name : string
Comment that describes the Evoked data created.
Comment that describes the Evoked data created. Deprecated.
reject : dict | None
Rejection parameters based on peak-to-peak amplitude.
Valid keys are 'grad' | 'mag' | 'eeg' | 'eog' | 'ecg'.
Expand Down Expand Up @@ -136,7 +136,7 @@ class RtEpochs(BaseEpochs):
@verbose
def __init__(self, client, event_id, tmin, tmax, stim_channel='STI 014',
sleep_time=0.1, baseline=(None, 0), picks=None,
name='Unknown', reject=None, flat=None, proj=True,
name=None, reject=None, flat=None, proj=True,
decim=1, reject_tmin=None, reject_tmax=None, detrend=None,
add_eeg_ref=False, isi_max=2., find_events=None,
verbose=None): # noqa: D102
Expand Down
11 changes: 6 additions & 5 deletions mne/tests/test_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ def test_epoch_combine_ids():
tmin, tmax, picks=picks, preload=False)
events_new = merge_events(events, [1, 2], 12)
epochs_new = combine_event_ids(epochs, ['a', 'b'], {'ab': 12})
assert_equal(epochs_new['ab'].name, 'ab')
assert_equal(epochs_new['ab']._name, 'ab')
assert_array_equal(events_new, epochs_new.events)
# should probably add test + functionality for non-replacement XXX

Expand Down Expand Up @@ -698,6 +698,7 @@ def test_read_write_epochs():
epochs_read2 = read_epochs(op.join(tempdir, 'foo-epo.fif'),
preload=preload)
assert_equal(epochs_read2.event_id, epochs.event_id)
assert_equal(epochs_read2['a:a'].average().comment, 'a:a')

# add reject here so some of the epochs get dropped
epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
Expand Down Expand Up @@ -911,8 +912,8 @@ def test_evoked_standard_error():
evoked = [epochs.average(), epochs.standard_error()]
write_evokeds(op.join(tempdir, 'evoked-ave.fif'), evoked)
evoked2 = read_evokeds(op.join(tempdir, 'evoked-ave.fif'), [0, 1])
evoked3 = [read_evokeds(op.join(tempdir, 'evoked-ave.fif'), 'Unknown'),
read_evokeds(op.join(tempdir, 'evoked-ave.fif'), 'Unknown',
evoked3 = [read_evokeds(op.join(tempdir, 'evoked-ave.fif'), '1'),
read_evokeds(op.join(tempdir, 'evoked-ave.fif'), '1',
kind='standard_error')]
for evoked_new in [evoked2, evoked3]:
assert_true(evoked_new[0]._aspect_kind ==
Expand Down Expand Up @@ -1478,8 +1479,8 @@ def test_access_by_name():
assert_array_almost_equal(epochs.get_data(), epochs6.get_data(), 20)

# Make sure we preserve names
assert_equal(epochs['a'].name, 'a')
assert_equal(epochs[['a', 'b']]['a'].name, 'a')
assert_equal(epochs['a']._name, 'a')
assert_equal(epochs[['a', 'b']]['a']._name, 'a')


@requires_pandas
Expand Down
4 changes: 2 additions & 2 deletions mne/viz/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,8 @@ def _prepare_mne_browse_epochs(params, projs, n_channels, n_epochs, scalings,
size = size.split(',')
size = tuple(float(s) for s in size)
if title is None:
title = epochs.name
if epochs.name is None or len(title) == 0:
title = epochs._name
if title is None or len(title) == 0:
title = ''
fig = figure_nobar(facecolor='w', figsize=size, dpi=80)
fig.canvas.set_window_title('mne_browse_epochs')
Expand Down

0 comments on commit 8b1087a

Please sign in to comment.