{"action":"created","comment":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/36590909","id":36590909,"diff_hunk":"@@ -0,0 +1,699 @@\n+\"\"\"\n+\n+=============================================================\n+Online Latent Dirichlet Allocation with variational inference\n+=============================================================\n+\n+This implementation is modified from Matthew D. Hoffman's onlineldavb code\n+Link: http://www.cs.princeton.edu/~mdhoffma/code/onlineldavb.tar\n+\"\"\"\n+\n+# Author: Chyi-Kwei Yau\n+# Author: Matthew D. Hoffman (original onlineldavb implementation)\n+\n+import numpy as np\n+import scipy.sparse as sp\n+from scipy.special import gammaln\n+\n+from ..base import BaseEstimator, TransformerMixin\n+from ..utils import (check_random_state, check_array,\n+                     gen_batches, gen_even_slices)\n+from ..utils.validation import NotFittedError, check_non_negative\n+\n+from ..externals.joblib import Parallel, delayed, cpu_count\n+from ..externals.six.moves import xrange\n+\n+from ._online_lda import (mean_change, _dirichlet_expectation_1d,\n+                          _dirichlet_expectation_2d)\n+\n+EPS = np.finfo(np.float).eps\n+\n+\n+def _log_dirichlet_expectation(X):\n+    \"\"\"\n+    For an array theta ~ Dir(X), computes `E[log(theta)]` given X.\n+\n+    Parameters\n+    ----------\n+    X : array-like\n+        1 or 2 dimensional vector\n+\n+    Returns\n+    -------\n+    dirichlet_expection : array-like\n+        Dirichlet expectation of input array X\n+    \"\"\"\n+\n+    if len(X.shape) == 1:\n+        dirichlet_expection = _dirichlet_expectation_1d(X)\n+    else:\n+        dirichlet_expection = _dirichlet_expectation_2d(X)\n+    return dirichlet_expection\n+\n+\n+def _update_doc_distribution(X, exp_topic_word_distr, doc_topic_prior, max_iters,\n+                             mean_change_tol, cal_sstats, rng):\n+    \"\"\"\n+    E-step: update document topic distribution.\n+    (In the literature, this is called `gamma`.)\n+\n+    Parameters\n+    ----------\n+    X : array-like or sparse matrix, shape=(n_samples, n_features)\n+        Document word matrix.\n+\n+    exp_topic_word_distr : dense matrix, shape=(n_topics, n_features)\n+        Exponential value of expection of log topic word distribution.\n+        In the literature, this is `exp(E[log(beta)])`.\n+\n+    doc_topic_prior : float\n+        Prior of document topic distribution `theta`.\n+\n+    max_iters : int\n+        Max number of iterations for updating document topic distribution in E-step.\n+\n+    mean_change_tol : float\n+        Stopping tolerance for updating document topic distribution in E-setp.\n+\n+    cal_sstats : boolean\n+        Parameter that indicate to calculate sufficient statistics or not.\n+        Set `cal_sstats` to `True` when we need to run M-step.\n+\n+    rng : RandomState instance or None\n+        Parameter that indicate how to initialize document topic distribution.\n+        Set `rng` to None will initialize document topic distribution to a\n+        constant number.\n+\n+    Returns\n+    -------\n+    (doc_topic_distr, suff_stats) :\n+        `doc_topic_distr` is unnormalized topic distribution for each document.\n+        In the literature, this is `gamma`. we can calcuate `E[log(theta)]`\n+        from it.\n+        `suff_stats` is expected sufficient statistics for the M-step.\n+            When `cal_sstats == False`, this will be None.\n+\n+    \"\"\"\n+    is_sparse_x = sp.issparse(X)\n+    n_samples, n_features = X.shape\n+    n_topics = exp_topic_word_distr.shape[0]\n+\n+    if rng:\n+        doc_topic_distr = rng.gamma(100., 0.01, (n_samples, n_topics))\n+    else:\n+        doc_topic_distr = np.ones((n_samples, n_topics))\n+\n+    # In the literature, this is `exp(E[log(theta)])`\n+    exp_doc_topic = np.exp(_log_dirichlet_expectation(doc_topic_distr))\n+\n+    # diff on `component_` (only calculate it when `cal_diff` is True)\n+    suff_stats = np.zeros(exp_topic_word_distr.shape) if cal_sstats else None\n+\n+    if is_sparse_x:\n+        X_data = X.data\n+        X_indices = X.indices\n+        X_indptr = X.indptr\n+\n+    for idx_d in xrange(n_samples):\n+        if is_sparse_x:\n+            ids = X_indices[X_indptr[idx_d]:X_indptr[idx_d + 1]]\n+            cnts = X_data[X_indptr[idx_d]:X_indptr[idx_d + 1]]\n+        else:\n+            ids = np.nonzero(X[idx_d, :])[0]\n+            cnts = X[idx_d, ids]\n+\n+        doc_topic_d = doc_topic_distr[idx_d, :]\n+        exp_doc_topic_d = exp_doc_topic[idx_d, :]\n+        exp_topic_word_d = exp_topic_word_distr[:, ids]\n+\n+        # The optimal phi_{dwk} is proportional to\n+        # exp(E[log(theta_{dk})]) * exp(E[log(beta_{dw})]).\n+        norm_phi = np.dot(exp_doc_topic_d, exp_topic_word_d) + EPS\n+\n+        # Iterate between `doc_topic_d` and `norm_phi` until convergence\n+        for _ in xrange(0, max_iters):\n+            last_d = doc_topic_d\n+\n+            doc_topic_d = (doc_topic_prior + exp_doc_topic_d *\n+                           np.dot(cnts / norm_phi, exp_topic_word_d.T))\n+            exp_doc_topic_d = np.exp(_log_dirichlet_expectation(doc_topic_d))\n+            norm_phi = np.dot(exp_doc_topic_d, exp_topic_word_d) + EPS\n+\n+            meanchange = mean_change(last_d, doc_topic_d)\n+            if meanchange < mean_change_tol:\n+                break\n+        doc_topic_distr[idx_d, :] = doc_topic_d\n+\n+        # Contribution of document d to the expected sufficient\n+        # statistics for the M step.\n+        if cal_sstats:\n+            suff_stats[:, ids] += np.outer(exp_doc_topic_d, cnts / norm_phi)\n+\n+    return (doc_topic_distr, suff_stats)\n+\n+\n+class LatentDirichletAllocation(BaseEstimator, TransformerMixin):\n+\n+    \"\"\"\n+    Latent Dirichlet Allocation with online variational Bayes algorithm\n+\n+    Parameters\n+    ----------\n+    n_topics : int, optional (default=10)\n+        Number of topics.\n+\n+    doc_topic_prior : float, optional (default=None)\n+        Prior of document topic distribution `theta`. If the value is None, defaults\n+        to `1 / n_topics`.\n+        In the literature, this is called `alpha`.\n+\n+    topic_word_prior : float, optional (default=None)\n+        Prior of topic word distribution `beta`. If the value is None, defaults\n+        to `1 / n_topics`.\n+        In the literature, this is called `eta`.\n+\n+    learning_method : 'batch' | 'online', default='online'\n+        Method used to update `_component`. Only used in `fit` method.\n+        In general, if the data size is large, the online update will be much faster than\n+        the batch update.\n+        Valid options::\n+\n+            'batch': Batch variational Bayes method. Use all training data in each EM update.\n+                Old `components_` will be overwritten in each iteration.\n+            'online': Online variational Bayes method. In each EM update, use min batch of\n+                training data to update the `components_` variable incrementally. The learning\n+                rate is controlled by the `learning_decay` and the `learning_offset` parameter.\n+\n+    learning_decay : float, optional (default=0.7)\n+        It is a parameter that control learning rate in the online learning method.\n+        The value should be set between (0.5, 1.0] to guarantee asymptotic convergence.\n+        When the value is 0.0 and batch_size is `n_samples`, the update method is same as\n+        batch learning. In the literature, this is called `kappa`.\n+\n+    learning_offset : float, optional (default=10.)\n+        A (positive) parameter that downweights early iterations in online learning.\n+        It should be greater than 1.0. In the literature, this is called `tau0`.\n+\n+    max_iter : integer, optional (default=10)\n+        The maximum number of iterations.\n+\n+    total_samples : int, optional (default=1e6)\n+        Total number of documents. It is only used in the `partial_fit` method.\n+\n+    batch_size : int, optional (default=128)\n+        Number of documents to use in each EM iteration. Only used in online learning.\n+\n+    evaluate_every : int optional (default=0)\n+        How often to evaluate perplexity. Only used in `fit` method.\n+        set it to 0 or and negative number to not evalute perplexity in training at all.\n+        Evaluating perplexity can help you check convergence in training process, but it\n+        will also increase total training time.\n+        Evaluating perplexity in every iteration might increase training time up to 2X.\n+\n+    perp_tol : float, optional (default=1e-1)\n+        Perplexity tolerance in batch learning. Only used when `evaluate_every` is\n+        greater than 0.\n+\n+    mean_change_tol : float, optional (default=1e-3)\n+        Stopping tolerance for updating document topic distribution in E-setp.\n+\n+    max_doc_update_iter : int (default=100)\n+        Max number of iterations for updating document topic distribution in E-step.\n+\n+    n_jobs : int, optional (default=1)\n+        The number of jobs to use in E-step. If -1 all CPUs are used.\n+\n+    verbose : int, optional (default=0)\n+        Verbosity level.\n+\n+    random_state : int or RandomState instance or None, optional (default=None)\n+        Pseudo Random Number generator seed control.\n+\n+\n+    Attributes\n+    ----------\n+    components_ : array, [n_topics, n_features]\n+        Topic word distribution. components_[i, j] represents word `j` in topic `i`.\n+        In the literature, this is called `lambda`.\n+\n+    n_iter_ : int\n+        Number of iteration.\n+\n+\n+    References\n+    ----------\n+    [1] \"Online Learning for Latent Dirichlet Allocation\", Matthew D. Hoffman,\n+        David M. Blei, Francis Bach, 2010\n+\n+    [2] \"Stochastic Variational Inference\", Matthew D. Hoffman,, David M. Blei,\n+        Chong Wang, John Paisley, 2013\n+\n+    [3] Matthew D. Hoffman's onlineldavb code. Link:\n+        http://www.cs.princeton.edu/~mdhoffma/code/onlineldavb.tar\n+\n+    \"\"\"\n+\n+    def __init__(self, n_topics=10, doc_topic_prior=None, topic_word_prior=None,\n+                 learning_method='online', learning_decay=.7, learning_offset=10.,\n+                 max_iter=10, batch_size=128, evaluate_every=-1, total_samples=1e6,\n+                 perp_tol=1e-1, mean_change_tol=1e-3, max_doc_update_iter=100,\n+                 n_jobs=1, verbose=0, random_state=None):\n+        self.n_topics = n_topics\n+        self.doc_topic_prior = doc_topic_prior\n+        self.topic_word_prior = topic_word_prior\n+        self.learning_method = learning_method\n+        self.learning_decay = learning_decay\n+        self.learning_offset = learning_offset\n+        self.max_iter = max_iter\n+        self.batch_size = batch_size\n+        self.evaluate_every = evaluate_every\n+        self.total_samples = total_samples\n+        self.perp_tol = perp_tol\n+        self.mean_change_tol = mean_change_tol\n+        self.max_doc_update_iter = max_doc_update_iter\n+        self.n_jobs = n_jobs\n+        self.verbose = verbose\n+        self.random_state = random_state\n+\n+    def _check_params(self):\n+        \"\"\"\n+        check model parameters\n+        \"\"\"\n+        if self.n_topics <= 0:\n+            err_msg = \"Invalid 'n_topics' parameter: %r\" % self.n_topics\n+            raise ValueError(err_msg)\n+\n+        if self.total_samples <= 0:\n+            err_msg = \"Invalid 'total_samples' parameter: %r\" % self.total_samples\n+            raise ValueError(err_msg)\n+\n+        if self.learning_offset < 0:\n+            err_msg = \"Invalid 'learning_offset' parameter: %r\" % self.learning_offset\n+            raise ValueError(err_msg)\n+\n+        if self.learning_method not in (\"batch\", \"online\"):\n+            err_msg = \"Invalid 'learning_method' parameter: %r\" % self.learning_method\n+            raise ValueError(err_msg)\n+\n+    def _init_latent_vars(self, n_features):\n+        \"\"\"\n+        Initialize latent variables.\n+        \"\"\"\n+        self.rng_ = check_random_state(self.random_state)\n+        self.n_iter_ = 1\n+        self.n_features = n_features\n+\n+        if self.doc_topic_prior is None:\n+            self.doc_topic_prior_ = 1. / self.n_topics\n+        else:\n+            self.doc_topic_prior_ = self.doc_topic_prior\n+\n+        if self.topic_word_prior is None:\n+            self.topic_word_prior_ = 1. / self.n_topics\n+        else:\n+            self.topic_word_prior_ = self.topic_word_prior\n+\n+        init_gamma = 100.\n+        init_var = 1. / init_gamma\n+        # In the literature, this is called `lambda`\n+        self.components_ = self.rng_.gamma(\n+            init_gamma, init_var, (self.n_topics, n_features))\n+        # In the literature, this is `E[log(beta)]`\n+        self.dirichlet_component_ = _log_dirichlet_expectation(self.components_)\n+        # In the literature, this is `exp(E[log(beta)])`\n+        self.exp_dirichlet_component_ = np.exp(self.dirichlet_component_)\n+\n+    def _e_step(self, X, cal_sstats, random_init):\n+        \"\"\"\n+        E-step\n+\n+        Parameters\n+        ----------\n+        X : array-like or sparse matrix, shape=(n_samples, n_features)\n+            Document word matrix.\n+\n+        cal_sstats : boolean\n+            Parameter that indicate whether to calculate sufficient statistics or not.\n+            Set `cal_sstats` to `True` when we need to run M-step.\n+\n+        random_init : boolean\n+            Parameter that indicate whether to initialize document topic distribution\n+            randomly in the E-step. Set it to `True` in training steps.\n+\n+        Returns\n+        -------\n+        (doc_topic_distr, suff_stats) :\n+            `doc_topic_distr` is unnormailzed topic distribution for each document.\n+            In the literature, this is called `gamma`.\n+            `suff_stats` is expected sufficient statistics for the M-step.\n+            When `cal_sstats == False`, it will be None.\n+\n+        \"\"\"\n+\n+        # Run e-step in parallel","path":"sklearn/decomposition/online_lda.py","position":347,"original_position":353,"commit_id":"0c485702f2d538a7753d7945a429e177a860156b","original_commit_id":"57bf37185a7a34190db70a76b8845049558f4bc7","user":{"login":"larsmans","id":335383,"avatar_url":"https://avatars.githubusercontent.com/u/335383?v=3","gravatar_id":"","url":"https://api.github.com/users/larsmans","html_url":"https://github.com/larsmans","followers_url":"https://api.github.com/users/larsmans/followers","following_url":"https://api.github.com/users/larsmans/following{/other_user}","gists_url":"https://api.github.com/users/larsmans/gists{/gist_id}","starred_url":"https://api.github.com/users/larsmans/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsmans/subscriptions","organizations_url":"https://api.github.com/users/larsmans/orgs","repos_url":"https://api.github.com/users/larsmans/repos","events_url":"https://api.github.com/users/larsmans/events{/privacy}","received_events_url":"https://api.github.com/users/larsmans/received_events","type":"User","site_admin":false},"body":"@GaelVaroquaux Is this comment still current?","created_at":"2015-08-09T17:19:52Z","updated_at":"2015-08-09T17:19:52Z","html_url":"https://github.com/scikit-learn/scikit-learn/pull/3659#discussion_r36590909","pull_request_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659","_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/36590909"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/3659#discussion_r36590909"},"pull_request":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659"}}},"pull_request":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659","id":21188463,"html_url":"https://github.com/scikit-learn/scikit-learn/pull/3659","diff_url":"https://github.com/scikit-learn/scikit-learn/pull/3659.diff","patch_url":"https://github.com/scikit-learn/scikit-learn/pull/3659.patch","issue_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/3659","number":3659,"state":"closed","locked":false,"title":"[MRG + 1] implementing LDA(Latent Dirichlet Allocation) with online variational Bayes","user":{"login":"chyikwei","id":2423442,"avatar_url":"https://avatars.githubusercontent.com/u/2423442?v=3","gravatar_id":"","url":"https://api.github.com/users/chyikwei","html_url":"https://github.com/chyikwei","followers_url":"https://api.github.com/users/chyikwei/followers","following_url":"https://api.github.com/users/chyikwei/following{/other_user}","gists_url":"https://api.github.com/users/chyikwei/gists{/gist_id}","starred_url":"https://api.github.com/users/chyikwei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chyikwei/subscriptions","organizations_url":"https://api.github.com/users/chyikwei/orgs","repos_url":"https://api.github.com/users/chyikwei/repos","events_url":"https://api.github.com/users/chyikwei/events{/privacy}","received_events_url":"https://api.github.com/users/chyikwei/received_events","type":"User","site_admin":false},"body":"This PR is an implementation of Matt Hoffman's topic modeling algorithm `LDA with online variational Bayes`.\r\n\r\nBased on previous discussion in [this email thread](http://sourceforge.net/p/scikit-learn/mailman/message/32748341/), I asked Matt if he could relicense his `onlineldavb` code to BSD. And now his code is relicensed, so I create a PR for it.\r\n\r\nI use the name `OnlineLDA` for this model and put it in `decomposition` folder. And since the model can run both online and batch update, I implemented both `fit` and `partial_fit` method.\r\nThe algorithm part and unit test is done and ready for review. Will work on an example next.\r\n\r\n###### Check List:\r\n- [X] algorithm implementation\r\n- [X] unit test \r\n- [X] profiling\r\n- [x] optimization\r\n- [X] example\r\n- [x] documentation\r\n\r\n###### Reference:\r\n[1] [\"Online Learning for Latent Dirichlet Allocation\", Matthew D. Hoffman, David M. Blei, Francis Bach](https://www.cs.princeton.edu/~blei/papers/HoffmanBleiBach2010b.pdf)\r\n[2] [original onlineldavb code (with BSD license)](https://www.dropbox.com/s/wnkro3xtqjm7bli/onlineldavb_bsd.tar)","created_at":"2014-09-13T02:28:48Z","updated_at":"2015-08-09T17:19:52Z","closed_at":"2015-08-09T17:13:01Z","merged_at":"2015-08-09T17:13:01Z","merge_commit_sha":"6a3c8dab4ee89d86f4b358d28e07f45bf220dacc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659/commits","review_comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659/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/3659/comments","statuses_url":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/0c485702f2d538a7753d7945a429e177a860156b","head":{"label":"chyikwei:onlineldavb","ref":"onlineldavb","sha":"0c485702f2d538a7753d7945a429e177a860156b","user":{"login":"chyikwei","id":2423442,"avatar_url":"https://avatars.githubusercontent.com/u/2423442?v=3","gravatar_id":"","url":"https://api.github.com/users/chyikwei","html_url":"https://github.com/chyikwei","followers_url":"https://api.github.com/users/chyikwei/followers","following_url":"https://api.github.com/users/chyikwei/following{/other_user}","gists_url":"https://api.github.com/users/chyikwei/gists{/gist_id}","starred_url":"https://api.github.com/users/chyikwei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chyikwei/subscriptions","organizations_url":"https://api.github.com/users/chyikwei/orgs","repos_url":"https://api.github.com/users/chyikwei/repos","events_url":"https://api.github.com/users/chyikwei/events{/privacy}","received_events_url":"https://api.github.com/users/chyikwei/received_events","type":"User","site_admin":false},"repo":{"id":23800539,"name":"scikit-learn","full_name":"chyikwei/scikit-learn","owner":{"login":"chyikwei","id":2423442,"avatar_url":"https://avatars.githubusercontent.com/u/2423442?v=3","gravatar_id":"","url":"https://api.github.com/users/chyikwei","html_url":"https://github.com/chyikwei","followers_url":"https://api.github.com/users/chyikwei/followers","following_url":"https://api.github.com/users/chyikwei/following{/other_user}","gists_url":"https://api.github.com/users/chyikwei/gists{/gist_id}","starred_url":"https://api.github.com/users/chyikwei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chyikwei/subscriptions","organizations_url":"https://api.github.com/users/chyikwei/orgs","repos_url":"https://api.github.com/users/chyikwei/repos","events_url":"https://api.github.com/users/chyikwei/events{/privacy}","received_events_url":"https://api.github.com/users/chyikwei/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/chyikwei/scikit-learn","description":"scikit-learn: machine learning in Python","fork":true,"url":"https://api.github.com/repos/chyikwei/scikit-learn","forks_url":"https://api.github.com/repos/chyikwei/scikit-learn/forks","keys_url":"https://api.github.com/repos/chyikwei/scikit-learn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chyikwei/scikit-learn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chyikwei/scikit-learn/teams","hooks_url":"https://api.github.com/repos/chyikwei/scikit-learn/hooks","issue_events_url":"https://api.github.com/repos/chyikwei/scikit-learn/issues/events{/number}","events_url":"https://api.github.com/repos/chyikwei/scikit-learn/events","assignees_url":"https://api.github.com/repos/chyikwei/scikit-learn/assignees{/user}","branches_url":"https://api.github.com/repos/chyikwei/scikit-learn/branches{/branch}","tags_url":"https://api.github.com/repos/chyikwei/scikit-learn/tags","blobs_url":"https://api.github.com/repos/chyikwei/scikit-learn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chyikwei/scikit-learn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chyikwei/scikit-learn/git/refs{/sha}","trees_url":"https://api.github.com/repos/chyikwei/scikit-learn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chyikwei/scikit-learn/statuses/{sha}","languages_url":"https://api.github.com/repos/chyikwei/scikit-learn/languages","stargazers_url":"https://api.github.com/repos/chyikwei/scikit-learn/stargazers","contributors_url":"https://api.github.com/repos/chyikwei/scikit-learn/contributors","subscribers_url":"https://api.github.com/repos/chyikwei/scikit-learn/subscribers","subscription_url":"https://api.github.com/repos/chyikwei/scikit-learn/subscription","commits_url":"https://api.github.com/repos/chyikwei/scikit-learn/commits{/sha}","git_commits_url":"https://api.github.com/repos/chyikwei/scikit-learn/git/commits{/sha}","comments_url":"https://api.github.com/repos/chyikwei/scikit-learn/comments{/number}","issue_comment_url":"https://api.github.com/repos/chyikwei/scikit-learn/issues/comments{/number}","contents_url":"https://api.github.com/repos/chyikwei/scikit-learn/contents/{+path}","compare_url":"https://api.github.com/repos/chyikwei/scikit-learn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chyikwei/scikit-learn/merges","archive_url":"https://api.github.com/repos/chyikwei/scikit-learn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chyikwei/scikit-learn/downloads","issues_url":"https://api.github.com/repos/chyikwei/scikit-learn/issues{/number}","pulls_url":"https://api.github.com/repos/chyikwei/scikit-learn/pulls{/number}","milestones_url":"https://api.github.com/repos/chyikwei/scikit-learn/milestones{/number}","notifications_url":"https://api.github.com/repos/chyikwei/scikit-learn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chyikwei/scikit-learn/labels{/name}","releases_url":"https://api.github.com/repos/chyikwei/scikit-learn/releases{/id}","created_at":"2014-09-08T17:22:54Z","updated_at":"2015-04-06T16:33:09Z","pushed_at":"2015-07-09T04:05:18Z","git_url":"git://github.com/chyikwei/scikit-learn.git","ssh_url":"git@github.com:chyikwei/scikit-learn.git","clone_url":"https://github.com/chyikwei/scikit-learn.git","svn_url":"https://github.com/chyikwei/scikit-learn","homepage":"http://scikit-learn.org","size":62068,"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":"scikit-learn:master","ref":"master","sha":"f65d7a7dd364e3896a09f5c8c9026d561b5d81b1","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-08-09T16:07:17Z","pushed_at":"2015-08-09T17:13:01Z","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":131319,"stargazers_count":7092,"watchers_count":7092,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4202,"mirror_url":null,"open_issues_count":754,"forks":4202,"open_issues":754,"watchers":7092,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/3659"},"issue":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/3659"},"comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/3659/comments"},"review_comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/3659/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/3659/commits"},"statuses":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/0c485702f2d538a7753d7945a429e177a860156b"}}}}