Repository: mne-tools/mne-python

Issue:
ica.plot_sources is problematic if inst.info['bads'] != []
When trying to plot ica sources, I run into an error:

``` python
RuntimeError: Epochs don't match fitted data: 207 channels fitted but 208 channels supplied. 
Please provide Epochs compatible with ica.ch_names
```

but that's because my `epochs.info['bads'] = ['MEG 130']`. this channel is dropped properly when computing the ICA but it's not being handled properly with the underlying `_transform_epochs`. The function should take a `picks` and if `picks=None`, remove the bads.

Thoughts @dengemann?



Discussion:
likely dup of #2140

yes, this seesm to be a regression. I'm pretty sure this part of the code has been working.

this is a bit different, it's not working for me. here's an example with the sample data:

``` python
import os.path as op
import mne


raw_file = op.join(mne.datasets.sample.data_path(), 'MEG', 'sample',
                   'sample_audvis_raw.fif')
evt_file = op.join(mne.datasets.sample.data_path(), 'MEG', 'sample',
                   'sample_audvis_raw-eve.fif')
raw = mne.io.read_raw_fif(raw_file, preload=True)
evts = mne.read_events(evt_file)
epochs = mne.Epochs(raw, evts, 1, tmin=-.2, tmax=.6, preload=True, verbose=False)
ica = mne.preprocessing.ICA(.9, random_state=42, method='infomax')
ica.fit(epochs, decim=2)
ica.plot_sources(epochs)  # This line was just corrected to fix an error (TB)

RuntimeError: Epochs don't match fitted data: 364 channels fitted but 365 channels supplied. 
Please provide Epochs compatible with ica.ch_names
```


Resolving patch:
diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py
--- a/mne/preprocessing/ica.py
+++ b/mne/preprocessing/ica.py
@@ -531,11 +531,12 @@ def _transform_raw(self, raw, start, stop):
             raise RuntimeError('No fit available. Please fit ICA.')
         start, stop = _check_start_stop(raw, start, stop)
 
-        picks = [raw.ch_names.index(k) for k in self.ch_names]
+        picks = pick_types(raw.info, include=self.ch_names, exclude='bads',
+                           meg=False, ref_meg=False)
         if len(picks) != len(self.ch_names):
-            raise RuntimeError('Epochs don\'t match fitted data: %i channels '
+            raise RuntimeError('Raw doesn\'t match fitted data: %i channels '
                                'fitted but %i channels supplied. \nPlease '
-                               'provide Epochs compatible with '
+                               'provide Raw compatible with '
                                'ica.ch_names' % (len(self.ch_names),
                                                  len(picks)))
 
@@ -548,9 +549,8 @@ def _transform_epochs(self, epochs, concatenate):
         if not hasattr(self, 'mixing_matrix_'):
             raise RuntimeError('No fit available. Please fit ICA')
 
-        picks = pick_types(epochs.info, include=self.ch_names, exclude=[],
-                           ref_meg=False)
-
+        picks = pick_types(epochs.info, include=self.ch_names, exclude='bads',
+                           meg=False, ref_meg=False)
         # special case where epochs come picked but fit was 'unpicked'.
         if len(picks) != len(self.ch_names):
             raise RuntimeError('Epochs don\'t match fitted data: %i channels '
@@ -575,13 +575,13 @@ def _transform_evoked(self, evoked):
         if not hasattr(self, 'mixing_matrix_'):
             raise RuntimeError('No fit available. Please first fit ICA')
 
-        picks = pick_types(evoked.info, include=self.ch_names, exclude=[],
-                           ref_meg=False)
+        picks = pick_types(evoked.info, include=self.ch_names, exclude='bads',
+                           meg=False, ref_meg=False)
 
         if len(picks) != len(self.ch_names):
-            raise RuntimeError('Epochs don\'t match fitted data: %i channels '
-                               'fitted but %i channels supplied. \nPlease '
-                               'provide Epochs compatible with '
+            raise RuntimeError('Evoked doesn\'t match fitted data: %i channels'
+                               ' fitted but %i channels supplied. \nPlease '
+                               'provide Evoked compatible with '
                                'ica.ch_names' % (len(self.ch_names),
                                                  len(picks)))
 
diff --git a/mne/viz/ica.py b/mne/viz/ica.py
--- a/mne/viz/ica.py
+++ b/mne/viz/ica.py
@@ -55,7 +55,7 @@ def plot_ica_sources(ica, inst, picks=None, exclude=None, start=None,
         The ICA solution.
     inst : instance of mne.io.Raw, mne.Epochs, mne.Evoked
         The object to plot the sources from.
-    picks : ndarray | None.
+    picks : int | array_like of int | None.
         The components to be displayed. If None, plot will show the
         sources in the order as fitted.
     exclude : array_like of int
@@ -105,6 +105,7 @@ def plot_ica_sources(ica, inst, picks=None, exclude=None, start=None,
         if start is not None or stop is not None:
             inst = inst.crop(start, stop, copy=True)
         fig = _plot_ica_sources_evoked(evoked=sources,
+                                       picks=picks,
                                        exclude=exclude,
                                        title=title, show=show)
     else:
@@ -184,15 +185,19 @@ def _plot_ica_grid(sources, start, stop,
     return fig
 
 
-def _plot_ica_sources_evoked(evoked, exclude, title, show):
+def _plot_ica_sources_evoked(evoked, picks, exclude, title, show):
     """Plot average over epochs in ICA space
 
     Parameters
     ----------
-    ica : instance of mne.prerocessing.ICA
-        The ICA object.
-    epochs : instance of mne.Epochs
-        The Epochs to be regarded.
+    evoked : instance of mne.Evoked
+        The Evoked to be used.
+    picks : int | array_like of int | None.
+        The components to be displayed. If None, plot will show the
+        sources in the order as fitted.
+    exclude : array_like of int
+        The components marked for exclusion. If None (default), ICA.exclude
+        will be used.
     title : str
         The figure title.
     show : bool
@@ -206,11 +211,14 @@ def _plot_ica_sources_evoked(evoked, exclude, title, show):
     times = evoked.times * 1e3
 
     # plot unclassified sources
-    plt.plot(times, evoked.data.T, 'k')
+    if picks is None:
+        picks = np.arange(evoked.data.shape[0])
+    plt.plot(times, evoked.data[picks].T, 'k')
     for ii in exclude:
-        # use indexing to expose event related sources
-        plt.plot(times, evoked.data[ii].T, color='r', label='ICA %02d' % ii)
-
+        if ii in picks:
+            # use indexing to expose event related sources
+            plt.plot(times, evoked.data[ii].T, color='r',
+                     label='ICA %03d' % (ii + 1))
     plt.title(title)
     plt.xlim(times[[0, -1]])
     plt.xlabel('Time (ms)')


Test patch:
diff --git a/mne/viz/tests/test_ica.py b/mne/viz/tests/test_ica.py
--- a/mne/viz/tests/test_ica.py
+++ b/mne/viz/tests/test_ica.py
@@ -73,6 +73,7 @@ def test_plot_ica_sources():
     """
     import matplotlib.pyplot as plt
     raw = io.Raw(raw_fname, preload=True)
+    raw.crop(0, 1, copy=False)
     picks = _get_picks(raw)
     epochs = _get_epochs()
     raw.pick_channels([raw.ch_names[k] for k in picks])
@@ -80,8 +81,12 @@ def test_plot_ica_sources():
                            ecg=False, eog=False, exclude='bads')
     ica = ICA(n_components=2, max_pca_components=3, n_pca_components=3)
     ica.fit(raw, picks=ica_picks)
-    ica.plot_sources(raw)
+    raw.info['bads'] = ['MEG 0113']
+    assert_raises(RuntimeError, ica.plot_sources, inst=raw)
     ica.plot_sources(epochs)
+    epochs.info['bads'] = ['MEG 0113']
+    assert_raises(RuntimeError, ica.plot_sources, inst=epochs)
+    epochs.info['bads'] = []
     with warnings.catch_warnings(record=True):  # no labeled objects mpl
         ica.plot_sources(epochs.average())
     assert_raises(ValueError, ica.plot_sources, 'meeow')