{"action":"created","comment":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/68841506","id":68841506,"diff_hunk":"@@ -0,0 +1,329 @@\n+import numpy as np\n+from sklearn.linear_model import Ridge\n+from sklearn.pipeline import Pipeline\n+from .feature import EventsBinarizer, DataDelayer\n+from ..io.pick import pick_types, pick_info\n+from ..externals.six import string_types\n+from ..utils import warn\n+from ..evoked import EvokedArray\n+from copy import deepcopy\n+\n+\n+class EventRelatedRegressor(object):\n+    \"\"\"Fit an rER[R/F] model to Raw objects.\n+\n+    This uses event onsets to create a continuous, binary representation of\n+    event times. It creates a time-lagged version of these events, in order to\n+    fit a set coefficients per event type that represent the effect of that\n+    event on the signal of interest.\n+\n+    Parameters\n+    ----------\n+    raw : instance of Raw\n+        The data to be modeled with event onsets. Data must be preloaded.\n+    events : array, shape (n_events, 3)\n+        An MNE events array specifying event onset indices (first column),\n+        and event types (last column)\n+    est : None | instance of sklearn estimator | string\n+        A sklearn-style estimator. The input matrix will be a binary matrix\n+        of events, while the output will be the neural signal of each channel.\n+        If a string, then an instance of a sklearn `Ridge` model will be\n+        created with alpha == 0, and this parameter passed to the `solver`\n+        parameter.\n+    event_id : dictionary\n+        A dictionary of (event_id: event_num) pairs\n+    tmin : float\n+        The minimum time for the rER[P/F]\n+    tmax : float\n+        The maximum time for the rER[P/F]\n+    preprox_x : None | instance of sklearn pipeline\n+        A pipeline that will be called on the input (X) variable.\n+        Must have a `fit_transform` method.\n+    preproc_y : None | instance of sklearn pipeline\n+        A pipeline that will be called on the output (y) variable.\n+        Must have a `fit_transform` method.\n+    preproc_func_xy : None | callable\n+        A function that will be called with *both* X and y as inputs. This is\n+        ideal for situations where both X and y should be modified in the same\n+        function. It will be called after the two preprocessing pipelines.\n+    coef_name : string\n+        The name of the fitted coefficients in the final step of the\n+        model's estimator attribute. For example, if the final step is\n+        an instance of `Ridge`, then this should be `coef_`. It will be\n+        used to retrieve the coefficients.\n+\n+    References\n+    ----------\n+    .. [1] Smith, N. J., & Kutas, M. (2015). Regression-based estimation of\n+       ERP waveforms: II. Non-linear effects, overlap correction, and\n+       practical considerations. Psychophysiology, 52(2), 169-189.\n+    \"\"\"\n+    def __init__(self, raw, events, est=None, event_id=None, tmin=-.1, tmax=.5,\n+                 preproc_x=None, preproc_y=None, preproc_func_xy=None,\n+                 picks=None, coef_name='coef_'):\n+        if events.shape[-1] != 3:\n+            raise ValueError('Events must be shape (n_events, 3)')\n+        if raw.preload is False:\n+            raise ValueError('Data must be preloaded')\n+\n+        tmin_flip = -1 * tmax\n+        tmax_flip = -1 * tmin\n+\n+        # Create events representation, pulling only events given in event_dict\n+        if event_id is None:\n+            event_id = dict(('%s' % i, i)\n+                            for i in range(len(np.unique(events[:, 2]))))\n+        events = events.copy()\n+        events[:, 0] = events[:, 0] - raw.first_samp\n+        msk_keep = np.array([ii in event_id.values() for ii in events[:, 2]])\n+        events = events[msk_keep]\n+\n+        self.ev_ixs = events[:, 0]\n+        self.ev_types = events[:, 2]\n+        self.unique_ev_types = np.unique(self.ev_types)\n+        self.event_id = event_id\n+        binarizer = EventsBinarizer(raw.n_times, sfreq=1., sparse=True)\n+        self.ev_binary = binarizer.fit_transform(self.ev_ixs, self.ev_types,\n+                                                 self.event_id)\n+        self.ev_names = binarizer.names_\n+\n+        # Prepare output data\n+        self.raw = raw\n+        if picks is None:\n+            picks = pick_types(raw.info, meg=True, eeg=True, ref_meg=True)\n+        self.picks = picks\n+\n+        # Prepare data preprocessors + design matrix\n+        delayer = DataDelayer(time_window=[tmin_flip, tmax_flip],\n+                              sfreq=self.raw.info['sfreq'])\n+        self.delayer = delayer\n+\n+        # Prepare preprocessing chains\n+        _check_preproc(preproc_x)\n+        _check_preproc(preproc_y)\n+        if preproc_x is not None:\n+            # Add the delays to the end of preproc_x\n+            preproc_x = Pipeline(preproc_x.steps + [('delayer', delayer)])\n+        else:\n+            preproc_x = Pipeline([('delayer', delayer)])\n+\n+        # Create model and attributes\n+        self.enc = EncodingModel(est, preproc_x=preproc_x, preproc_y=preproc_y,\n+                                 preproc_func_xy=preproc_func_xy,\n+                                 coef_name=coef_name)\n+        self.tmin = tmin\n+        self.tmax = tmax\n+\n+    def fit(self, X=None, y=None):\n+        \"\"\"Fit the encoding model.\"\"\"\n+        self.raw.info = pick_info(self.raw.info, self.picks)\n+        Y = self.raw._data[self.picks]\n+        self.enc.fit(self.ev_binary, Y.T)\n+        return self\n+\n+    def to_evoked(self):\n+        \"\"\"Return model coefficients as Evoked objects.\n+\n+        Returns\n+        -------\n+        evokeds : dictionary of `Evoked` instances\n+            A dictionary of (event_id: `Evoked`) pairs. The `Evoked` objects\n+            are the rER[P/F] for each channel for that event.\n+        \"\"\"\n+        # Unstack coefficients so they're shape (n_chans, n_feats, n_lags)\n+        n_delays = len(self.delayer.delays)\n+        coefs = self.enc.coef_\n+        coefs = np.stack([icoef.reshape([-1, n_delays]) for icoef in coefs])\n+        # Reverse last dimension so that it is in time, not lags\n+        coefs = coefs[..., ::-1]\n+\n+        # Iterate through event types and create the evoked objects\n+        evokeds = dict()\n+        for ev_name, ev_num in self.event_id.items():\n+            n_ev = (self.ev_types == ev_num).sum()\n+            coefs_ix = np.where(np.asarray(self.ev_names) == ev_name)[0][0]\n+\n+            # nave and kind are technically incorrect\n+            evokeds[ev_name] = EvokedArray(\n+                coefs[:, coefs_ix, :], info=self.raw.info, comment=ev_name,\n+                tmin=self.tmin, nave=n_ev, kind='average')\n+        return evokeds\n+\n+\n+class EncodingModel(object):\n+    def __init__(self, est=None, preproc_x=None, preproc_y=None,","path":"mne/encoding/model.py","position":154,"original_position":154,"commit_id":"d291dd702620d06f41cb0bb76bf43507c5f18a61","original_commit_id":"d291dd702620d06f41cb0bb76bf43507c5f18a61","user":{"login":"dengemann","id":1908618,"avatar_url":"https://avatars.githubusercontent.com/u/1908618?v=3","gravatar_id":"","url":"https://api.github.com/users/dengemann","html_url":"https://github.com/dengemann","followers_url":"https://api.github.com/users/dengemann/followers","following_url":"https://api.github.com/users/dengemann/following{/other_user}","gists_url":"https://api.github.com/users/dengemann/gists{/gist_id}","starred_url":"https://api.github.com/users/dengemann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dengemann/subscriptions","organizations_url":"https://api.github.com/users/dengemann/orgs","repos_url":"https://api.github.com/users/dengemann/repos","events_url":"https://api.github.com/users/dengemann/events{/privacy}","received_events_url":"https://api.github.com/users/dengemann/received_events","type":"User","site_admin":false},"body":"leave one blank line above","created_at":"2016-06-28T20:56:04Z","updated_at":"2016-06-28T20:56:04Z","html_url":"https://github.com/mne-tools/mne-python/pull/3310#discussion_r68841506","pull_request_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310","_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/68841506"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/3310#discussion_r68841506"},"pull_request":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310"}}},"pull_request":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310","id":74009397,"html_url":"https://github.com/mne-tools/mne-python/pull/3310","diff_url":"https://github.com/mne-tools/mne-python/pull/3310.diff","patch_url":"https://github.com/mne-tools/mne-python/pull/3310.patch","issue_url":"https://api.github.com/repos/mne-tools/mne-python/issues/3310","number":3310,"state":"open","locked":false,"title":"WIP: sklearn-style encoding / modularizing encoding pipelines","user":{"login":"choldgraf","id":1839645,"avatar_url":"https://avatars.githubusercontent.com/u/1839645?v=3","gravatar_id":"","url":"https://api.github.com/users/choldgraf","html_url":"https://github.com/choldgraf","followers_url":"https://api.github.com/users/choldgraf/followers","following_url":"https://api.github.com/users/choldgraf/following{/other_user}","gists_url":"https://api.github.com/users/choldgraf/gists{/gist_id}","starred_url":"https://api.github.com/users/choldgraf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/choldgraf/subscriptions","organizations_url":"https://api.github.com/users/choldgraf/orgs","repos_url":"https://api.github.com/users/choldgraf/repos","events_url":"https://api.github.com/users/choldgraf/events{/privacy}","received_events_url":"https://api.github.com/users/choldgraf/received_events","type":"User","site_admin":false},"body":"Hey all - this is is a WIP change to port the `linear_regression_raw` code into a more scikit-learn friendly interface. The hope is that this will be more modular and easier to use with cross-validation routines. It also allows support for both continuous and events-based inputs for the regression.\r\n\r\nI haven't changed much of @jona-sassenhagen's code so we can make comparisons, but I can start to do this if the `EncodingModel` class is on the right track. I also wrote a simple example but this should also be improved as well.\r\n\r\nMy hope is that this can be a somewhat incremental PR, and not implement the full preprocessing pipelines / feature extractions / cross validation that people eventually want, so that this doesn't take forever. But agreeing on a general API and making this code function and useful would be a good first step.\r\n\r\n@jona-sassenhagen and @kingjr probably have lots of opinions :)","created_at":"2016-06-16T01:20:58Z","updated_at":"2016-06-28T20:56:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2ce2eb077bab4e6a9ceb1a03a3b88c0a246e3033","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310/commits","review_comments_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310/comments","review_comment_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/issues/3310/comments","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/d291dd702620d06f41cb0bb76bf43507c5f18a61","head":{"label":"choldgraf:continuous_encoding","ref":"continuous_encoding","sha":"d291dd702620d06f41cb0bb76bf43507c5f18a61","user":{"login":"choldgraf","id":1839645,"avatar_url":"https://avatars.githubusercontent.com/u/1839645?v=3","gravatar_id":"","url":"https://api.github.com/users/choldgraf","html_url":"https://github.com/choldgraf","followers_url":"https://api.github.com/users/choldgraf/followers","following_url":"https://api.github.com/users/choldgraf/following{/other_user}","gists_url":"https://api.github.com/users/choldgraf/gists{/gist_id}","starred_url":"https://api.github.com/users/choldgraf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/choldgraf/subscriptions","organizations_url":"https://api.github.com/users/choldgraf/orgs","repos_url":"https://api.github.com/users/choldgraf/repos","events_url":"https://api.github.com/users/choldgraf/events{/privacy}","received_events_url":"https://api.github.com/users/choldgraf/received_events","type":"User","site_admin":false},"repo":{"id":29602471,"name":"mne-python","full_name":"choldgraf/mne-python","owner":{"login":"choldgraf","id":1839645,"avatar_url":"https://avatars.githubusercontent.com/u/1839645?v=3","gravatar_id":"","url":"https://api.github.com/users/choldgraf","html_url":"https://github.com/choldgraf","followers_url":"https://api.github.com/users/choldgraf/followers","following_url":"https://api.github.com/users/choldgraf/following{/other_user}","gists_url":"https://api.github.com/users/choldgraf/gists{/gist_id}","starred_url":"https://api.github.com/users/choldgraf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/choldgraf/subscriptions","organizations_url":"https://api.github.com/users/choldgraf/orgs","repos_url":"https://api.github.com/users/choldgraf/repos","events_url":"https://api.github.com/users/choldgraf/events{/privacy}","received_events_url":"https://api.github.com/users/choldgraf/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/choldgraf/mne-python","description":"MNE : Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python","fork":true,"url":"https://api.github.com/repos/choldgraf/mne-python","forks_url":"https://api.github.com/repos/choldgraf/mne-python/forks","keys_url":"https://api.github.com/repos/choldgraf/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/choldgraf/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/choldgraf/mne-python/teams","hooks_url":"https://api.github.com/repos/choldgraf/mne-python/hooks","issue_events_url":"https://api.github.com/repos/choldgraf/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/choldgraf/mne-python/events","assignees_url":"https://api.github.com/repos/choldgraf/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/choldgraf/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/choldgraf/mne-python/tags","blobs_url":"https://api.github.com/repos/choldgraf/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/choldgraf/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/choldgraf/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/choldgraf/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/choldgraf/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/choldgraf/mne-python/languages","stargazers_url":"https://api.github.com/repos/choldgraf/mne-python/stargazers","contributors_url":"https://api.github.com/repos/choldgraf/mne-python/contributors","subscribers_url":"https://api.github.com/repos/choldgraf/mne-python/subscribers","subscription_url":"https://api.github.com/repos/choldgraf/mne-python/subscription","commits_url":"https://api.github.com/repos/choldgraf/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/choldgraf/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/choldgraf/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/choldgraf/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/choldgraf/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/choldgraf/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/choldgraf/mne-python/merges","archive_url":"https://api.github.com/repos/choldgraf/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/choldgraf/mne-python/downloads","issues_url":"https://api.github.com/repos/choldgraf/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/choldgraf/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/choldgraf/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/choldgraf/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/choldgraf/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/choldgraf/mne-python/releases{/id}","deployments_url":"https://api.github.com/repos/choldgraf/mne-python/deployments","created_at":"2015-01-21T17:41:37Z","updated_at":"2015-01-21T17:41:40Z","pushed_at":"2016-06-27T21:55:03Z","git_url":"git://github.com/choldgraf/mne-python.git","ssh_url":"git@github.com:choldgraf/mne-python.git","clone_url":"https://github.com/choldgraf/mne-python.git","svn_url":"https://github.com/choldgraf/mne-python","homepage":"http://martinos.org/mne/","size":86909,"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":"mne-tools:master","ref":"master","sha":"92a289622da962ddd4fdd4746fb00fd7bd004aef","user":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars.githubusercontent.com/u/583875?v=3","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"repo":{"id":1301584,"name":"mne-python","full_name":"mne-tools/mne-python","owner":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars.githubusercontent.com/u/583875?v=3","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mne-tools/mne-python","description":"MNE : Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python","fork":false,"url":"https://api.github.com/repos/mne-tools/mne-python","forks_url":"https://api.github.com/repos/mne-tools/mne-python/forks","keys_url":"https://api.github.com/repos/mne-tools/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mne-tools/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mne-tools/mne-python/teams","hooks_url":"https://api.github.com/repos/mne-tools/mne-python/hooks","issue_events_url":"https://api.github.com/repos/mne-tools/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/mne-tools/mne-python/events","assignees_url":"https://api.github.com/repos/mne-tools/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/mne-tools/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/mne-tools/mne-python/tags","blobs_url":"https://api.github.com/repos/mne-tools/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mne-tools/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mne-tools/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/mne-tools/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/mne-tools/mne-python/languages","stargazers_url":"https://api.github.com/repos/mne-tools/mne-python/stargazers","contributors_url":"https://api.github.com/repos/mne-tools/mne-python/contributors","subscribers_url":"https://api.github.com/repos/mne-tools/mne-python/subscribers","subscription_url":"https://api.github.com/repos/mne-tools/mne-python/subscription","commits_url":"https://api.github.com/repos/mne-tools/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/mne-tools/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/mne-tools/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/mne-tools/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/mne-tools/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mne-tools/mne-python/merges","archive_url":"https://api.github.com/repos/mne-tools/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mne-tools/mne-python/downloads","issues_url":"https://api.github.com/repos/mne-tools/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/mne-tools/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/mne-tools/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/mne-tools/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mne-tools/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/mne-tools/mne-python/releases{/id}","deployments_url":"https://api.github.com/repos/mne-tools/mne-python/deployments","created_at":"2011-01-28T03:31:13Z","updated_at":"2016-06-26T02:34:24Z","pushed_at":"2016-06-28T19:25:28Z","git_url":"git://github.com/mne-tools/mne-python.git","ssh_url":"git@github.com:mne-tools/mne-python.git","clone_url":"https://github.com/mne-tools/mne-python.git","svn_url":"https://github.com/mne-tools/mne-python","homepage":"http://martinos.org/mne/","size":178830,"stargazers_count":211,"watchers_count":211,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":203,"mirror_url":null,"open_issues_count":201,"forks":203,"open_issues":201,"watchers":211,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/3310"},"issue":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/3310"},"comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/3310/comments"},"review_comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310/comments"},"review_comment":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/3310/commits"},"statuses":{"href":"https://api.github.com/repos/mne-tools/mne-python/statuses/d291dd702620d06f41cb0bb76bf43507c5f18a61"}}}}
{"actor":{"display_login":"dengemann"}}