{"action":"created","comment":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/25134340","id":25134340,"diff_hunk":"@@ -0,0 +1,532 @@\n+\"\"\" Kernels for Gaussian process regression and classification.\n+\n+The kernels in this module allow kernel-engineering, i.e., they can be\n+combined via the \"+\" and \"*\" operators. These expressions can also contain\n+scalar values, which are automatically converted to a constant kernel.\n+\n+All kernels allow (analytic) gradient-based hyperparameter optimization.\n+The space of hyperparameters can be specified by giving lower und upper\n+boundaries for the value of each hyperparameter (the search space is thus\n+rectangular). This can be achieved by using a pair or triple instead of a\n+single float wherever a parameter value is specified. In case of a pair,\n+the first value specifies the lower boundary and the second value the upper\n+boundary. In case of a triple, the middle value specified the initial value\n+of the parameter during hyperparameter-optimization.\n+\"\"\"\n+\n+# Author: Jan Hendrik Metzen <jhm@informatik.uni-bremen.de>\n+# Licence: BSD 3 clause\n+\n+# Note: this module is strongly inspired by the kernel module of the george\n+#       package.\n+\n+from abc import ABCMeta, abstractmethod\n+from functools import partial\n+\n+import numpy as np\n+from scipy.spatial.distance import pdist, cdist, squareform\n+from scipy.optimize import approx_fprime\n+\n+from ..metrics.pairwise import pairwise_kernels\n+from ..externals import six\n+\n+\n+class Kernel(six.with_metaclass(ABCMeta)):\n+    \"\"\" Base class for all kernels.\"\"\"\n+\n+    def _parse_param_space(self, param_space):\n+        if not np.iterable(param_space):  # fixed hyperparameter\n+            self.params = np.array([float(param_space)])\n+            self.has_bounds = False\n+            return\n+        param_space = np.atleast_2d(param_space)\n+        if param_space.shape[1] == 1:\n+            # fixed hyperparameter\n+            self.params = param_space[:, 0]\n+            self.has_bounds = False\n+        elif param_space.shape[1] == 2:\n+            # lower+upper bound for hyperparameter\n+            self.bounds = param_space\n+            self.has_bounds = True\n+            # Use geometric mean of upper and lower boundary as initial\n+            # hyperparameter value\n+            if np.any(np.equal(self.l_bound, None)) \\\n+               or np.any(np.equal(self.u_bound, None)):\n+                raise ValueError(\"Lower or upper bound being None requires \"\n+                                 \"explicitly specifying the initial value.\")\n+            self.params = np.array([np.sqrt(self.l_bound * self.u_bound)])\n+        elif param_space.shape[1] == 3:\n+            # lower bound, initial value, upper bound\n+            self.params = param_space[:, 1]\n+            self.bounds = param_space[:, [0, 2]]\n+            self.has_bounds = True\n+        else:\n+            raise ValueError(\"Invalid parameter space given. Must not have \"\n+                             \"more than 3 entries per parameter.\")\n+\n+    @property\n+    def n_params(self):\n+        return self.params.shape[0]\n+\n+    @property\n+    def bounds(self):\n+        return np.vstack((self.l_bound, self.u_bound)).T\n+\n+    @bounds.setter\n+    def bounds(self, bounds):\n+        bounds = bounds.reshape(-1, 2)\n+        self.l_bound = bounds[:, 0]\n+        self.u_bound = bounds[:, 1]\n+\n+    def __add__(self, b):\n+        if not isinstance(b, Kernel):\n+            return Sum(self, ConstantKernel(b))\n+        return Sum(self, b)\n+\n+    def __radd__(self, b):\n+        if not isinstance(b, Kernel):\n+            return Sum(ConstantKernel(b), self)\n+        return Sum(b, self)\n+\n+    def __mul__(self, b):\n+        if not isinstance(b, Kernel):\n+            return Product(self, ConstantKernel(b))\n+        return Product(self, b)\n+\n+    def __rmul__(self, b):\n+        if not isinstance(b, Kernel):\n+            return Product(ConstantKernel(b), self)\n+        return Product(b, self)\n+\n+    def __repr__(self):\n+        return \"{0}({1})\".format(self.__class__.__name__,\n+                                 \", \".join(map(\"{0}\".format, self.params)))\n+\n+    @abstractmethod\n+    def __call__(self, X, Y=None, eval_gradient=False):\n+        \"\"\"Evaluate the kernel.\"\"\"\n+\n+\n+class KernelOperator(Kernel):\n+    \"\"\" Base class for all kernel operators. \"\"\"\n+\n+    def __init__(self, k1, k2):\n+        self.k1 = k1\n+        self.k2 = k2\n+        # XXX: Deal with situations in which only some of the hyperparameter\n+        #      shall be optimized\n+        self.has_bounds = k1.has_bounds and k2.has_bounds\n+\n+    @property\n+    def params(self):\n+        return np.append(self.k1.params, self.k2.params)\n+\n+    @params.setter\n+    def params(self, theta):\n+        i = self.k1.n_params\n+        self.k1.params = theta[:i]\n+        self.k2.params = theta[i:]\n+\n+    @property\n+    def bounds(self):\n+        assert self.has_bounds","path":"sklearn/gaussian_process/kernels.py","position":132,"original_position":132,"commit_id":"34368950e6b5264b2f5d0ec255e93c51cc2c88ad","original_commit_id":"34368950e6b5264b2f5d0ec255e93c51cc2c88ad","user":{"login":"eickenberg","id":1306635,"avatar_url":"https://avatars.githubusercontent.com/u/1306635?v=3","gravatar_id":"","url":"https://api.github.com/users/eickenberg","html_url":"https://github.com/eickenberg","followers_url":"https://api.github.com/users/eickenberg/followers","following_url":"https://api.github.com/users/eickenberg/following{/other_user}","gists_url":"https://api.github.com/users/eickenberg/gists{/gist_id}","starred_url":"https://api.github.com/users/eickenberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/eickenberg/subscriptions","organizations_url":"https://api.github.com/users/eickenberg/orgs","repos_url":"https://api.github.com/users/eickenberg/repos","events_url":"https://api.github.com/users/eickenberg/events{/privacy}","received_events_url":"https://api.github.com/users/eickenberg/received_events","type":"User","site_admin":false},"body":"raise error","created_at":"2015-02-22T17:44:58Z","updated_at":"2015-02-22T17:44:58Z","html_url":"https://github.com/scikit-learn/scikit-learn/pull/4270#discussion_r25134340","pull_request_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270","_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/25134340"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/4270#discussion_r25134340"},"pull_request":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270"}}},"pull_request":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270","id":29643833,"html_url":"https://github.com/scikit-learn/scikit-learn/pull/4270","diff_url":"https://github.com/scikit-learn/scikit-learn/pull/4270.diff","patch_url":"https://github.com/scikit-learn/scikit-learn/pull/4270.patch","issue_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/4270","number":4270,"state":"open","locked":false,"title":"[WIP] Gaussian process revisited","user":{"login":"jmetzen","id":1116263,"avatar_url":"https://avatars.githubusercontent.com/u/1116263?v=3","gravatar_id":"","url":"https://api.github.com/users/jmetzen","html_url":"https://github.com/jmetzen","followers_url":"https://api.github.com/users/jmetzen/followers","following_url":"https://api.github.com/users/jmetzen/following{/other_user}","gists_url":"https://api.github.com/users/jmetzen/gists{/gist_id}","starred_url":"https://api.github.com/users/jmetzen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmetzen/subscriptions","organizations_url":"https://api.github.com/users/jmetzen/orgs","repos_url":"https://api.github.com/users/jmetzen/repos","events_url":"https://api.github.com/users/jmetzen/events{/privacy}","received_events_url":"https://api.github.com/users/jmetzen/received_events","type":"User","site_admin":false},"body":"This PR intents to address some of the points on the wish list for Gaussian processes that have been discussed on the mailing list recently.  Among the features are\r\n * kernel engineering (combining basic kernels using \"+\" and \"*\" operators)\r\n * gradient-based hyperparameter optimization\r\n * estimating the global noise level as part of the hyperparameter optimization (using a \"WhiteKernel\")\r\n * Gaussian process classification\r\n * sampling of functions from GP prior and GP posterior\r\n\r\nExamples for this PR are (for the moment) in examples/gaussian_process/new. \r\n\r\nThis PR deliberately avoids any interdependency with the existing GP code and starts completely from scratch. This is surely debatable but in my opinion the interface of the existing GP is part of the problem as it separates the specification of the kernel and its parameters which makes more complex kernels close to impossible to specify. Moreover, the existing code is not amenable for extensions, in my opinion. \r\n\r\nThis PR is still in an early state with several open issues and incomplete documentation. Feedback is very welcome. There are definitely some open questions left in this PR, in particular:\r\n * how could we make a transition from the \"old\" GP code to the new one? Do we want to do that or should this code live rather as an extension module outside of sklearn? So far, I haven't removed any of the old code.\r\n * clarify the details of specifying advanced kernels, in particular the lower and upper boundary for the hyperparameters. If someone has an alternative idea to the current triple-based notation (preliminary documentation of this in kernel.py), I am very open for changing it.\r\n\r\nList of todos:\r\n  - [ ] explicit basis function for GPR (GPML book, chapter 2.7)\r\n  - [ ] support multiple restarts of gradient-based optimization\r\n  - [ ] optimize all hyperparameters with specified search-space, keep other hyperparameters fixed (currently only all or none is supported) \r\n  - [ ] replace deepcopy(kernel) by kernel_with_params()\r\n  - [ ] test kernels for stationarity\r\n  - [ ] implement special method __eq__ for GPs\r\n  - [ ] check dimensionality of returned value of GPC.predict_proba()\r\n  - [ ] complete documentation\r\n\r\nSome plots:\r\nGP classification\r\n![gpc](https://cloud.githubusercontent.com/assets/1116263/6274705/bde22c48-b879-11e4-9522-dfce288ccba8.png)\r\n\r\nEstimation of noise-level:\r\n![noise-estimation](https://cloud.githubusercontent.com/assets/1116263/6274724/d4dc711a-b879-11e4-9582-43eaaa3dfed2.png)\r\n\r\nIllustration of GP prior and GP posterior based on sample-function:\r\n![prior_posterior](https://cloud.githubusercontent.com/assets/1116263/6274736/eb01f2b2-b879-11e4-9b68-4976be198cd7.png)\r\n\r\n","created_at":"2015-02-19T20:03:01Z","updated_at":"2015-02-22T17:44:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ace90a1f95565e4b5692577747579326d1cef0c0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270/commits","review_comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270/comments","review_comment_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/{number}","comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/4270/comments","statuses_url":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/34368950e6b5264b2f5d0ec255e93c51cc2c88ad","head":{"label":"jmetzen:gp_new","ref":"gp_new","sha":"34368950e6b5264b2f5d0ec255e93c51cc2c88ad","user":{"login":"jmetzen","id":1116263,"avatar_url":"https://avatars.githubusercontent.com/u/1116263?v=3","gravatar_id":"","url":"https://api.github.com/users/jmetzen","html_url":"https://github.com/jmetzen","followers_url":"https://api.github.com/users/jmetzen/followers","following_url":"https://api.github.com/users/jmetzen/following{/other_user}","gists_url":"https://api.github.com/users/jmetzen/gists{/gist_id}","starred_url":"https://api.github.com/users/jmetzen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmetzen/subscriptions","organizations_url":"https://api.github.com/users/jmetzen/orgs","repos_url":"https://api.github.com/users/jmetzen/repos","events_url":"https://api.github.com/users/jmetzen/events{/privacy}","received_events_url":"https://api.github.com/users/jmetzen/received_events","type":"User","site_admin":false},"repo":{"id":15583876,"name":"scikit-learn","full_name":"jmetzen/scikit-learn","owner":{"login":"jmetzen","id":1116263,"avatar_url":"https://avatars.githubusercontent.com/u/1116263?v=3","gravatar_id":"","url":"https://api.github.com/users/jmetzen","html_url":"https://github.com/jmetzen","followers_url":"https://api.github.com/users/jmetzen/followers","following_url":"https://api.github.com/users/jmetzen/following{/other_user}","gists_url":"https://api.github.com/users/jmetzen/gists{/gist_id}","starred_url":"https://api.github.com/users/jmetzen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmetzen/subscriptions","organizations_url":"https://api.github.com/users/jmetzen/orgs","repos_url":"https://api.github.com/users/jmetzen/repos","events_url":"https://api.github.com/users/jmetzen/events{/privacy}","received_events_url":"https://api.github.com/users/jmetzen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jmetzen/scikit-learn","description":"scikit-learn: machine learning in Python","fork":true,"url":"https://api.github.com/repos/jmetzen/scikit-learn","forks_url":"https://api.github.com/repos/jmetzen/scikit-learn/forks","keys_url":"https://api.github.com/repos/jmetzen/scikit-learn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jmetzen/scikit-learn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jmetzen/scikit-learn/teams","hooks_url":"https://api.github.com/repos/jmetzen/scikit-learn/hooks","issue_events_url":"https://api.github.com/repos/jmetzen/scikit-learn/issues/events{/number}","events_url":"https://api.github.com/repos/jmetzen/scikit-learn/events","assignees_url":"https://api.github.com/repos/jmetzen/scikit-learn/assignees{/user}","branches_url":"https://api.github.com/repos/jmetzen/scikit-learn/branches{/branch}","tags_url":"https://api.github.com/repos/jmetzen/scikit-learn/tags","blobs_url":"https://api.github.com/repos/jmetzen/scikit-learn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jmetzen/scikit-learn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jmetzen/scikit-learn/git/refs{/sha}","trees_url":"https://api.github.com/repos/jmetzen/scikit-learn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jmetzen/scikit-learn/statuses/{sha}","languages_url":"https://api.github.com/repos/jmetzen/scikit-learn/languages","stargazers_url":"https://api.github.com/repos/jmetzen/scikit-learn/stargazers","contributors_url":"https://api.github.com/repos/jmetzen/scikit-learn/contributors","subscribers_url":"https://api.github.com/repos/jmetzen/scikit-learn/subscribers","subscription_url":"https://api.github.com/repos/jmetzen/scikit-learn/subscription","commits_url":"https://api.github.com/repos/jmetzen/scikit-learn/commits{/sha}","git_commits_url":"https://api.github.com/repos/jmetzen/scikit-learn/git/commits{/sha}","comments_url":"https://api.github.com/repos/jmetzen/scikit-learn/comments{/number}","issue_comment_url":"https://api.github.com/repos/jmetzen/scikit-learn/issues/comments{/number}","contents_url":"https://api.github.com/repos/jmetzen/scikit-learn/contents/{+path}","compare_url":"https://api.github.com/repos/jmetzen/scikit-learn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jmetzen/scikit-learn/merges","archive_url":"https://api.github.com/repos/jmetzen/scikit-learn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jmetzen/scikit-learn/downloads","issues_url":"https://api.github.com/repos/jmetzen/scikit-learn/issues{/number}","pulls_url":"https://api.github.com/repos/jmetzen/scikit-learn/pulls{/number}","milestones_url":"https://api.github.com/repos/jmetzen/scikit-learn/milestones{/number}","notifications_url":"https://api.github.com/repos/jmetzen/scikit-learn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jmetzen/scikit-learn/labels{/name}","releases_url":"https://api.github.com/repos/jmetzen/scikit-learn/releases{/id}","created_at":"2014-01-02T13:13:12Z","updated_at":"2015-02-18T16:00:17Z","pushed_at":"2015-02-22T14:05:21Z","git_url":"git://github.com/jmetzen/scikit-learn.git","ssh_url":"git@github.com:jmetzen/scikit-learn.git","clone_url":"https://github.com/jmetzen/scikit-learn.git","svn_url":"https://github.com/jmetzen/scikit-learn","homepage":"http://scikit-learn.org","size":59445,"stargazers_count":1,"watchers_count":1,"language":"C","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":1,"default_branch":"master"}},"base":{"label":"scikit-learn:master","ref":"master","sha":"a798d9e50632f0c5dbf244a540d3f78806c0a7dc","user":{"login":"scikit-learn","id":365630,"avatar_url":"https://avatars.githubusercontent.com/u/365630?v=3","gravatar_id":"","url":"https://api.github.com/users/scikit-learn","html_url":"https://github.com/scikit-learn","followers_url":"https://api.github.com/users/scikit-learn/followers","following_url":"https://api.github.com/users/scikit-learn/following{/other_user}","gists_url":"https://api.github.com/users/scikit-learn/gists{/gist_id}","starred_url":"https://api.github.com/users/scikit-learn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scikit-learn/subscriptions","organizations_url":"https://api.github.com/users/scikit-learn/orgs","repos_url":"https://api.github.com/users/scikit-learn/repos","events_url":"https://api.github.com/users/scikit-learn/events{/privacy}","received_events_url":"https://api.github.com/users/scikit-learn/received_events","type":"Organization","site_admin":false},"repo":{"id":843222,"name":"scikit-learn","full_name":"scikit-learn/scikit-learn","owner":{"login":"scikit-learn","id":365630,"avatar_url":"https://avatars.githubusercontent.com/u/365630?v=3","gravatar_id":"","url":"https://api.github.com/users/scikit-learn","html_url":"https://github.com/scikit-learn","followers_url":"https://api.github.com/users/scikit-learn/followers","following_url":"https://api.github.com/users/scikit-learn/following{/other_user}","gists_url":"https://api.github.com/users/scikit-learn/gists{/gist_id}","starred_url":"https://api.github.com/users/scikit-learn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scikit-learn/subscriptions","organizations_url":"https://api.github.com/users/scikit-learn/orgs","repos_url":"https://api.github.com/users/scikit-learn/repos","events_url":"https://api.github.com/users/scikit-learn/events{/privacy}","received_events_url":"https://api.github.com/users/scikit-learn/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scikit-learn/scikit-learn","description":"scikit-learn: machine learning in Python","fork":false,"url":"https://api.github.com/repos/scikit-learn/scikit-learn","forks_url":"https://api.github.com/repos/scikit-learn/scikit-learn/forks","keys_url":"https://api.github.com/repos/scikit-learn/scikit-learn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scikit-learn/scikit-learn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scikit-learn/scikit-learn/teams","hooks_url":"https://api.github.com/repos/scikit-learn/scikit-learn/hooks","issue_events_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/events{/number}","events_url":"https://api.github.com/repos/scikit-learn/scikit-learn/events","assignees_url":"https://api.github.com/repos/scikit-learn/scikit-learn/assignees{/user}","branches_url":"https://api.github.com/repos/scikit-learn/scikit-learn/branches{/branch}","tags_url":"https://api.github.com/repos/scikit-learn/scikit-learn/tags","blobs_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/refs{/sha}","trees_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/{sha}","languages_url":"https://api.github.com/repos/scikit-learn/scikit-learn/languages","stargazers_url":"https://api.github.com/repos/scikit-learn/scikit-learn/stargazers","contributors_url":"https://api.github.com/repos/scikit-learn/scikit-learn/contributors","subscribers_url":"https://api.github.com/repos/scikit-learn/scikit-learn/subscribers","subscription_url":"https://api.github.com/repos/scikit-learn/scikit-learn/subscription","commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/commits{/sha}","git_commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/commits{/sha}","comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/comments{/number}","issue_comment_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/comments{/number}","contents_url":"https://api.github.com/repos/scikit-learn/scikit-learn/contents/{+path}","compare_url":"https://api.github.com/repos/scikit-learn/scikit-learn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scikit-learn/scikit-learn/merges","archive_url":"https://api.github.com/repos/scikit-learn/scikit-learn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scikit-learn/scikit-learn/downloads","issues_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues{/number}","pulls_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls{/number}","milestones_url":"https://api.github.com/repos/scikit-learn/scikit-learn/milestones{/number}","notifications_url":"https://api.github.com/repos/scikit-learn/scikit-learn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scikit-learn/scikit-learn/labels{/name}","releases_url":"https://api.github.com/repos/scikit-learn/scikit-learn/releases{/id}","created_at":"2010-08-17T09:43:38Z","updated_at":"2015-02-22T11:01:29Z","pushed_at":"2015-02-21T07:33:24Z","git_url":"git://github.com/scikit-learn/scikit-learn.git","ssh_url":"git@github.com:scikit-learn/scikit-learn.git","clone_url":"https://github.com/scikit-learn/scikit-learn.git","svn_url":"https://github.com/scikit-learn/scikit-learn","homepage":"http://scikit-learn.org","size":111774,"stargazers_count":5098,"watchers_count":5098,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2994,"mirror_url":null,"open_issues_count":585,"forks":2994,"open_issues":585,"watchers":5098,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/4270"},"issue":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/4270"},"comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/4270/comments"},"review_comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270/comments"},"review_comment":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/4270/commits"},"statuses":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/34368950e6b5264b2f5d0ec255e93c51cc2c88ad"}}}}