Repository: mne-tools/mne-python

Issue:
MEG Interpolation
Dear MNE-community,

I am currently trying to interpolate some bad MEG channels with MNE.
Unfortunately there seems to occur an exception, such that the resulting
channels hold only zeros. Even when I try to interpolate only one
channel I get from '_field_interpolation.py:80' the log
'Truncate at 0 missing 0.0001'
As I understand it, this happens because the eigenvalues are all too
small. My data contains of 248 channels measured at ~291000 time steps.

I am currently stuck there, with no idea remaining. Can you give me a
hint what else I could try to get at least some interpolation for my bad
channels?

As suggested, I attached a minimal example.

[interpol.zip](https://github.com/mne-tools/mne-python/files/1136430/interpol.zip)


Thanks in advance, Christoph


Discussion:
@Eric89GXL are you on it?
This example runs with no problem on my machine. Can you try updating to latest MNE `master` to see if it fixes the problem?

If it doesn't, what is your `mne.sys_info()`?
... oh wait, the channel is all zero. I'll see if I can make sense of it.

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -825,7 +825,9 @@ def add_channels(self, add_list, force_update_info=False):
 class InterpolationMixin(object):
     """Mixin class for Raw, Evoked, Epochs."""
 
-    def interpolate_bads(self, reset_bads=True, mode='accurate'):
+    @verbose
+    def interpolate_bads(self, reset_bads=True, mode='accurate',
+                         verbose=None):
         """Interpolate bad MEG and EEG channels.
 
         Operates in place.
@@ -838,6 +840,10 @@ def interpolate_bads(self, reset_bads=True, mode='accurate'):
             Either `'accurate'` or `'fast'`, determines the quality of the
             Legendre polynomial expansion used for interpolation of MEG
             channels.
+        verbose : bool, str, int, or None
+            If not None, override default verbose level (see
+            :func:`mne.verbose` and :ref:`Logging documentation <tut_logging>`
+            for more).
 
         Returns
         -------
diff --git a/mne/channels/interpolation.py b/mne/channels/interpolation.py
--- a/mne/channels/interpolation.py
+++ b/mne/channels/interpolation.py
@@ -6,7 +6,7 @@
 from numpy.polynomial.legendre import legval
 from scipy import linalg
 
-from ..utils import logger, warn
+from ..utils import logger, warn, verbose
 from ..io.pick import pick_types, pick_channels, pick_info
 from ..surface import _normalize_vectors
 from ..bem import _fit_sphere
@@ -104,7 +104,8 @@ def _do_interp_dots(inst, interpolation, goods_idx, bads_idx):
                          .format(type(inst)))
 
 
-def _interpolate_bads_eeg(inst):
+@verbose
+def _interpolate_bads_eeg(inst, verbose=None):
     """Interpolate bad EEG channels.
 
     Operates in place.
@@ -153,6 +154,7 @@ def _interpolate_bads_eeg(inst):
     _do_interp_dots(inst, interpolation, goods_idx, bads_idx)
 
 
+@verbose
 def _interpolate_bads_meg(inst, mode='accurate', verbose=None):
     """Interpolate bad channels from data in good channels.
 
diff --git a/mne/forward/_field_interpolation.py b/mne/forward/_field_interpolation.py
--- a/mne/forward/_field_interpolation.py
+++ b/mne/forward/_field_interpolation.py
@@ -78,8 +78,10 @@ def _compute_mapping_matrix(fmd, info):
     # Eigenvalue truncation
     sumk = np.cumsum(sing)
     sumk /= sumk[-1]
-    fmd['nest'] = np.where(sumk > (1.0 - fmd['miss']))[0][0]
-    logger.info('    [Truncate at %d missing %g]' % (fmd['nest'], fmd['miss']))
+    fmd['nest'] = np.where(sumk > (1.0 - fmd['miss']))[0][0] + 1
+    logger.info('    Truncating at %d/%d components to omit less than %g '
+                '(%0.2g)' % (fmd['nest'], len(sing), fmd['miss'],
+                             1. - sumk[fmd['nest'] - 1]))
     sing = 1.0 / sing[:fmd['nest']]
 
     # Put the inverse together


Test patch:
diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py
--- a/mne/channels/tests/test_interpolation.py
+++ b/mne/channels/tests/test_interpolation.py
@@ -92,6 +92,18 @@ def test_interpolation():
         inst.info['bads'] = [inst.ch_names[1]]
         assert_raises(ValueError, inst.interpolate_bads)
 
+    # check that interpolation works with few channels
+    raw_few = raw.copy().crop(0, 0.1).load_data()
+    raw_few.pick_channels(raw_few.ch_names[:1] + raw_few.ch_names[3:4])
+    assert_equal(len(raw_few.ch_names), 2)
+    raw_few.del_proj()
+    raw_few.info['bads'] = [raw_few.ch_names[-1]]
+    orig_data = raw_few[1][0]
+    raw_few.interpolate_bads(reset_bads=False)
+    new_data = raw_few[1][0]
+    assert_true((new_data == 0).mean() < 0.5)
+    assert_true(np.corrcoef(new_data, orig_data)[0, 1] > 0.1)
+
     # check that interpolation works when non M/EEG channels are present
     # before MEG channels
     with warnings.catch_warnings(record=True):  # change of units
@@ -131,4 +143,5 @@ def test_interpolation():
     data2 = evoked.interpolate_bads().data[pick]
     assert_true(np.corrcoef(data1, data2)[0, 1] > thresh)
 
+
 run_tests_if_main()
diff --git a/mne/forward/tests/test_field_interpolation.py b/mne/forward/tests/test_field_interpolation.py
--- a/mne/forward/tests/test_field_interpolation.py
+++ b/mne/forward/tests/test_field_interpolation.py
@@ -176,17 +176,20 @@ def test_make_field_map_meeg():
     evoked.pick_channels([evoked.ch_names[p] for p in picks])
     evoked.info.normalize_proj()
     maps = make_field_map(evoked, trans_fname, subject='sample',
-                          subjects_dir=subjects_dir, n_jobs=1)
+                          subjects_dir=subjects_dir, n_jobs=1, verbose='debug')
     assert_equal(maps[0]['data'].shape, (642, 6))  # EEG->Head
     assert_equal(maps[1]['data'].shape, (304, 31))  # MEG->Helmet
     # reasonable ranges
-    for map_ in maps:
-        assert_true(0.5 < map_['data'].max() < 2)
-        assert_true(-2 < map_['data'].min() < -0.5)
+    maxs = (1.2, 2.0)  # before #4418, was (1.1, 2.0)
+    mins = (-0.8, -1.3)  # before #4418, was (-0.6, -1.2)
+    assert_equal(len(maxs), len(maps))
+    for map_, max_, min_ in zip(maps, maxs, mins):
+        assert_allclose(map_['data'].max(), max_, rtol=5e-2)
+        assert_allclose(map_['data'].min(), min_, rtol=5e-2)
     # calculated from correct looking mapping on 2015/12/26
-    assert_allclose(np.sqrt(np.sum(maps[0]['data'] ** 2)), 16.6088,
+    assert_allclose(np.sqrt(np.sum(maps[0]['data'] ** 2)), 19.0903,  # 16.6088,
                     atol=1e-3, rtol=1e-3)
-    assert_allclose(np.sqrt(np.sum(maps[1]['data'] ** 2)), 20.1245,
+    assert_allclose(np.sqrt(np.sum(maps[1]['data'] ** 2)), 19.4748,  # 20.1245,
                     atol=1e-3, rtol=1e-3)