Repository: mne-tools/mne-python

Issue:
check `n_components > max_pca_components` in read_ica fails
A friend's ICA object can't be read because `n_components` is stored as `None` and `max_pca_components` is an `int`. Can anybody think of where this one is coming from?

I guess the check could be circumvented, but I'm not sure that's the optimal fix.

@dengemann 

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-9879665cf5a0> in <module>()
----> 1 mne.preprocessing.read_ica("/Users/jona/Downloads/ss0018_ET_EEG-ica.fif")

/Users/jona/tools/mne-python/mne/preprocessing/ica.py in read_ica(fname)

/Users/jona/tools/mne-python/mne/utils.py in verbose(function, *args, **kwargs)
    707         with use_log_level(verbose_level):
    708             return function(*args, **kwargs)
--> 709     return function(*args, **kwargs)
    710 
    711 

/Users/jona/tools/mne-python/mne/preprocessing/ica.py in read_ica(fname)
   1904     ica_init = dict((k, v) for k, v in ica_init.items()
   1905                     if k in _get_args(ICA.__init__))
-> 1906     ica = ICA(**ica_init)
   1907     ica.current_fit = current_fit
   1908     ica.ch_names = ch_names.split(':')

/Users/jona/tools/mne-python/mne/preprocessing/ica.py in __init__(self, n_components, max_pca_components, n_pca_components, noise_cov, random_state, method, fit_params, max_iter, verbose)

/Users/jona/tools/mne-python/mne/utils.py in verbose(function, *args, **kwargs)
    707         with use_log_level(verbose_level):
    708             return function(*args, **kwargs)
--> 709     return function(*args, **kwargs)
    710 
    711 

/Users/jona/tools/mne-python/mne/preprocessing/ica.py in __init__(self, n_components, max_pca_components, n_pca_components, noise_cov, random_state, method, fit_params, max_iter, verbose)
    235 
    236         if max_pca_components is not None and \
--> 237                 n_components > max_pca_components:
    238             raise ValueError('n_components must be smaller than '
    239                              'max_pca_components')

TypeError: unorderable types: NoneType() > int()
```


Discussion:
Can we reproduce this in a test? I thought our IO coverage is not so bad.

On Thu, Jan 26, 2017 at 3:20 PM jona-sassenhagen <notifications@github.com>
wrote:

> A friend's ICA object can't be read because n_components is stored as None
> and max_pca_components is an int. Can anybody think of where this one is
> coming from?
>
> I guess the check could be circumvented, but I'm not sure that's the
> optimal fix.
>
> @dengemann <https://github.com/dengemann>
>
> ---------------------------------------------------------------------------
> TypeError                                 Traceback (most recent call last)
> <ipython-input-3-9879665cf5a0> in <module>()
> ----> 1 mne.preprocessing.read_ica("/Users/jona/Downloads/ss0018_ET_EEG-ica.fif")
>
> /Users/jona/tools/mne-python/mne/preprocessing/ica.py in read_ica(fname)
>
> /Users/jona/tools/mne-python/mne/utils.py in verbose(function, *args, **kwargs)
>     707         with use_log_level(verbose_level):
>     708             return function(*args, **kwargs)
> --> 709     return function(*args, **kwargs)
>     710
>     711
>
> /Users/jona/tools/mne-python/mne/preprocessing/ica.py in read_ica(fname)
>    1904     ica_init = dict((k, v) for k, v in ica_init.items()
>    1905                     if k in _get_args(ICA.__init__))
> -> 1906     ica = ICA(**ica_init)
>    1907     ica.current_fit = current_fit
>    1908     ica.ch_names = ch_names.split(':')
>
> /Users/jona/tools/mne-python/mne/preprocessing/ica.py in __init__(self, n_components, max_pca_components, n_pca_components, noise_cov, random_state, method, fit_params, max_iter, verbose)
>
> /Users/jona/tools/mne-python/mne/utils.py in verbose(function, *args, **kwargs)
>     707         with use_log_level(verbose_level):
>     708             return function(*args, **kwargs)
> --> 709     return function(*args, **kwargs)
>     710
>     711
>
> /Users/jona/tools/mne-python/mne/preprocessing/ica.py in __init__(self, n_components, max_pca_components, n_pca_components, noise_cov, random_state, method, fit_params, max_iter, verbose)
>     235
>     236         if max_pca_components is not None and \
> --> 237                 n_components > max_pca_components:
>     238             raise ValueError('n_components must be smaller than '
>     239                              'max_pca_components')
>
> TypeError: unorderable types: NoneType() > int()
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-python/issues/3933>, or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB0fihKGWGEf_O2juClZjJtZSqd7QjfPks5rWKuxgaJpZM4Lur0u>
> .
>

Or is it possible the ICA object is very old? Maybe we had an old bug
It's supposedly from 0.13 ... I'll try to come up with a reproducible example. I'm a bit flabbergasted, but it sounds like something we've previously sorted out!
Yes stuff like this can happen with old objects. We don't really explicitly
support old objects ...

On Thu, Jan 26, 2017 at 3:26 PM Eric Larson <notifications@github.com>
wrote:

> Or is it possible the ICA object is very old? Maybe we had an old bug
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-python/issues/3933#issuecomment-275400529>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB0fiu0zQ6kRbanE76QmZ3muruVbbm4Mks5rWK0XgaJpZM4Lur0u>
> .
>

As this is my ICA object I figured I would just to briefly mention that the ICA objects are made in the same script as they are loaded in, i.e. not made in some older version of MNE than the one that I'm trying to read them with...
Ah. Can you share some code that shows this issue on the MNE sample data?

On Wed, Feb 1, 2017 at 3:18 PM Tim Cornelissen <notifications@github.com>
wrote:

> As this is my ICA object I figured I would just to briefly mention that
> the ICA objects are made in the same script as they are loaded in, i.e. not
> made in some older version of MNE than the one that I'm trying to read them
> with...
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-python/issues/3933#issuecomment-276668081>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB0fitrsgNPn8hONy8pKo5CmDAoNjLnFks5rYJQhgaJpZM4Lur0u>
> .
>

Sorry, I was about to update that comment with the information that I ran the ICA on a concatenation of two datasets (from the same participant, same setup, same session, etc... just two files). Not sure that is relevant, but I'll leave that judgement to you!

I'll try with the example data. Can you give me a quick hint where/how to easily find that data?
Hi Tim,

have a look at any example from our website. If you run any of those the
data will be downloaded if they aren't already on your local drive.

On Wed, Feb 1, 2017 at 3:28 PM Tim Cornelissen <notifications@github.com>
wrote:

> Sorry, I was about to update that comment with the information that I ran
> the ICA on a concatenation of two datasets (from the same participant, same
> setup, same session, etc... just two files). Not sure that is relevant, but
> I'll leave that judgement to you!
>
> I'll try with the example data. Can you give me a quick hint where/how to
> easily find that data?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-python/issues/3933#issuecomment-276670689>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB0finflBHoyUr1Kaq4uxJ1M8ES2HeDJks5rYJadgaJpZM4Lur0u>
> .
>

Thanks, I tried and I get no error when using the example data...

On the other hand, when I use my own data and print back ica.n_components and ica.max_pca_components right before saving the object, I get 

None
62

So the i/o seems to be doing its work just fine, in a sense that it is reading and writing properties of the ICA object. It appears to be the ICA object itself that has properties that are not allowed as a combination of arguments to the init of a new ICA object (as far as I can tell by the error messages).

Edit: indeed, read_ica() works when I very bluntly do: ica.n_components = ica.max_pca_components before ica.save()...
I can reliably reproduce this issue using the example data with the following code:
```python
import os
import mne
from mne.datasets import sample


data_path = sample.data_path()
output_path = os.path.normpath('/tmp')

raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
event_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
tmin, tmax = -0.2, 0.5
event_id = dict(aud_l=1, vis_l=3)

# Setup for reading the raw data
raw = mne.io.read_raw_fif(raw_fname, preload=True)
events = mne.read_events(event_fname)

# Read epochs
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True,
                    decim=4, baseline=None, preload=True, )

eeg_epochs = epochs.copy().pick_types(meg=False, eeg=True)

ica = mne.preprocessing.ICA(n_components=None, random_state=1)
ica.fit(eeg_epochs)
ica.save(fname=os.path.join(output_path, 'test_ica-ica.fif'))

# This is going to fail.
ica = mne.preprocessing.read_ica(fname=os.path.join(output_path, 'test_ica-ica.fif'))
```

Resolving patch:
diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py
--- a/mne/preprocessing/ica.py
+++ b/mne/preprocessing/ica.py
@@ -233,8 +233,9 @@ def __init__(self, n_components=None, max_pca_components=None,
 
         self.noise_cov = noise_cov
 
-        if max_pca_components is not None and \
-                n_components > max_pca_components:
+        if (n_components is not None and
+                max_pca_components is not None and
+                n_components > max_pca_components):
             raise ValueError('n_components must be smaller than '
                              'max_pca_components')
 


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
@@ -10,7 +10,7 @@
 import warnings
 
 from nose.tools import (assert_true, assert_raises, assert_equal, assert_false,
-                        assert_not_equal)
+                        assert_not_equal, assert_is_none)
 import numpy as np
 from numpy.testing import (assert_array_almost_equal, assert_array_equal,
                            assert_allclose)
@@ -710,4 +710,89 @@ def test_eog_channel():
         ica.fit(inst, picks=picks1)
         assert_false(any('EOG' in ch for ch in ica.ch_names))
 
+
+@requires_sklearn
+def test_max_pca_components_none():
+    raw = read_raw_fif(raw_fname).crop(1.5, stop).load_data()
+    events = read_events(event_name)
+    picks = pick_types(raw.info, eeg=True, meg=False)
+    epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                    baseline=(None, 0), preload=True)
+
+    max_pca_components = None
+    n_components = 10
+    random_state = 12345
+
+    tempdir = _TempDir()
+    output_fname = op.join(tempdir, 'test_ica-ica.fif')
+
+    ica = ICA(max_pca_components=max_pca_components,
+              n_components=n_components, random_state=random_state)
+    ica.fit(epochs)
+    ica.save(output_fname)
+
+    ica = read_ica(output_fname)
+
+    # ICA.fit() replaced max_pca_components, which was previously None,
+    # with the appropriate integer value.
+    assert_equal(ica.max_pca_components, epochs.info['nchan'])
+    assert_equal(ica.n_components, 10)
+
+
+@requires_sklearn
+def test_n_components_none():
+    raw = read_raw_fif(raw_fname).crop(1.5, stop).load_data()
+    events = read_events(event_name)
+    picks = pick_types(raw.info, eeg=True, meg=False)
+    epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                    baseline=(None, 0), preload=True)
+
+    max_pca_components = 10
+    n_components = None
+    random_state = 12345
+
+    tempdir = _TempDir()
+    output_fname = op.join(tempdir, 'test_ica-ica.fif')
+
+    ica = ICA(max_pca_components=max_pca_components,
+              n_components=n_components, random_state=random_state)
+    ica.fit(epochs)
+    ica.save(output_fname)
+
+    ica = read_ica(output_fname)
+
+    # ICA.fit() replaced max_pca_components, which was previously None,
+    # with the appropriate integer value.
+    assert_equal(ica.max_pca_components, 10)
+    assert_is_none(ica.n_components)
+
+
+@requires_sklearn
+def test_n_components_and_max_pca_components_none():
+    raw = read_raw_fif(raw_fname).crop(1.5, stop).load_data()
+    events = read_events(event_name)
+    picks = pick_types(raw.info, eeg=True, meg=False)
+    epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
+                    baseline=(None, 0), preload=True)
+
+    max_pca_components = None
+    n_components = None
+    random_state = 12345
+
+    tempdir = _TempDir()
+    output_fname = op.join(tempdir, 'test_ica-ica.fif')
+
+    ica = ICA(max_pca_components=max_pca_components,
+              n_components=n_components, random_state=random_state)
+    ica.fit(epochs)
+    ica.save(output_fname)
+
+    ica = read_ica(output_fname)
+
+    # ICA.fit() replaced max_pca_components, which was previously None,
+    # with the appropriate integer value.
+    assert_equal(ica.max_pca_components, epochs.info['nchan'])
+    assert_is_none(ica.n_components)
+
+
 run_tests_if_main()