Repository: mne-tools/mne-python

Issue:
GDF events (again!)
#### Describe the bug
mne-python (0.18.1)
The new _get_annotations_gdf function collapses all the events not found in the global GDF_EVENTS_LUT to the description 'Unknown'. This is done at read_raw_gdf, so all the options in events_from_annotations are useless. 
There is thus no way to recover and distinguish among those 'Unknown' events.

#### Steps and/or code to reproduce
import mne
signal = mne.io.read_raw_gdf('data.gdf',preload=True)
events = mne.events_from_annotations(signal)
print(events)

Of course data.gdf must contain events that are not in GDF_EVENTS_LUT (or some events in GDF_EVENTS_LUT can be removed).

Several tools (e.g. OpenViBE) can produce tags which are not in the list of predefined events depicted in the gdf standard. Collapsing all of those to 'Unknown' is bad because the information lost is unrecoverable.

For my file, the above code gives:
(array([[  5538,      0,      1],
       [  5844,      0,      1],
       [  6176,      0,      1],
       ...,
       [164368,      0,      1],
       [164609,      0,      1],
       [164768,      0,      1]]), {'Unknown': 1, 'condition 11': 2, 'condition 12': 3, 'condition 14': 4, 'condition 17': 5, 'condition 2': 6, 'condition 20': 7, 'condition 23': 8, 'condition 3': 9, 'condition 48': 10})

With an old version of mne-python (0.13.1) and using find_edf_events, we get:
>>> events
[3049, array([  5538,   5844,   6176, ..., 164368, 164609, 164768], dtype=uint32), array([181, 193, 190, ..., 199, 190, 182], dtype=uint16), array([0, 0, 0, ..., 0, 0, 0], dtype=int32), array([1, 1, 1, ..., 1, 1, 1], dtype=uint32)]
>>> np.unique(events[2])
array([  0,   2,   3,  11,  12,  14,  17,  20,  23,  27,  36,  37,  39,
        40,  42,  44,  45,  46,  48, 181, 182, 183, 184, 187, 190, 193,
       195, 198, 199], dtype=uint16)

Events 2,   3,  11,  12,  14,  17,  20,  23, 48 are kept but all the others are collapses under 'Unknown': 1

#### Expected results
Identity of initial events should be recoverable.... Actually, I must say that I do not understand why events are re-numbered by default through the sorting of descriptions at gdf read time... This seems unnecessary (events are already numbered) and unreliable (imagine the case of a name change in gdf, or the addition of an event between two similar experiments, besides the currently described case of non standardized user defined events).
I would expect read_raw_gdf to do minimal changes to this event table and the renaming to be done at events_from_annotations where the user has some leverage of what happens using the event_id parameter.

On the same trend, _get_annotations_gdf converts gdf time stamps to seconds which are then converted back to integers in events_from_annotations. This will probably induces some interesting rounding effects... Not a big deal, but not totally reliable/reproducible....

#### Actual results
See above


#### Additional information
>>> mne.sys_info()
/usr/lib64/python3.7/site-packages/sklearn/externals/six.py:7: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
/usr/lib64/python3.7/site-packages/sklearn/utils/__init__.py:4: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sequence
Platform:      Linux-5.1.17-300.fc30.x86_64-x86_64-with-fedora-30-Thirty
Python:        3.7.3 (default, May 11 2019, 00:38:04)  [GCC 9.1.1 20190503 (Red Hat 9.1.1-1)]
Executable:    /usr/bin/python3
CPU:           x86_64: 12 cores
Memory:        31.1 GB

mne:           0.18.1
numpy:         1.16.4 {blas=openblasp, lapack=openblasp}
scipy:         1.2.0
matplotlib:    3.0.3 {backend=Qt5Agg}

sklearn:       0.19.1
nibabel:       2.4.1
mayavi:        Not found
cupy:          Not found
pandas:        0.23.4
dipy:          Not found



Discussion:

Resolving patch:
diff --git a/mne/io/edf/edf.py b/mne/io/edf/edf.py
--- a/mne/io/edf/edf.py
+++ b/mne/io/edf/edf.py
@@ -1437,8 +1437,6 @@ def _get_annotations_gdf(edf_info, sfreq):
     if events is not None and events[1].shape[0] > 0:
         onset = events[1] / sfreq
         duration = events[4] / sfreq
-        desc = [GDF_EVENTS_LUT[key]
-                if key in GDF_EVENTS_LUT else 'Unknown'
+        desc = [GDF_EVENTS_LUT.get(key, 'Undefined(%s)' % (key,))
                 for key in events[2]]
-
     return onset, duration, desc


Test patch:
diff --git a/mne/io/edf/tests/test_gdf.py b/mne/io/edf/tests/test_gdf.py
--- a/mne/io/edf/tests/test_gdf.py
+++ b/mne/io/edf/tests/test_gdf.py
@@ -32,9 +32,14 @@ def test_gdf_data():
 
     # Test Status is added as event
     EXPECTED_EVS_ONSETS = raw._raw_extras[0]['events'][1]
+    EXPECTED_EVS_ID = {
+        'Undefined({})'.format(evs): i for i, evs in enumerate(
+            [32769, 32770, 33024, 33025, 33026, 33027, 33028, 33029, 33040,
+             33041, 33042, 33043, 33044, 33045, 33285, 33286], 1)
+    }
     evs, evs_id = events_from_annotations(raw)
     assert_array_equal(evs[:, 0], EXPECTED_EVS_ONSETS)
-    assert evs_id == {'Unknown': 1}
+    assert evs_id == EXPECTED_EVS_ID
 
     # this .npy was generated using the official biosig python package
     raw_biosig = np.load(gdf1_path + '_biosig.npy')