Repository: mne-tools/mne-python

Issue:
API/BUG: remove support for event_id if event not present on construction time
currently you can do sth. like 

``` Python

event_id  = dict(aud_l=1, aud_r=2, this_does_not_exist=123456789)

epochs = mne.Epochs(raw, events, event_id, ...)

# and you willl hate it if you're used to say

for key in epochs.event_id:
    evoked = epochs[key].average()
    print evoked.nave

```

output:

```
72
72
0
```

this means you can compute an average even if no epochs are present ...

Is there any reason not to pop / remove keys if events passed are not found?

e.g. realtime specific reasons @mainakjas @mluessi 

cc @agramfort 



Discussion:
I would raise an Exception if 123456789 is not even in events[:, 2]

and a warning if nave == 0 due certainly to rejection values too low.


Resolving patch:
diff --git a/mne/epochs.py b/mne/epochs.py
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -435,6 +435,9 @@ def _compute_mean_or_stderr(self, picks, mode='ave'):
         # otherwise the apply_proj will be confused
         evoked.proj = True if self.proj is True else None
         evoked.verbose = self.verbose
+        if evoked.nave < 1:
+            warnings.warn('evoked object is empty (based on less '
+                          'than 1 epoch)', RuntimeWarning)
 
         return evoked
 
@@ -669,6 +672,10 @@ def __init__(self, raw, events, event_id, tmin, tmax, baseline=(None, 0),
         self._projector, self.info = setup_proj(self.info, add_eeg_ref,
                                                 activate=activate)
 
+        for key, val in self.event_id.items():
+            if val not in events[:, 2]:
+                raise ValueError('No matching events found for %s '
+                                 '(event id %i)' % (key, val))
         # Select the desired events
         values = list(self.event_id.values())
         selected = in1d(events[:, 2], values)


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
@@ -859,7 +859,7 @@ def test_drop_epochs():
                        np.where(events[:, 2] == event_id)[0])
     assert_equal(len(epochs.drop_log), len(events))
     assert_true(all(epochs.drop_log[k] == ['IGNORED']
-                 for k in set(range(len(events))) - set(epochs.selection)))
+                for k in set(range(len(events))) - set(epochs.selection)))
 
     selection = epochs.selection.copy()
     epochs.drop_epochs([2, 4], reason='d')
@@ -905,7 +905,7 @@ def test_contains():
     tests = [(('mag', False), ('grad', 'eeg')),
              (('grad', False), ('mag', 'eeg')),
              ((False, True), ('grad', 'mag'))]
-    
+
     for (meg, eeg), others in tests:
         picks_contains = fiff.pick_types(raw.info, meg=meg, eeg=eeg)
         epochs = Epochs(raw, events, {'a': 1, 'b': 2}, tmin, tmax,
@@ -914,7 +914,7 @@ def test_contains():
         test = 'eeg' if eeg is True else meg
         assert_true(test in epochs)
         assert_true(not any(o in epochs for o in others))
-    
+
     assert_raises(ValueError, epochs.__contains__, 'foo')
     assert_raises(ValueError, epochs.__contains__, 1)
 
@@ -944,3 +944,11 @@ def test_equalize_channels():
     equalize_channels(my_comparison)
     for e in my_comparison:
         assert_equal(ch_names, e.ch_names)
+
+
+def test_illegal_event_id():
+    """Test handling of invalid events ids"""
+    event_id_illegal = dict(aud_l=1, does_not_exist=12345678)
+
+    assert_raises(ValueError, Epochs, raw, events, event_id_illegal, tmin,
+                  tmax, picks=picks, baseline=(None, 0), proj=False)