Repository: nilearn/nilearn

Issue:
ConnectivityMeasure requires 3D data
Otherwise it does strange stuff. Minimal example to come.



Discussion:

Resolving patch:
diff --git a/nilearn/connectome/connectivity_matrices.py b/nilearn/connectome/connectivity_matrices.py
--- a/nilearn/connectome/connectivity_matrices.py
+++ b/nilearn/connectome/connectivity_matrices.py
@@ -6,6 +6,7 @@
 
 from sklearn.base import BaseEstimator, TransformerMixin, clone
 from sklearn.covariance import LedoitWolf
+from .. import signal
 from .._utils.extmath import is_spd
 
 
@@ -287,7 +288,7 @@ class ConnectivityMeasure(BaseEstimator, TransformerMixin):
     in post-stroke patients using group-level covariance modeling, MICCAI 2010.
     """
 
-    def __init__(self, cov_estimator=LedoitWolf(),
+    def __init__(self, cov_estimator=LedoitWolf(store_precision=False),
                  kind='covariance'):
         self.cov_estimator = cov_estimator
         self.kind = kind
@@ -298,7 +299,7 @@ def fit(self, X, y=None):
 
         Parameters
         ----------
-        X : list of numpy.ndarray, shapes (n_samples, n_features)
+        X : list of numpy.ndarray, shape for each (n_samples, n_features)
             The input subjects time series.
 
         Returns
@@ -307,6 +308,26 @@ def fit(self, X, y=None):
             The object itself. Useful for chaining operations.
         """
         self.cov_estimator_ = clone(self.cov_estimator)
+        if not hasattr(X, "__iter__"):
+            raise ValueError("'subjects' input argument must be an iterable. "
+                             "You provided {0}".format(X.__class__))
+
+        subjects_types = [type(s) for s in X]
+        if set(subjects_types) != set([np.ndarray]):
+            raise ValueError("Each subject must be 2D numpy.ndarray.\n You "
+                             "provided {0}".format(str(subjects_types)))
+
+        subjects_dims = [s.ndim for s in X]
+        if set(subjects_dims) != set([2]):
+            raise ValueError("Each subject must be 2D numpy.ndarray.\n You"
+                             "provided arrays of dimensions "
+                             "{0}".format(str(subjects_dims)))
+
+        n_subjects = [s.shape[1] for s in X]
+        if len(set(n_subjects)) > 1:
+            raise ValueError("All subjects must have the same number of "
+                             "features.\nYou provided: "
+                             "{0}".format(str(n_subjects)))
 
         if self.kind == 'tangent':
             covariances = [self.cov_estimator_.fit(x).covariance_ for x in X]
@@ -330,25 +351,29 @@ def transform(self, X):
         output : numpy.ndarray, shape (n_samples, n_features, n_features)
              The transformed connectivity matrices.
         """
-        covariances = [self.cov_estimator_.fit(x).covariance_ for x in X]
-        covariances = np.array(covariances)
-        if self.kind == 'covariance':
-            connectivities = covariances
-        elif self.kind == 'tangent':
-            connectivities = [_map_eigenvalues(np.log, self.whitening_.dot(
-                                               cov).dot(self.whitening_))
-                              for cov in covariances]
-        elif self.kind == 'precision':
-            connectivities = [linalg.inv(cov) for cov in covariances]
-        elif self.kind == 'partial correlation':
-            connectivities = [_prec_to_partial(linalg.inv(cov))
-                              for cov in covariances]
-        elif self.kind == 'correlation':
-            connectivities = [_cov_to_corr(cov) for cov in covariances]
+        if self.kind == 'correlation':
+            covariances_std = [self.cov_estimator_.fit(
+                signal._standardize(x, detrend=False, normalize=True)
+                ).covariance_ for x in X]
+            connectivities = [_cov_to_corr(cov) for cov in covariances_std]
         else:
-            raise ValueError('Allowed connectivity kinds are "correlation", '
-                             '"partial correlation", "tangent", '
-                             '"covariance" and "precision", got kind '
-                             '"{}"'.format(self.kind))
+            covariances = [self.cov_estimator_.fit(x).covariance_ for x in X]
+            if self.kind == 'covariance':
+                connectivities = covariances
+            elif self.kind == 'tangent':
+                connectivities = [_map_eigenvalues(np.log, self.whitening_.dot(
+                                                   cov).dot(self.whitening_))
+                                  for cov in covariances]
+            elif self.kind == 'precision':
+                connectivities = [linalg.inv(cov) for cov in covariances]
+            elif self.kind == 'partial correlation':
+                connectivities = [_prec_to_partial(linalg.inv(cov))
+                                  for cov in covariances]
+            else:
+                raise ValueError('Allowed connectivity kinds are '
+                                 '"correlation", '
+                                 '"partial correlation", "tangent", '
+                                 '"covariance" and "precision", got kind '
+                                 '"{}"'.format(self.kind))
 
         return np.array(connectivities)


Test patch:
diff --git a/nilearn/connectome/tests/test_connectivity_matrices.py b/nilearn/connectome/tests/test_connectivity_matrices.py
--- a/nilearn/connectome/tests/test_connectivity_matrices.py
+++ b/nilearn/connectome/tests/test_connectivity_matrices.py
@@ -7,7 +7,7 @@
 from numpy.testing import assert_array_almost_equal, assert_array_equal
 from nose.tools import assert_raises, assert_equal, assert_true
 from sklearn.utils import check_random_state
-from sklearn.covariance import EmpiricalCovariance
+from sklearn.covariance import EmpiricalCovariance, LedoitWolf
 
 from nilearn._utils.extmath import is_spd
 from nilearn.connectome.connectivity_matrices import (
@@ -70,13 +70,11 @@ def grad_geometric_mean(mats, init=None, max_iter=10, tol=1e-7):
 
 
 def test_check_square():
-    """Test _check_square function"""
     non_square = np.ones((2, 3))
     assert_raises(ValueError, _check_square, non_square)
 
 
 def test_check_spd():
-    """Test _check_spd function"""
     non_sym = np.array([[0, 1], [0, 0]])
     assert_raises(ValueError, _check_spd, non_sym)
 
@@ -85,7 +83,6 @@ def test_check_spd():
 
 
 def test_map_eigenvalues():
-    """Test _map_eigenvalues function"""
     # Test on exp map
     sym = np.ones((2, 2))
     sym_exp = exp(1.) * np.array([[cosh(1.), sinh(1.)], [sinh(1.), cosh(1.)]])
@@ -103,7 +100,6 @@ def test_map_eigenvalues():
 
 
 def test_geometric_mean_couple():
-    """Test _geometric_mean function for two matrices"""
     n_features = 7
     spd1 = np.ones((n_features, n_features))
     spd1 = spd1.dot(spd1) + n_features * np.eye(n_features)
@@ -118,7 +114,6 @@ def test_geometric_mean_couple():
 
 
 def test_geometric_mean_diagonal():
-    """Test _geometric_mean function for diagonal matrices"""
     n_matrices = 20
     n_features = 5
     diags = []
@@ -133,7 +128,6 @@ def test_geometric_mean_diagonal():
 
 
 def test_geometric_mean_geodesic():
-    """Test geometric_mean function for single geodesic matrices"""
     n_matrices = 10
     n_features = 6
     sym = np.arange(n_features) / np.linalg.norm(np.arange(n_features))
@@ -246,8 +240,6 @@ def random_non_singular(p, sing_min=1., sing_max=2., random_state=0):
 
 
 def test_geometric_mean_properties():
-    """Test _geometric_mean function for random spd matrices
-    """
     n_matrices = 40
     n_features = 15
     spds = []
@@ -314,9 +306,7 @@ def test_geometric_mean_properties():
         gmean = _geometric_mean(spds, max_iter=max_iter, tol=1e-5)
 
 
-def test_geometric_mean_checks():
-    """Errors check for _geometric_mean function
-    """
+def test_geometric_mean_errors():
     n_features = 5
 
     # Non square input matrix
@@ -333,74 +323,96 @@ def test_geometric_mean_checks():
 
 
 def test_sym_to_vec():
-    """Test sym_to_vec function"""
     sym = np.ones((3, 3))
-    vec = np.array([1., sqrt(2), 1., sqrt(2),  sqrt(2), 1.])
+    vec = np.array([1., sqrt(2), 1., sqrt(2), sqrt(2), 1.])
     assert_array_almost_equal(sym_to_vec(sym), vec)
 
 
 def test_prec_to_partial():
-    """Test prec_to_partial function"""
     prec = np.array([[2., -1., 1.], [-1., 2., -1.], [1., -1., 1.]])
     partial = np.array([[1., .5, -sqrt(2.) / 2.], [.5, 1., sqrt(2.) / 2.],
                         [-sqrt(2.) / 2., sqrt(2.) / 2., 1.]])
     assert_array_almost_equal(_prec_to_partial(prec), partial)
 
 
-def test_fit_transform():
-    """Test fit_transform method for class ConnectivityMeasure"""
+def test_connectivity_measure_errors():
+    # Raising error for input subjects not iterable
+    conn_measure = ConnectivityMeasure()
+    assert_raises(ValueError, conn_measure.fit, 1.)
+
+    # Raising error for input subjects not 2D numpy.ndarrays
+    assert_raises(ValueError, conn_measure.fit, [np.ones((100, 40)),
+                                                 np.ones((10,))])
+
+    # Raising error for input subjects with different number of features
+    assert_raises(ValueError, conn_measure.fit,
+                  [np.ones((100, 40)), np.ones((100, 41))])
+
+
+def test_connectivity_measure_outputs():
     n_subjects = 10
     n_features = 49
     n_samples = 200
 
-    # Generate signals and compute empirical covariances
-    covs = []
+    # Generate signals and compute covariances
+    emp_covs = []
+    ledoit_covs = []
     signals = []
     random_state = check_random_state(0)
+    ledoit_estimator = LedoitWolf()
     for k in range(n_subjects):
         signal = random_state.randn(n_samples, n_features)
         signals.append(signal)
         signal -= signal.mean(axis=0)
-        covs.append((signal.T).dot(signal) / n_samples)
+        emp_covs.append((signal.T).dot(signal) / n_samples)
+        ledoit_covs.append(ledoit_estimator.fit(signal).covariance_)
 
-    input_covs = copy.copy(covs)
     kinds = ["correlation", "tangent", "precision",
              "partial correlation"]
-    for kind in kinds:
-        conn_measure = ConnectivityMeasure(kind=kind,
-                                           cov_estimator=EmpiricalCovariance())
-        connectivities = conn_measure.fit_transform(signals)
-
-        # Generic
-        assert_true(isinstance(connectivities, np.ndarray))
-        assert_equal(len(connectivities), len(covs))
-
-        for k, cov_new in enumerate(connectivities):
-            assert_array_equal(input_covs[k], covs[k])
-            assert(is_spd(covs[k], decimal=7))
-
-            # Positive definiteness if expected and output value checks
-            if kind == "tangent":
-                assert_array_almost_equal(cov_new, cov_new.T)
-                gmean_sqrt = _map_eigenvalues(np.sqrt,
-                                              conn_measure.mean_)
-                assert(is_spd(gmean_sqrt, decimal=7))
-                assert(is_spd(conn_measure.whitening_, decimal=7))
-                assert_array_almost_equal(conn_measure.whitening_.dot(
-                    gmean_sqrt), np.eye(n_features))
-                assert_array_almost_equal(gmean_sqrt.dot(
-                    _map_eigenvalues(np.exp, cov_new)).dot(gmean_sqrt),
-                    covs[k])
-            elif kind == "precision":
-                assert(is_spd(cov_new, decimal=7))
-                assert_array_almost_equal(cov_new.dot(covs[k]),
-                                          np.eye(n_features))
-            elif kind == "correlation":
-                assert(is_spd(cov_new, decimal=7))
-                d = np.sqrt(np.diag(np.diag(covs[k])))
-                assert_array_almost_equal(d.dot(cov_new).dot(d), covs[k])
-            elif kind == "partial correlation":
-                prec = linalg.inv(covs[k])
-                d = np.sqrt(np.diag(np.diag(prec)))
-                assert_array_almost_equal(d.dot(cov_new).dot(d), -prec +
-                                          2 * np.diag(np.diag(prec)))
+
+    # Check outputs properties
+    for cov_estimator, covs in zip([EmpiricalCovariance(), LedoitWolf()],
+                                   [emp_covs, ledoit_covs]):
+        input_covs = copy.copy(covs)
+        for kind in kinds:
+            conn_measure = ConnectivityMeasure(kind=kind,
+                                               cov_estimator=cov_estimator)
+            connectivities = conn_measure.fit_transform(signals)
+
+            # Generic
+            assert_true(isinstance(connectivities, np.ndarray))
+            assert_equal(len(connectivities), len(covs))
+
+            for k, cov_new in enumerate(connectivities):
+                assert_array_equal(input_covs[k], covs[k])
+                assert(is_spd(covs[k], decimal=7))
+
+                # Positive definiteness if expected and output value checks
+                if kind == "tangent":
+                    assert_array_almost_equal(cov_new, cov_new.T)
+                    gmean_sqrt = _map_eigenvalues(np.sqrt,
+                                                  conn_measure.mean_)
+                    assert(is_spd(gmean_sqrt, decimal=7))
+                    assert(is_spd(conn_measure.whitening_, decimal=7))
+                    assert_array_almost_equal(conn_measure.whitening_.dot(
+                        gmean_sqrt), np.eye(n_features))
+                    assert_array_almost_equal(gmean_sqrt.dot(
+                        _map_eigenvalues(np.exp, cov_new)).dot(gmean_sqrt),
+                        covs[k])
+                elif kind == "precision":
+                    assert(is_spd(cov_new, decimal=7))
+                    assert_array_almost_equal(cov_new.dot(covs[k]),
+                                              np.eye(n_features))
+                elif kind == "correlation":
+                    assert(is_spd(cov_new, decimal=7))
+                    d = np.sqrt(np.diag(np.diag(covs[k])))
+                    if cov_estimator == EmpiricalCovariance():
+                        assert_array_almost_equal(d.dot(cov_new).dot(d),
+                                                  covs[k])
+                    assert_array_almost_equal(np.diag(cov_new),
+                                              np.ones((n_features)))
+                elif kind == "partial correlation":
+                    prec = linalg.inv(covs[k])
+                    d = np.sqrt(np.diag(np.diag(prec)))
+                    assert_array_almost_equal(d.dot(cov_new).dot(d), -prec +
+                                              2 * np.diag(np.diag(prec)))