Skip to content

Commit

Permalink
[MRG+1] Boundary annotations. (#4082)
Browse files Browse the repository at this point in the history
* Boundary annotations.

* Fixes.

* Docs.

* Update whats_new.
  • Loading branch information
jaeilepp authored and larsoner committed Mar 22, 2017
1 parent 7bdeaf5 commit 7d2ae7f
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 4 deletions.
2 changes: 1 addition & 1 deletion doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ Changelog

- Add plotting of head positions as a function of time in :func:`mne.viz.plot_head_positions` by `Eric Larson`_


- Add ``real_filter`` option to :func:`mne.beamformer.dics`, :func:`mne.beamformer.dics_source_power`, :func:`mne.beamformer.tf_dics` and :func:`mne.beamformer.dics_epochs` by `Eric Larson`_, `Alex Gramfort`_ and `Andrea Brovelli`_.

- Add a demo script showing how to use a custom inverse solver with MNE by `Alex Gramfort`_
Expand All @@ -100,6 +99,7 @@ Changelog

- Allow using ``spatial_colors`` for non-standard layouts by creating custom layouts from channel locations and add ``to_sphere`` keyword to :func:`mne.viz.plot_sensors` to allow plotting sensors that are not on the head surface by `Jaakko Leppakangas`_

- Concatenating raws with :func:`mne.concatenate_raws` now creates boundary annotations automatically by `Jaakko Leppakangas`_

BUG
~~~
Expand Down
17 changes: 17 additions & 0 deletions mne/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,11 @@ def load_bad_channels(self, bad_file=None, force=False):
def append(self, raws, preload=None):
"""Concatenate raw instances as if they were continuous.
.. note:: Boundaries of the raw files are annotated bad. If you wish to
use the data as continuous recording, you can remove the
boundary annotations after concatenation (see
:meth:`mne.Annotations.delete`).
Parameters
----------
raws : list, or Raw instance
Expand Down Expand Up @@ -1959,19 +1964,27 @@ def append(self, raws, preload=None):

# now combine information from each raw file to construct new self
annotations = self.annotations
edge_samps = list()
for r in raws:
annotations = _combine_annotations((annotations, r.annotations),
self._last_samps,
self._first_samps,
self.info['sfreq'],
self.info['meas_date'])
edge_samps.append(sum(self._last_samps) - sum(self._first_samps))
self._first_samps = np.r_[self._first_samps, r._first_samps]
self._last_samps = np.r_[self._last_samps, r._last_samps]
self._raw_extras += r._raw_extras
self._filenames += r._filenames

self._update_times()
if annotations is None:
annotations = Annotations([], [], [])
self.annotations = annotations
for edge_samp in edge_samps:
onset = _sync_onset(self, (edge_samp) / self.info['sfreq'], True)
self.annotations.append(onset, (1. / self.info['sfreq']),
'BAD boundary')

if not (len(self._first_samps) == len(self._last_samps) ==
len(self._raw_extras) == len(self._filenames)):
Expand Down Expand Up @@ -2387,6 +2400,10 @@ def concatenate_raws(raws, preload=None, events_list=None):
"""Concatenate raw instances as if they were continuous.
.. note:: ``raws[0]`` is modified in-place to achieve the concatenation.
Boundaries of the raw files are annotated bad. If you wish to use
the data as continuous recording, you can remove the boundary
annotations after concatenation (see
:meth:`mne.Annotations.delete`).
Parameters
----------
Expand Down
4 changes: 2 additions & 2 deletions mne/io/fiff/tests/test_raw_fiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -1201,8 +1201,8 @@ def test_save():
onsets = raw.annotations.onset
durations = raw.annotations.duration
# 2*5s clips combined with annotations at 2.5s + 2s clip, annotation at 1s
assert_array_almost_equal([2.5, 7.5, 11.], onsets, decimal=2)
assert_array_almost_equal([2., 2.5, 1.], durations, decimal=2)
assert_array_almost_equal([2.5, 7.5, 11.], onsets[:3], decimal=2)
assert_array_almost_equal([2., 2.5, 1.], durations[:3], decimal=2)

# test annotation clipping
annot = Annotations([0., raw.times[-1]], [2., 2.], 'test',
Expand Down
3 changes: 3 additions & 0 deletions mne/io/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ def _test_raw_reader(reader, test_preloading=True, **kwargs):
assert_equal(concat_raw.n_times, 2 * raw.n_times)
assert_equal(concat_raw.first_samp, first_samp)
assert_equal(concat_raw.last_samp - last_samp + first_samp, last_samp + 1)
idx = np.where(concat_raw.annotations.description == 'BAD boundary')[0]
assert_array_almost_equal([(last_samp - first_samp) / raw.info['sfreq']],
concat_raw.annotations.onset[idx], decimal=2)
return raw


Expand Down
6 changes: 5 additions & 1 deletion mne/tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def test_annotations():
raw2.annotations = annot
assert_array_equal(raw2.annotations.onset, onset)
concatenate_raws([raw, raw2])
raw.annotations.delete(-1) # remove boundary annotation
assert_array_almost_equal(onset + 20., raw.annotations.onset, decimal=2)
assert_array_equal(annot.duration, raw.annotations.duration)
assert_array_equal(raw.annotations.description, np.repeat('test', 10))
Expand All @@ -65,6 +66,9 @@ def test_annotations():
raw.annotations = Annotations([1.], [.5], 'x', None)
raws.append(raw)
raw = concatenate_raws(raws)
boundary_idx = np.where(raw.annotations.description == 'BAD boundary')[0]
assert_equal(len(boundary_idx), 3)
raw.annotations.delete(boundary_idx)
assert_array_equal(raw.annotations.onset, [1., 2., 11., 12., 21., 22.,
31.])
raw.annotations.delete(2)
Expand All @@ -82,7 +86,7 @@ def test_annotations():
raw.annotations = Annotations([45.], [3], 'test', raw.info['meas_date'])
raw2.annotations = Annotations([2.], [3], 'BAD', None)
raw = concatenate_raws([raw, raw2])

raw.annotations.delete(-1) # remove boundary annotation
assert_array_almost_equal(raw.annotations.onset, [45., 2. + last_time],
decimal=2)

Expand Down

0 comments on commit 7d2ae7f

Please sign in to comment.