Repository: mne-tools/mne-python

Issue:
EpochsArray constructor is broken if len(times) == 1
This is useful for EEG/MEG measures which reduce the time dimension in each trial. It used to work with the last release. Tried to find a fix but it seems to be deeper than I assumed.

cc @Eric89GXL @agramfort 



Discussion:
we cuttently have this one in epochs.py:

https://github.com/mne-tools/mne-python/blob/master/mne/epochs.py#L277

It won't play nicely if shape[-1] == 1

as round((tmax - tmin) can be 0

cc @fraimondo 

sorry parsed the code incorrectly, so if only 1 time sample exists, the conditional breaks. 

can you share a gist that reproduces the bug?

provide a tiny test and we'll make it pass

> provide a tiny test and we'll make it pass

How about this PR instead ? #2435 ;)


Resolving patch:
diff --git a/mne/epochs.py b/mne/epochs.py
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -245,8 +245,8 @@ def __init__(self, info, data, events, event_id, tmin, tmax,
                     err = ("Baseline interval (tmax = %s) is outside of epoch "
                            "data (tmax = %s)" % (baseline_tmax, tmax))
                     raise ValueError(err)
-        if tmin >= tmax:
-            raise ValueError('tmin has to be smaller than tmax')
+        if tmin > tmax:
+            raise ValueError('tmin has to be less than or equal to tmax')
 
         self.tmin = tmin
         self.tmax = tmax
@@ -1834,7 +1834,7 @@ def __init__(self, data, info, events, tmin=0, event_id=None,
                                           flat=flat, reject_tmin=reject_tmin,
                                           reject_tmax=reject_tmax, decim=1)
         if len(events) != in1d(self.events[:, 2],
-                               list(event_id.values())).sum():
+                               list(self.event_id.values())).sum():
             raise ValueError('The events must only contain event numbers from '
                              'event_id')
         for ii, e in enumerate(self._data):


Test patch:
diff --git a/mne/tests/test_epochs.py b/mne/tests/test_epochs.py
--- a/mne/tests/test_epochs.py
+++ b/mne/tests/test_epochs.py
@@ -504,6 +504,16 @@ def test_read_write_epochs():
     assert_array_equal(epochs.selection, epochs_read.selection)
     assert_equal(epochs.drop_log, epochs_read.drop_log)
 
+    # Test that having a single time point works
+    epochs.preload_data()
+    epochs.crop(0, 0, copy=False)
+    assert_equal(len(epochs.times), 1)
+    assert_equal(epochs.get_data().shape[-1], 1)
+    epochs.save(temp_fname)
+    epochs_read = read_epochs(temp_fname)
+    assert_equal(len(epochs_read.times), 1)
+    assert_equal(epochs.get_data().shape[-1], 1)
+
 
 def test_epochs_proj():
     """Test handling projection (apply proj in Raw or in Epochs)
@@ -1589,6 +1599,23 @@ def test_array_epochs():
     ep_data = epochs.get_data()
     assert_array_equal(np.zeros_like(ep_data), ep_data)
 
+    # one time point
+    epochs = EpochsArray(data[:, :, :1], info, events=events,
+                         event_id=event_id, tmin=0., baseline=None)
+    assert_allclose(epochs.times, [0.])
+    assert_allclose(epochs.get_data(), data[:, :, :1])
+    epochs.save(temp_fname)
+    epochs_read = read_epochs(temp_fname)
+    assert_allclose(epochs_read.times, [0.])
+    assert_allclose(epochs_read.get_data(), data[:, :, :1])
+
+    # event as integer (#2435)
+    mask = (events[:, 2] == 1)
+    data_1 = data[mask]
+    events_1 = events[mask]
+    epochs = EpochsArray(data_1, info, events=events_1, event_id=1,
+                         tmin=-0.2, baseline=(None, 0))
+
 
 def test_concatenate_epochs():
     """Test concatenate epochs"""