{"action":"created","comment":{"url":"https://api.github.com/repos/nistats/nistats/pulls/comments/43251047","id":43251047,"diff_hunk":"@@ -58,183 +47,394 @@ def data_scaling(Y):\n     -------\n     Y : array of shape (n_time_points, n_voxels),\n        the data after mean-scaling, de-meaning and multiplication by 100\n+\n     mean : array of shape (n_voxels,)\n         the data mean\n     \"\"\"\n     mean = Y.mean(0)\n+    if (mean == 0).any():\n+        warn('Mean values of 0 observed.'\n+             'The data have probably been centered.'\n+             'Scaling might not work as expected')\n+    mean = np.maximum(mean, 1)\n     Y = 100 * (Y / mean - 1)\n     return Y, mean\n \n \n-class GeneralLinearModel(object):\n-    \"\"\" This class handles the so-called on General Linear Model\n+def session_glm(Y, X, noise_model='ar1', bins=100):\n+    \"\"\" GLM fit for an fMRI data matrix\n \n-    Most of what it does in the fit() and contrast() methods\n-    fit() performs the standard two-step ('ols' then 'ar1') GLM fitting\n-    contrast() returns a contrast instance, yileding statistics and p-values.\n-    The link between fit() and constrast is done vis the two class members:\n+    Parameters\n+    ----------\n+    Y : array of shape(n_time_points, n_voxels)\n+        the fMRI data\n+\n+    X : array of shape (n_time_points, n_regressors)\n+           the design matrix\n \n-    glm_results : dictionary of nistats.regression.RegressionResults instances,\n-                 describing results of a GLM fit\n+    noise_model : {'ar1', 'ols'}, optional\n+        the temporal variance model. Defaults to 'ar1'\n \n-    labels : array of shape(n_voxels),\n-            labels that associate each voxel with a results key\n+    bins : int, optional\n+        Maximum number of discrete bins for the AR(1) coef histogram\n+\n+    Returns\n+    -------\n+    labels : array of shape (n_voxels),\n+        a map of values on voxels used to identify the correseponding the model\n+\n+    results : dictionary,\n+        keys correspond to the different labels values\n+        values are RegressionResults instances corresponding to the voxels\n     \"\"\"\n+    if noise_model not in ['ar1', 'ols']:\n+        raise ValueError(' %s Unknown noise model' % noise_model)\n \n-    def __init__(self, X):\n-        \"\"\"\n-        Parameters\n-        ----------\n-        X : array of shape (n_time_points, n_regressors)\n-           the design matrix\n-        \"\"\"\n-        self.X = X\n-        self.labels_ = None\n-        self.results_ = None\n+    if Y.shape[0] != X.shape[0]:\n+        raise ValueError('Response and predictors are inconsistent')\n \n-    def fit(self, Y, model='ar1', bins=100):\n-        \"\"\"GLM fitting of a dataset using 'ols' regression or the two-pass\n+    # fit the OLS model\n+    ols_result = OLSModel(X).fit(Y)\n \n-        Parameters\n-        ----------\n-        Y : array of shape(n_time_points, n_samples)\n-            the fMRI data\n-        model : {'ar1', 'ols'}, optional\n-            the temporal variance model. Defaults to 'ar1'\n-        bins : int, optional\n-            Maximum number of discrete bins for the AR(1) coef histogram\n-        \"\"\"\n-        if model not in ['ar1', 'ols']:\n-            raise ValueError('Unknown model')\n-\n-        if Y.ndim == 1:\n-            Y = Y[:, np.newaxis]\n-\n-        if Y.shape[0] != self.X.shape[0]:\n-            raise ValueError('Response and predictors are inconsistent')\n-\n-        # fit the OLS model\n-        ols_result = OLSModel(self.X).fit(Y)\n-\n-        # compute and discretize the AR1 coefs\n-        ar1 = ((ols_result.resid[1:] * ols_result.resid[:-1]).sum(0) /\n-               (ols_result.resid ** 2).sum(0))\n-        ar1 = (ar1 * bins).astype(np.int) * 1. / bins\n-\n-        # Fit the AR model acccording to current AR(1) estimates\n-        if model == 'ar1':\n-            self.results_ = {}\n-            self.labels_ = ar1\n-            # fit the model\n-            for val in np.unique(self.labels_):\n-                m = ARModel(self.X, val)\n-                self.results_[val] = m.fit(Y[:, self.labels_ == val])\n+    # compute and discretize the AR1 coefs\n+    ar1 = ((ols_result.resid[1:] * ols_result.resid[:-1]).sum(0) /\n+           (ols_result.resid ** 2).sum(0))\n+    ar1 = (ar1 * bins).astype(np.int) * 1. / bins\n+\n+    # Fit the AR model acccording to current AR(1) estimates\n+    if noise_model == 'ar1':\n+        results = {}\n+        labels = ar1\n+        # fit the model\n+        for val in np.unique(labels):\n+            model = ARModel(X, val)\n+            results[val] = model.fit(Y[:, labels == val])\n+    else:\n+        labels = np.zeros(Y.shape[1])\n+        results = {0.0: ols_result}\n+    return labels, results\n+\n+\n+def compute_contrast(labels, regression_result, con_val, contrast_type=None):\n+    \"\"\" compute the specified contrast given an estimated glm\n+\n+    Parameters\n+    ----------\n+    labels : array of shape (n_voxels),\n+        a map of values on voxels used to identify the correseponding the model\n+\n+    results : dictionary,\n+        keys correspond to the differnt labels\n+        values are RegressionResults instances corresponding to the voxels\n+\n+    con_val : numpy.ndarray of shape (p) or (q, p)\n+        where q = number of contrast vectors and p = number of regressors\n+\n+    contrast_type : {None, 't', 'F' or 'tmin-conjunction'}, optional\n+        type of the contrast.  If None, then defaults to 't' for 1D\n+        `con_val` and 'F' for 2D `con_val`\n+\n+    Returns\n+    -------\n+    con : Contrast instance,\n+        Yields the statistics of the contrast (effects, variance, p-values)\n+    \"\"\"\n+    con_val = np.asarray(con_val)\n+    dim = 1\n+    if con_val.ndim > 1:\n+        dim = con_val.shape[0]\n+\n+    if contrast_type is None:\n+        if dim == 1:\n+            contrast_type = 't'\n         else:\n-            self.labels_ = np.zeros(Y.shape[1])\n-            self.results_ = {0.0: ols_result}\n+            contrast_type = 'F'\n+\n+    if contrast_type not in ['t', 'F']:\n+        raise ValueError('Unknown contrast type: %s' % contrast_type)\n+\n+    effect_ = np.zeros((dim, labels.size), dtype=np.float)\n+    var_ = np.zeros((dim, dim, labels.size), dtype=np.float)\n+    if contrast_type == 't':\n+        for label_ in regression_result.keys():\n+            resl = regression_result[label_].Tcontrast(con_val)\n+            effect_[:, labels == label_] = resl.effect.T\n+            var_[:, :, labels == label_] = (resl.sd ** 2).T\n+    else:\n+        for label_ in regression_result.keys():\n+            resl = regression_result[label_].Fcontrast(con_val)\n+            effect_[:, labels == label_] = resl.effect\n+            var_[:, :, labels == label_] = resl.covariance\n+\n+    dof_ = regression_result[label_].df_resid\n+    return Contrast(effect=effect_, variance=var_, dof=dof_,\n+                    contrast_type=contrast_type)\n+\n+\n+class FirstLevelGLM(BaseEstimator, TransformerMixin, CacheMixin):\n+    \"\"\" Implementation of the General Linear Model for Single-session fMRI data\n+\n+    mask: Niimg-like object, instance of NiftiMasker or MultiNiftiMasker,\n+     optional\n+        Mask to be used on data. If an instance of masker is passed,\n+        then its mask will be used. If no mask is given,\n+        it will be computed automatically by a MultiNiftiMasker with default\n+        parameters.\n+\n+    target_affine: 3x3 or 4x4 matrix, optional\n+        This parameter is passed to image.resample_img. Please see the","path":"nistats/glm.py","position":248,"original_position":248,"commit_id":"d730a94828e0a1e3e1f3930fedb41c09064e4121","original_commit_id":"d730a94828e0a1e3e1f3930fedb41c09064e4121","user":{"login":"lesteve","id":1680079,"avatar_url":"https://avatars.githubusercontent.com/u/1680079?v=3","gravatar_id":"","url":"https://api.github.com/users/lesteve","html_url":"https://github.com/lesteve","followers_url":"https://api.github.com/users/lesteve/followers","following_url":"https://api.github.com/users/lesteve/following{/other_user}","gists_url":"https://api.github.com/users/lesteve/gists{/gist_id}","starred_url":"https://api.github.com/users/lesteve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lesteve/subscriptions","organizations_url":"https://api.github.com/users/lesteve/orgs","repos_url":"https://api.github.com/users/lesteve/repos","events_url":"https://api.github.com/users/lesteve/events{/privacy}","received_events_url":"https://api.github.com/users/lesteve/received_events","type":"User","site_admin":false},"body":"maybe nilearn.image.resample_img for the sake of explicitness?","created_at":"2015-10-28T12:58:35Z","updated_at":"2015-10-28T12:58:35Z","html_url":"https://github.com/nistats/nistats/pull/22#discussion_r43251047","pull_request_url":"https://api.github.com/repos/nistats/nistats/pulls/22","_links":{"self":{"href":"https://api.github.com/repos/nistats/nistats/pulls/comments/43251047"},"html":{"href":"https://github.com/nistats/nistats/pull/22#discussion_r43251047"},"pull_request":{"href":"https://api.github.com/repos/nistats/nistats/pulls/22"}}},"pull_request":{"url":"https://api.github.com/repos/nistats/nistats/pulls/22","id":43417135,"html_url":"https://github.com/nistats/nistats/pull/22","diff_url":"https://github.com/nistats/nistats/pull/22.diff","patch_url":"https://github.com/nistats/nistats/pull/22.patch","issue_url":"https://api.github.com/repos/nistats/nistats/issues/22","number":22,"state":"open","locked":false,"title":"MRG: Glm refactor","user":{"login":"bthirion","id":234454,"avatar_url":"https://avatars.githubusercontent.com/u/234454?v=3","gravatar_id":"","url":"https://api.github.com/users/bthirion","html_url":"https://github.com/bthirion","followers_url":"https://api.github.com/users/bthirion/followers","following_url":"https://api.github.com/users/bthirion/following{/other_user}","gists_url":"https://api.github.com/users/bthirion/gists{/gist_id}","starred_url":"https://api.github.com/users/bthirion/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bthirion/subscriptions","organizations_url":"https://api.github.com/users/bthirion/orgs","repos_url":"https://api.github.com/users/bthirion/repos","events_url":"https://api.github.com/users/bthirion/events{/privacy}","received_events_url":"https://api.github.com/users/bthirion/received_events","type":"User","site_admin":false},"body":"Addresses #11 (see comments there).\r\n\r\nNote that refactoring of Contrast is deffered to the future.","created_at":"2015-08-26T15:39:36Z","updated_at":"2015-10-28T12:58:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"769aa08ab0d45284b6fc4f78b6ed935ceb3a449c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nistats/nistats/pulls/22/commits","review_comments_url":"https://api.github.com/repos/nistats/nistats/pulls/22/comments","review_comment_url":"https://api.github.com/repos/nistats/nistats/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nistats/nistats/issues/22/comments","statuses_url":"https://api.github.com/repos/nistats/nistats/statuses/d730a94828e0a1e3e1f3930fedb41c09064e4121","head":{"label":"bthirion:glm-refactor","ref":"glm-refactor","sha":"d730a94828e0a1e3e1f3930fedb41c09064e4121","user":{"login":"bthirion","id":234454,"avatar_url":"https://avatars.githubusercontent.com/u/234454?v=3","gravatar_id":"","url":"https://api.github.com/users/bthirion","html_url":"https://github.com/bthirion","followers_url":"https://api.github.com/users/bthirion/followers","following_url":"https://api.github.com/users/bthirion/following{/other_user}","gists_url":"https://api.github.com/users/bthirion/gists{/gist_id}","starred_url":"https://api.github.com/users/bthirion/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bthirion/subscriptions","organizations_url":"https://api.github.com/users/bthirion/orgs","repos_url":"https://api.github.com/users/bthirion/repos","events_url":"https://api.github.com/users/bthirion/events{/privacy}","received_events_url":"https://api.github.com/users/bthirion/received_events","type":"User","site_admin":false},"repo":{"id":36019144,"name":"nistats","full_name":"bthirion/nistats","owner":{"login":"bthirion","id":234454,"avatar_url":"https://avatars.githubusercontent.com/u/234454?v=3","gravatar_id":"","url":"https://api.github.com/users/bthirion","html_url":"https://github.com/bthirion","followers_url":"https://api.github.com/users/bthirion/followers","following_url":"https://api.github.com/users/bthirion/following{/other_user}","gists_url":"https://api.github.com/users/bthirion/gists{/gist_id}","starred_url":"https://api.github.com/users/bthirion/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bthirion/subscriptions","organizations_url":"https://api.github.com/users/bthirion/orgs","repos_url":"https://api.github.com/users/bthirion/repos","events_url":"https://api.github.com/users/bthirion/events{/privacy}","received_events_url":"https://api.github.com/users/bthirion/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bthirion/nistats","description":"Modeling and statistical inference on fMRI data in Python ","fork":true,"url":"https://api.github.com/repos/bthirion/nistats","forks_url":"https://api.github.com/repos/bthirion/nistats/forks","keys_url":"https://api.github.com/repos/bthirion/nistats/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bthirion/nistats/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bthirion/nistats/teams","hooks_url":"https://api.github.com/repos/bthirion/nistats/hooks","issue_events_url":"https://api.github.com/repos/bthirion/nistats/issues/events{/number}","events_url":"https://api.github.com/repos/bthirion/nistats/events","assignees_url":"https://api.github.com/repos/bthirion/nistats/assignees{/user}","branches_url":"https://api.github.com/repos/bthirion/nistats/branches{/branch}","tags_url":"https://api.github.com/repos/bthirion/nistats/tags","blobs_url":"https://api.github.com/repos/bthirion/nistats/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bthirion/nistats/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bthirion/nistats/git/refs{/sha}","trees_url":"https://api.github.com/repos/bthirion/nistats/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bthirion/nistats/statuses/{sha}","languages_url":"https://api.github.com/repos/bthirion/nistats/languages","stargazers_url":"https://api.github.com/repos/bthirion/nistats/stargazers","contributors_url":"https://api.github.com/repos/bthirion/nistats/contributors","subscribers_url":"https://api.github.com/repos/bthirion/nistats/subscribers","subscription_url":"https://api.github.com/repos/bthirion/nistats/subscription","commits_url":"https://api.github.com/repos/bthirion/nistats/commits{/sha}","git_commits_url":"https://api.github.com/repos/bthirion/nistats/git/commits{/sha}","comments_url":"https://api.github.com/repos/bthirion/nistats/comments{/number}","issue_comment_url":"https://api.github.com/repos/bthirion/nistats/issues/comments{/number}","contents_url":"https://api.github.com/repos/bthirion/nistats/contents/{+path}","compare_url":"https://api.github.com/repos/bthirion/nistats/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bthirion/nistats/merges","archive_url":"https://api.github.com/repos/bthirion/nistats/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bthirion/nistats/downloads","issues_url":"https://api.github.com/repos/bthirion/nistats/issues{/number}","pulls_url":"https://api.github.com/repos/bthirion/nistats/pulls{/number}","milestones_url":"https://api.github.com/repos/bthirion/nistats/milestones{/number}","notifications_url":"https://api.github.com/repos/bthirion/nistats/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bthirion/nistats/labels{/name}","releases_url":"https://api.github.com/repos/bthirion/nistats/releases{/id}","created_at":"2015-05-21T14:57:04Z","updated_at":"2015-05-21T14:57:05Z","pushed_at":"2015-09-30T06:49:44Z","git_url":"git://github.com/bthirion/nistats.git","ssh_url":"git@github.com:bthirion/nistats.git","clone_url":"https://github.com/bthirion/nistats.git","svn_url":"https://github.com/bthirion/nistats","homepage":null,"size":405,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"nistats:master","ref":"master","sha":"e35dfdff053ed3ca6052a4a58329662ba68ce3b3","user":{"login":"nistats","id":12428142,"avatar_url":"https://avatars.githubusercontent.com/u/12428142?v=3","gravatar_id":"","url":"https://api.github.com/users/nistats","html_url":"https://github.com/nistats","followers_url":"https://api.github.com/users/nistats/followers","following_url":"https://api.github.com/users/nistats/following{/other_user}","gists_url":"https://api.github.com/users/nistats/gists{/gist_id}","starred_url":"https://api.github.com/users/nistats/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nistats/subscriptions","organizations_url":"https://api.github.com/users/nistats/orgs","repos_url":"https://api.github.com/users/nistats/repos","events_url":"https://api.github.com/users/nistats/events{/privacy}","received_events_url":"https://api.github.com/users/nistats/received_events","type":"Organization","site_admin":false},"repo":{"id":35545854,"name":"nistats","full_name":"nistats/nistats","owner":{"login":"nistats","id":12428142,"avatar_url":"https://avatars.githubusercontent.com/u/12428142?v=3","gravatar_id":"","url":"https://api.github.com/users/nistats","html_url":"https://github.com/nistats","followers_url":"https://api.github.com/users/nistats/followers","following_url":"https://api.github.com/users/nistats/following{/other_user}","gists_url":"https://api.github.com/users/nistats/gists{/gist_id}","starred_url":"https://api.github.com/users/nistats/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nistats/subscriptions","organizations_url":"https://api.github.com/users/nistats/orgs","repos_url":"https://api.github.com/users/nistats/repos","events_url":"https://api.github.com/users/nistats/events{/privacy}","received_events_url":"https://api.github.com/users/nistats/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nistats/nistats","description":"Modeling and statistical inference on fMRI data in Python ","fork":false,"url":"https://api.github.com/repos/nistats/nistats","forks_url":"https://api.github.com/repos/nistats/nistats/forks","keys_url":"https://api.github.com/repos/nistats/nistats/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nistats/nistats/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nistats/nistats/teams","hooks_url":"https://api.github.com/repos/nistats/nistats/hooks","issue_events_url":"https://api.github.com/repos/nistats/nistats/issues/events{/number}","events_url":"https://api.github.com/repos/nistats/nistats/events","assignees_url":"https://api.github.com/repos/nistats/nistats/assignees{/user}","branches_url":"https://api.github.com/repos/nistats/nistats/branches{/branch}","tags_url":"https://api.github.com/repos/nistats/nistats/tags","blobs_url":"https://api.github.com/repos/nistats/nistats/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nistats/nistats/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nistats/nistats/git/refs{/sha}","trees_url":"https://api.github.com/repos/nistats/nistats/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nistats/nistats/statuses/{sha}","languages_url":"https://api.github.com/repos/nistats/nistats/languages","stargazers_url":"https://api.github.com/repos/nistats/nistats/stargazers","contributors_url":"https://api.github.com/repos/nistats/nistats/contributors","subscribers_url":"https://api.github.com/repos/nistats/nistats/subscribers","subscription_url":"https://api.github.com/repos/nistats/nistats/subscription","commits_url":"https://api.github.com/repos/nistats/nistats/commits{/sha}","git_commits_url":"https://api.github.com/repos/nistats/nistats/git/commits{/sha}","comments_url":"https://api.github.com/repos/nistats/nistats/comments{/number}","issue_comment_url":"https://api.github.com/repos/nistats/nistats/issues/comments{/number}","contents_url":"https://api.github.com/repos/nistats/nistats/contents/{+path}","compare_url":"https://api.github.com/repos/nistats/nistats/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nistats/nistats/merges","archive_url":"https://api.github.com/repos/nistats/nistats/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nistats/nistats/downloads","issues_url":"https://api.github.com/repos/nistats/nistats/issues{/number}","pulls_url":"https://api.github.com/repos/nistats/nistats/pulls{/number}","milestones_url":"https://api.github.com/repos/nistats/nistats/milestones{/number}","notifications_url":"https://api.github.com/repos/nistats/nistats/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nistats/nistats/labels{/name}","releases_url":"https://api.github.com/repos/nistats/nistats/releases{/id}","created_at":"2015-05-13T11:44:31Z","updated_at":"2015-10-25T21:54:12Z","pushed_at":"2015-10-03T16:50:35Z","git_url":"git://github.com/nistats/nistats.git","ssh_url":"git@github.com:nistats/nistats.git","clone_url":"https://github.com/nistats/nistats.git","svn_url":"https://github.com/nistats/nistats","homepage":null,"size":569,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"open_issues_count":16,"forks":8,"open_issues":16,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nistats/nistats/pulls/22"},"html":{"href":"https://github.com/nistats/nistats/pull/22"},"issue":{"href":"https://api.github.com/repos/nistats/nistats/issues/22"},"comments":{"href":"https://api.github.com/repos/nistats/nistats/issues/22/comments"},"review_comments":{"href":"https://api.github.com/repos/nistats/nistats/pulls/22/comments"},"review_comment":{"href":"https://api.github.com/repos/nistats/nistats/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nistats/nistats/pulls/22/commits"},"statuses":{"href":"https://api.github.com/repos/nistats/nistats/statuses/d730a94828e0a1e3e1f3930fedb41c09064e4121"}}}}