Repository: nilearn/nilearn

Issue:
cast/type issue in high_variance confounds
Hi All,
I am hitting a cast error with this line

``` python
hv_confounds = nilearn.image.high_variance_confounds(func_filename)
```

The error goes like this

```
.....
 --> 144     signals -= np.mean(signals, axis=0)
    145     if type == "linear":
    146         # Keeping "signals" dtype avoids some type conversion further down,

TypeError: Cannot cast ufunc subtract output from dtype('float64') to dtype('<i2') with casting rule 'same_kind'
....
```

the versions we have installed are: nilearn 0.1.4.post1  and numpy 1.10.1
Any help is much appreciated! Love nilearn!
Best, Ralf.

PS see this seems to relate to https://github.com/scikit-learn/scikit-learn/issues/5397
PPS tried downgrading to previous numpy "conda install numpy=1.9.1", but then there are dependencies to scipy... 



Discussion:
> PS see this seems to relate to scikit-learn/scikit-learn#5397

What's your scikit-learn version? I would have thought this would be fixed in scikit-learn 0.17.

Conda tip: not sure exactly why, but you need to explicitly specify dependencies in conda when using conda install with a specific version, e.g. something like:
`conda install numpy=1.9.1 scipy scikit-learn'

otherwise you'll keep scipy and scikit-learn compiled with whatever your previous version of numpy was.

Thanks lesteve,
I tried to update to scikit learn 0.17, but this didn't solve the issue. 
Am still testing to get it to work via downgrading (thanks for the tip), which involved more dependency issues than scipy and scikit-learn.
Best, Ralf.

Looking at this one more carefully, this doesn't have anything to do with scikit-learn. Here is a snippet to reproduce the issue:

``` python
import numpy as np
import nibabel
from nilearn import image

img = nibabel.Nifti1Image(np.arange(54, dtype='i8').reshape(3, 3, 3, 2), np.eye(4))
image.high_variance_confounds(img)
```

I guess we need to convert int arrays into floats as we do in other places in nilearn.

> I guess we need to convert int arrays into floats as we do in other places in nilearn.

+1

sklearn.utils.validation.as_float_array can be used here  to fix ?

May be also related to #471 

> sklearn.utils.validation.as_float_array can be used here to fix ?

Indeed.

Hi ,
in nilearn's signal.py function I changed the following:
      `from sklearn.utils import as_float_array, check_array, check_symmetric`
and 
in line 146 of the _detrend function, where the error was thrown in the line before (signals -= np.mean(signals, axis=0))
      `signals = as_float_array(signals)    
`
That seems to fix it for me and for now. Happy to proceed with learning nilearn.
Best r


Resolving patch:
diff --git a/nilearn/signal.py b/nilearn/signal.py
--- a/nilearn/signal.py
+++ b/nilearn/signal.py
@@ -13,7 +13,7 @@
 import numpy as np
 import scipy
 from scipy import signal, stats, linalg
-from sklearn.utils import gen_even_slices
+from sklearn.utils import gen_even_slices, as_float_array
 from distutils.version import LooseVersion
 
 from ._utils.compat import _basestring
@@ -135,8 +135,7 @@ def _detrend(signals, inplace=False, type="linear", n_batches=10):
     If a signal of lenght 1 is given, it is returned unchanged.
 
     """
-    if not inplace:
-        signals = signals.copy()
+    signals = as_float_array(signals, copy=not inplace)
     if signals.shape[0] == 1:
         warnings.warn('Detrending of 3D signal has been requested but '
             'would lead to zero values. Skipping.')


Test patch:
diff --git a/nilearn/tests/test_signal.py b/nilearn/tests/test_signal.py
--- a/nilearn/tests/test_signal.py
+++ b/nilearn/tests/test_signal.py
@@ -8,6 +8,7 @@
 
 import numpy as np
 from nose.tools import assert_true, assert_false, assert_raises
+from sklearn.utils.testing import assert_less
 
 # Use nisignal here to avoid name collisions (using nilearn.signal is
 # not possible)
@@ -215,6 +216,11 @@ def test_detrend():
     np.testing.assert_array_equal(length_1_signal,
                                   nisignal._detrend(length_1_signal))
 
+    # Mean removal on integers
+    detrended = nisignal._detrend(x.astype(np.int64), inplace=True,
+                                  type="constant")
+    assert_less(abs(detrended.mean(axis=0)).max(),
+                20. * np.finfo(np.float).eps)
 
 def test_mean_of_squares():
     """Test _mean_of_squares."""