Repository: mne-tools/mne-python

Issue:
dealing with eeglab float event latencies
Eeglab represents event latencies with floats after downsampling (to suggest that events are no longer precisely co-occuring with specific time samples). Instead of turning these to int representation, which truncates the value, we should be rounding.


Discussion:

Resolving patch:
diff --git a/mne/io/eeglab/eeglab.py b/mne/io/eeglab/eeglab.py
--- a/mne/io/eeglab/eeglab.py
+++ b/mne/io/eeglab/eeglab.py
@@ -675,10 +675,16 @@ def read_events_eeglab(eeg, event_id=None, event_id_func='strip_to_integer',
         logger.info('No events found, returning empty stim channel ...')
         return np.zeros((0, 3))
 
-    if (latencies < 0).any():
+    if (latencies < -1).any():
         raise ValueError('At least one event sample index is negative. Please'
                          ' check if EEG.event.sample values are correct.')
-
+    if (latencies == -1).any():
+        warn("At least one event has a sample index of -1. This usually is "
+             "a consequence of how eeglab handles event latency after "
+             "resampling - especially when you had a boundary event at the "
+             "beginning of the file. Please make sure that the events at "
+             "the very beginning of your EEGLAB file can be safely dropped "
+             "(e.g., because they are boundary events).")
     not_in_event_id = set(x for x in types if x not in event_id)
     not_purely_numeric = set(x for x in not_in_event_id if not x.isdigit())
     no_numbers = set([x for x in not_purely_numeric
@@ -703,7 +709,7 @@ def read_events_eeglab(eeg, event_id=None, event_id_func='strip_to_integer',
     for tt, latency in zip(types, latencies):
         try:  # look up the event in event_id and if not, try event_id_func
             event_code = event_id[tt] if tt in event_id else event_id_func(tt)
-            events.append([int(latency), 1, event_code])
+            events.append([int(round(latency)), 1, event_code])
         except (ValueError, TypeError):  # if event_id_func fails
             pass  # We're already raising warnings above, so we just drop
 


Test patch:
diff --git a/mne/io/eeglab/tests/test_eeglab.py b/mne/io/eeglab/tests/test_eeglab.py
--- a/mne/io/eeglab/tests/test_eeglab.py
+++ b/mne/io/eeglab/tests/test_eeglab.py
@@ -108,6 +108,7 @@ def test_io_set_raw(fnames, tmpdir):
         event.type = 1
     assert_equal(read_events_eeglab(eeg)[-1, -1], 1)
     eeg.event = eeg.event[0]  # single event
+    eeg.event.latency = float(eeg.event.latency) - .1  # test rounding
     assert_equal(read_events_eeglab(eeg)[-1, -1], 1)
 
     # test reading file with one event (read old version)
@@ -141,8 +142,9 @@ def test_io_set_raw(fnames, tmpdir):
     shutil.copyfile(op.join(base_dir, 'test_raw.fdt'),
                     negative_latency_fname.replace('.set', '.fdt'))
     event_id = {eeg.event[0].type: 1}
-    pytest.raises(ValueError, read_raw_eeglab, montage=montage, preload=True,
-                  event_id=event_id, input_fname=negative_latency_fname)
+    with pytest.warns(RuntimeWarning, match="has a sample index of -1."):
+        read_raw_eeglab(input_fname=negative_latency_fname, preload=True,
+                        event_id=event_id, montage=montage)
 
     # test overlapping events
     overlap_fname = op.join(tmpdir, 'test_overlap_event.set')