Repository: mne-tools/mne-python

Issue:
Error in _check_compensation_grade for ICA on EEG data after switching from v15 to v16
Dear all,
a script that ran under Version 15 now throws an error for every subject I tried. I am computing ICAs on epoched MEG and EEG data separately. The error occurs only for the EEG data. 

The error: 
```
File "/home/sh252228/anaconda2/lib/python2.7/site-packages/mne/utils.py", line 2213, in _check_compensation_grade
    name2, inst2.compensation_grade))
*** TypeError: %d format: a number is required, not NoneType
```

I realized that inst.compensation_grade is empty, while inst2.compensation_grade = 0


A snippet of my code:

```
ica_parameters = dict(n_components = .9999, method = 'fastica',  decim = 3, random_state = 23)
nip='az180202'

 # load data, hp filtered for ICA
 filename = root_directory + '/filtered/' + nip + '/' + nip + '_' + 'filtered4ICA_tsss_epo.fif'
 ICAData =  mne.read_epochs(filename, preload=True)
 
 ###################### run ICA ########################         
 picks_meg = mne.pick_types(ICAData.info, meg=True, eeg=False)
 picks_eeg = mne.pick_types(ICAData.info, meg=False, eeg=True)
     
  all_picks = dict({'meg':picks_meg,'eeg':picks_eeg})
     
  # Compute ICA, separately for EEG and MEG data 
  for ch_type in ['eeg']: # 'meg',
        print(ch_type)
        picks = all_picks[ch_type]
        
        # Apply ICA
        ica = ICA(n_components=ica_parameters['n_components'], 
                 method=ica_parameters['method'], random_state=ica_parameters['random_state'])
        print(ica)
            
        # Fit it to the data 
        ica.fit(ICAData, picks=picks, decim=ica_parameters['decim'], 
                reject=reject[ch_type])
```

I uploaded the data here: https://somaxluan.hd.free.fr/owncloud/index.php/s/3Ccgbp4g18Gf1Q4

Thank you!
Sophie 


Discussion:
@SophieHerbst is it with current master or version 0.16.1 ?
0.16.1, yes
python version is 2.7.14
@SophieHerbst what is the `reject` object you are passing as `reject=reject[ch_type]`?
But yes, I can reproduce this error. No idea what the compensation grade is, but it seems to be something MEG-specific, so it should not be checked for EEG data...
reject=dict(meg = dict(mag=10e-12, grad=500e-12), 
                eeg = dict(eeg=3000e-6))

Sorry, forgot that. 
@cbrnr can you replicate with current master?

@larsoner or @bloyl can you have a look at you worked most recently on this code?
Yep, I can replicate with the current master.

Resolving patch:
diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py
--- a/mne/preprocessing/ica.py
+++ b/mne/preprocessing/ica.py
@@ -657,7 +657,6 @@ def _transform_raw(self, raw, start, stop, reject_by_annotation=False):
             data = raw[picks, start:stop][0]
 
         # remove comp matrices
-        assert(raw.compensation_grade == self.compensation_grade)
         info = raw.info.copy()
         if info['comps']:
             info['comps'] = []
@@ -680,7 +679,6 @@ def _transform_epochs(self, epochs, concatenate):
                                'ica.ch_names' % (len(self.ch_names),
                                                  len(picks)))
         # remove comp matrices
-        assert(epochs.compensation_grade == self.compensation_grade)
         info = epochs.info.copy()
         if info['comps']:
             info['comps'] = []
@@ -711,7 +709,6 @@ def _transform_evoked(self, evoked):
                                                  len(picks)))
 
         # remove comp matrices
-        assert(evoked.compensation_grade == self.compensation_grade)
         info = evoked.info.copy()
         if info['comps']:
             info['comps'] = []
@@ -762,13 +759,16 @@ def get_sources(self, inst, add_channels=None, start=None, stop=None):
             The ICA sources time series.
         """
         if isinstance(inst, BaseRaw):
-            _check_compensation_grade(self, inst, 'ICA', 'Raw')
+            _check_compensation_grade(self, inst, 'ICA', 'Raw',
+                                      ch_names=self.ch_names)
             sources = self._sources_as_raw(inst, add_channels, start, stop)
         elif isinstance(inst, BaseEpochs):
-            _check_compensation_grade(self, inst, 'ICA', 'Epochs')
+            _check_compensation_grade(self, inst, 'ICA', 'Epochs',
+                                      ch_names=self.ch_names)
             sources = self._sources_as_epochs(inst, add_channels, False)
         elif isinstance(inst, Evoked):
-            _check_compensation_grade(self, inst, 'ICA', 'Evoked')
+            _check_compensation_grade(self, inst, 'ICA', 'Evoked',
+                                      ch_names=self.ch_names)
             sources = self._sources_as_evoked(inst, add_channels)
         else:
             raise ValueError('Data input must be of Raw, Epochs or Evoked '
@@ -925,14 +925,17 @@ def score_sources(self, inst, target=None, score_func='pearsonr',
             scores for each source as returned from score_func
         """
         if isinstance(inst, BaseRaw):
-            _check_compensation_grade(self, inst, 'ICA', 'Raw')
+            _check_compensation_grade(self, inst, 'ICA', 'Raw',
+                                      ch_names=self.ch_names)
             sources = self._transform_raw(inst, start, stop,
                                           reject_by_annotation)
         elif isinstance(inst, BaseEpochs):
-            _check_compensation_grade(self, inst, 'ICA', 'Epochs')
+            _check_compensation_grade(self, inst, 'ICA', 'Epochs',
+                                      ch_names=self.ch_names)
             sources = self._transform_epochs(inst, concatenate=True)
         elif isinstance(inst, Evoked):
-            _check_compensation_grade(self, inst, 'ICA', 'Evoked')
+            _check_compensation_grade(self, inst, 'ICA', 'Evoked',
+                                      ch_names=self.ch_names)
             sources = self._transform_evoked(inst)
         else:
             raise ValueError('Data input must be of Raw, Epochs or Evoked '
@@ -1240,18 +1243,21 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
             The processed data.
         """
         if isinstance(inst, BaseRaw):
-            _check_compensation_grade(self, inst, 'ICA', 'Raw')
+            _check_compensation_grade(self, inst, 'ICA', 'Raw',
+                                      ch_names=self.ch_names)
             out = self._apply_raw(raw=inst, include=include,
                                   exclude=exclude,
                                   n_pca_components=n_pca_components,
                                   start=start, stop=stop)
         elif isinstance(inst, BaseEpochs):
-            _check_compensation_grade(self, inst, 'ICA', 'Epochs')
+            _check_compensation_grade(self, inst, 'ICA', 'Epochs',
+                                      ch_names=self.ch_names)
             out = self._apply_epochs(epochs=inst, include=include,
                                      exclude=exclude,
                                      n_pca_components=n_pca_components)
         elif isinstance(inst, Evoked):
-            _check_compensation_grade(self, inst, 'ICA', 'Evoked')
+            _check_compensation_grade(self, inst, 'ICA', 'Evoked',
+                                      ch_names=self.ch_names)
             out = self._apply_evoked(evoked=inst, include=include,
                                      exclude=exclude,
                                      n_pca_components=n_pca_components)
diff --git a/mne/utils.py b/mne/utils.py
--- a/mne/utils.py
+++ b/mne/utils.py
@@ -2193,13 +2193,35 @@ def _check_preload(inst, msg):
                 '%s.load_data().' % (name, name))
 
 
-def _check_compensation_grade(inst, inst2, name, name2):
-    """Ensure that objects have same compenstation_grade."""
-    if (None not in [inst.info, inst2.info]) and (inst.compensation_grade !=
-                                                  inst2.compensation_grade):
-            msg = ('Compensation grade of %s (%d) and %s (%d) don\'t match')
-            raise RuntimeError(msg % (name, inst.compensation_grade,
-                                      name2, inst2.compensation_grade))
+def _check_compensation_grade(inst, inst2, name, name2, ch_names=None):
+    """Ensure that objects have same compensation_grade."""
+    from .io.pick import pick_channels, pick_info
+    from .io.compensator import get_current_comp
+
+    if None in [inst.info, inst2.info]:
+        return
+
+    if ch_names is None:
+        grade = inst.compensation_grade
+        grade2 = inst2.compensation_grade
+    else:
+        info = inst.info.copy()
+        info2 = inst2.info.copy()
+        # pick channels
+        for t_info in [info, info2]:
+            if t_info['comps']:
+                t_info['comps'] = []
+            picks = pick_channels(t_info['ch_names'], ch_names)
+            pick_info(t_info, picks, copy=False)
+        # get compensation grades
+        grade = get_current_comp(info)
+        grade2 = get_current_comp(info2)
+
+    # perform check
+    if grade != grade2:
+        msg = 'Compensation grade of %s (%d) and %s (%d) don\'t match'
+        raise RuntimeError(msg % (name, inst.compensation_grade,
+                                  name2, inst2.compensation_grade))
 
 
 def _check_pandas_installed(strict=True):


Test patch:
diff --git a/mne/preprocessing/tests/test_ica.py b/mne/preprocessing/tests/test_ica.py
--- a/mne/preprocessing/tests/test_ica.py
+++ b/mne/preprocessing/tests/test_ica.py
@@ -26,7 +26,7 @@
                                read_ica, run_ica)
 from mne.preprocessing.ica import (get_score_funcs, corrmap, _sort_components,
                                    _ica_explained_variance)
-from mne.io import read_raw_fif, Info, RawArray, read_raw_ctf
+from mne.io import read_raw_fif, Info, RawArray, read_raw_ctf, read_raw_eeglab
 from mne.io.meas_info import _kind_dict
 from mne.io.pick import _DATA_CH_TYPES_SPLIT
 from mne.tests.common import assert_naming
@@ -46,8 +46,15 @@
 event_name = op.join(data_dir, 'test-eve.fif')
 test_cov_name = op.join(data_dir, 'test-cov.fif')
 
-ctf_fname = op.join(testing.data_path(download=False), 'CTF',
-                    'testdata_ctf.ds')
+test_base_dir = testing.data_path(download=False)
+ctf_fname = op.join(test_base_dir, 'CTF', 'testdata_ctf.ds')
+
+fif_fname = op.join(test_base_dir, 'MEG', 'sample',
+                    'sample_audvis_trunc_raw.fif')
+eeglab_fname = op.join(test_base_dir, 'EEGLAB', 'test_raw.set')
+eeglab_montage = op.join(test_base_dir, 'EEGLAB', 'test_chans.locs')
+
+ctf_fname2 = op.join(test_base_dir, 'CTF', 'catch-alp-good-f.ds')
 
 event_id, tmin, tmax = 1, -0.2, 0.2
 # if stop is too small pca may fail in some cases, but we're okay on this file
@@ -889,7 +896,7 @@ def test_ica_ctf():
             ica = ICA(n_components=2, random_state=0, max_iter=2,
                       method=method)
             with warnings.catch_warnings(record=True):  # convergence
-                ica.fit(raw)
+                ica.fit(inst)
 
         # test apply and get_sources
         for inst in [raw, epochs, evoked]:
@@ -911,4 +918,66 @@ def test_ica_ctf():
             ica.get_sources(inst)
 
 
+@requires_sklearn
+@testing.requires_testing_data
+def test_ica_eeg():
+    method = 'fastica'
+    raw_fif = read_raw_fif(fif_fname, preload=True)
+    with warnings.catch_warnings(record=True):  # events etc.
+        raw_eeglab = read_raw_eeglab(input_fname=eeglab_fname,
+                                     montage=eeglab_montage, preload=True)
+    for raw in [raw_fif, raw_eeglab]:
+        events = make_fixed_length_events(raw, 99999, start=0, stop=0.3,
+                                          duration=0.1)
+        picks_meg = pick_types(raw.info, meg=True, eeg=False)[:2]
+        picks_eeg = pick_types(raw.info, meg=False, eeg=True)[:2]
+        picks_all = []
+        picks_all.extend(picks_meg)
+        picks_all.extend(picks_eeg)
+        epochs = Epochs(raw, events, None, -0.1, 0.1, preload=True)
+        evoked = epochs.average()
+
+        for picks in [picks_meg, picks_eeg, picks_all]:
+            if len(picks) == 0:
+                continue
+            # test fit
+            for inst in [raw, epochs]:
+                ica = ICA(n_components=2, random_state=0, max_iter=2,
+                          method=method)
+                with warnings.catch_warnings(record=True):  # convergence
+                    ica.fit(inst, picks=picks)
+
+            # test apply and get_sources
+            for inst in [raw, epochs, evoked]:
+                ica.apply(inst)
+                ica.get_sources(inst)
+
+    with warnings.catch_warnings(record=True):  # misc channel
+        raw = read_raw_ctf(ctf_fname2,  preload=True)
+    events = make_fixed_length_events(raw, 99999, start=0, stop=0.2,
+                                      duration=0.1)
+    picks_meg = pick_types(raw.info, meg=True, eeg=False)[:2]
+    picks_eeg = pick_types(raw.info, meg=False, eeg=True)[:2]
+    picks_all = picks_meg + picks_eeg
+    for comp in [0, 1]:
+        raw.apply_gradient_compensation(comp)
+        epochs = Epochs(raw, events, None, -0.1, 0.1, preload=True)
+        evoked = epochs.average()
+
+        for picks in [picks_meg, picks_eeg, picks_all]:
+            if len(picks) == 0:
+                continue
+            # test fit
+            for inst in [raw, epochs]:
+                ica = ICA(n_components=2, random_state=0, max_iter=2,
+                          method=method)
+                with warnings.catch_warnings(record=True):  # convergence
+                    ica.fit(inst)
+
+            # test apply and get_sources
+            for inst in [raw, epochs, evoked]:
+                ica.apply(inst)
+                ica.get_sources(inst)
+
+
 run_tests_if_main()