diff --git a/doc/whats_new.rst b/doc/whats_new.rst index 52daeabc293..33e9ae99bc8 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -209,6 +209,7 @@ API - :func:`mne.io.read_raw_egi` now names channels with pattern 'E'. 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: diff --git a/mne/epochs.py b/mne/epochs.py index 1004dc2cd3f..463c4bdbcb8 100644 --- a/mne/epochs.py +++ b/mne/epochs.py @@ -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, ' @@ -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() @@ -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) @@ -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) @@ -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 @@ -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, @@ -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. @@ -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 @@ -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.') @@ -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 @@ -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 diff --git a/mne/io/eeglab/tests/test_eeglab.py b/mne/io/eeglab/tests/test_eeglab.py index dd69dfd6079..88ede7a9efe 100644 --- a/mne/io/eeglab/tests/test_eeglab.py +++ b/mne/io/eeglab/tests/test_eeglab.py @@ -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') diff --git a/mne/realtime/epochs.py b/mne/realtime/epochs.py index b5059ae6b29..1729e9bf6a3 100644 --- a/mne/realtime/epochs.py +++ b/mne/realtime/epochs.py @@ -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'. @@ -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 diff --git a/mne/tests/test_epochs.py b/mne/tests/test_epochs.py index e1217ab7ed2..2e270a6383f 100644 --- a/mne/tests/test_epochs.py +++ b/mne/tests/test_epochs.py @@ -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 @@ -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, @@ -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 == @@ -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 diff --git a/mne/viz/epochs.py b/mne/viz/epochs.py index cbf4bf7a647..74e5c2d2af2 100644 --- a/mne/viz/epochs.py +++ b/mne/viz/epochs.py @@ -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')