{"action":"created","comment":{"url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/comments/227183156","pull_request_review_id":167156450,"id":227183156,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyNzE4MzE1Ng==","diff_hunk":"@@ -0,0 +1,686 @@\n+# -*- coding: utf-8 -*-\n+\"\"\"\n+Created on Fri Jun  5 16:32:00 2015\n+\n+@author: Luca Puggini <lucapuggio@gmail.com>\n+\"\"\"\n+\n+from __future__ import division\n+## import usefull only for development ##\n+from abc import ABCMeta, abstractmethod\n+from statsmodels.compat.python import with_metaclass\n+\n+import numpy as np\n+import pandas as pd\n+from patsy import dmatrix\n+\n+from statsmodels.tools.linalg import matrix_sqrt, transf_constraints\n+\n+\n+### Obtain b splines from patsy ###\n+\n+\n+def _equally_spaced_knots(x, df):\n+    n_knots = df - 2\n+    x_min = x.min()\n+    x_max = x.max()\n+    knots = np.linspace(x_min, x_max, n_knots)\n+    return knots\n+\n+\n+def _R_compat_quantile(x, probs):\n+    # return np.percentile(x, 100 * np.asarray(probs))\n+    probs = np.asarray(probs)\n+    quantiles = np.asarray([np.percentile(x, 100 * prob)\n+                            for prob in probs.ravel(order=\"C\")])\n+    return quantiles.reshape(probs.shape, order=\"C\")\n+\n+\n+## from patsy splines.py\n+def _eval_bspline_basis(x, knots, degree):\n+    try:\n+        from scipy.interpolate import splev\n+    except ImportError:  # pragma: no cover\n+        raise ImportError(\"spline functionality requires scipy\")\n+    # 'knots' are assumed to be already pre-processed. E.g. usually you\n+    # want to include duplicate copies of boundary knots; you should do\n+    # that *before* calling this constructor.\n+    knots = np.atleast_1d(np.asarray(knots, dtype=float))\n+    assert knots.ndim == 1\n+    knots.sort()\n+    degree = int(degree)\n+    x = np.atleast_1d(x)\n+    if x.ndim == 2 and x.shape[1] == 1:\n+        x = x[:, 0]\n+    assert x.ndim == 1\n+    # XX FIXME: when points fall outside of the boundaries, splev and R seem\n+    # to handle them differently. I don't know why yet. So until we understand\n+    # this and decide what to do with it, I'm going to play it safe and\n+    # disallow such points.\n+    if np.min(x) < np.min(knots) or np.max(x) > np.max(knots):\n+        raise NotImplementedError(\"some data points fall outside the \"\n+                                  \"outermost knots, and I'm not sure how \"\n+                                  \"to handle them. (Patches accepted!)\")\n+    # Thanks to Charles Harris for explaining splev. It's not well\n+    # documented, but basically it computes an arbitrary b-spline basis\n+    # given knots and degree on some specificed points (or derivatives\n+    # thereof, but we don't use that functionality), and then returns some\n+    # linear combination of these basis functions. To get out the basis\n+    # functions themselves, we use linear combinations like [1, 0, 0], [0,\n+    # 1, 0], [0, 0, 1].\n+    # NB: This probably makes it rather inefficient (though I haven't checked\n+    # to be sure -- maybe the fortran code actually skips computing the basis\n+    # function for coefficients that are zero).\n+    # Note: the order of a spline is the same as its degree + 1.\n+    # Note: there are (len(knots) - order) basis functions.\n+    n_bases = len(knots) - (degree + 1)\n+    basis = np.empty((x.shape[0], n_bases), dtype=float)\n+    der1_basis = np.empty((x.shape[0], n_bases), dtype=float)\n+    der2_basis = np.empty((x.shape[0], n_bases), dtype=float)\n+\n+    for i in range(n_bases):\n+        coefs = np.zeros((n_bases,))\n+        coefs[i] = 1\n+        basis[:, i] = splev(x, (knots, coefs, degree))\n+        der1_basis[:, i] = splev(x, (knots, coefs, degree), der=1)\n+        der2_basis[:, i] = splev(x, (knots, coefs, degree), der=2)\n+\n+    return basis, der1_basis, der2_basis\n+\n+\n+def compute_all_knots(x, df, degree):\n+    order = degree + 1\n+    n_inner_knots = df - order\n+    lower_bound = np.min(x)\n+    upper_bound = np.max(x)\n+    knot_quantiles = np.linspace(0, 1, n_inner_knots + 2)[1:-1]\n+    inner_knots = _R_compat_quantile(x, knot_quantiles)\n+    all_knots = np.concatenate(([lower_bound, upper_bound] * order, inner_knots))\n+    return all_knots, lower_bound, upper_bound, inner_knots\n+\n+\n+def make_bsplines_basis(x, df, degree):\n+    ''' make a spline basis for x '''\n+\n+    all_knots, _, _, _ = compute_all_knots(x, df, degree)\n+    basis, der_basis, der2_basis = _eval_bspline_basis(x, all_knots, degree)\n+    return basis, der_basis, der2_basis\n+\n+\n+# TODO: this function should be deleted\n+def make_poly_basis(x, degree, intercept=True):\n+    '''\n+    given a vector x returns poly=(1, x, x^2, ..., x^degree)\n+    and its first and second derivative\n+    '''\n+\n+    if intercept:\n+        start = 0\n+    else:\n+        start = 1\n+\n+    n_samples = len(x)\n+    basis = np.zeros(shape=(n_samples, degree + 1 - start))\n+    der_basis = np.zeros(shape=(n_samples, degree + 1 - start))\n+    der2_basis = np.zeros(shape=(n_samples, degree + 1 - start))\n+\n+    for i in range(start, degree + 1):\n+        basis[:, i - start] = x ** i\n+        der_basis[:, i - start] = i * x ** (i - 1)\n+        der2_basis[:, i - start] = i * (i - 1) * x ** (i - 2)\n+\n+    return basis, der_basis, der2_basis\n+\n+\n+class BS(object):\n+    \"\"\"bs(x, df=None, knots=None, degree=3, include_intercept=False, lower_bound=None, upper_bound=None)\n+    Generates a B-spline basis for ``x``, allowing non-linear fits. The usual\n+    usage is something like::\n+      y ~ 1 + bs(x, 4)\n+    to fit ``y`` as a smooth function of ``x``, with 4 degrees of freedom\n+    given to the smooth.\n+    :arg df: The number of degrees of freedom to use for this spline. The\n+      return value will have this many columns. You must specify at least one\n+      of ``df`` and ``knots``.\n+    :arg knots: The interior knots to use for the spline. If unspecified, then\n+      equally spaced quantiles of the input data are used. You must specify at\n+      least one of ``df`` and ``knots``.\n+    :arg degree: The degree of the spline to use.\n+    :arg include_intercept: If ``True``, then the resulting\n+      spline basis will span the intercept term (i.e., the constant\n+      function). If ``False`` (the default) then this will not be the case,\n+      which is useful for avoiding overspecification in models that include\n+      multiple spline terms and/or an intercept term.\n+    :arg lower_bound: The lower exterior knot location.\n+    :arg upper_bound: The upper exterior knot location.\n+    A spline with ``degree=0`` is piecewise constant with breakpoints at each\n+    knot, and the default knot positions are quantiles of the input. So if you\n+    find yourself in the situation of wanting to quantize a continuous\n+    variable into ``num_bins`` equal-sized bins with a constant effect across\n+    each bin, you can use ``bs(x, num_bins - 1, degree=0)``. (The ``- 1`` is\n+    because one degree of freedom will be taken by the intercept;\n+    alternatively, you could leave the intercept term out of your model and\n+    use ``bs(x, num_bins, degree=0, include_intercept=True)``.\n+    A spline with ``degree=1`` is piecewise linear with breakpoints at each\n+    knot.\n+    The default is ``degree=3``, which gives a cubic b-spline.\n+    This is a stateful transform (for details see\n+    :ref:`stateful-transforms`). If ``knots``, ``lower_bound``, or\n+    ``upper_bound`` are not specified, they will be calculated from the data\n+    and then the chosen values will be remembered and re-used for prediction\n+    from the fitted model.\n+    Using this function requires scipy be installed.\n+    .. note:: This function is very similar to the R function of the same\n+      name. In cases where both return output at all (e.g., R's ``bs`` will\n+      raise an error if ``degree=0``, while patsy's will not), they should\n+      produce identical output given identical input and parameter settings.\n+    .. warning:: I'm not sure on what the proper handling of points outside\n+      the lower/upper bounds is, so for now attempting to evaluate a spline\n+      basis at such points produces an error. Patches gratefully accepted.\n+    .. versionadded:: 0.2.0\n+    \"\"\"\n+\n+    def __init__(self):\n+        self._tmp = {}\n+        self._degree = None\n+        self._all_knots = None\n+\n+    def memorize_chunk(self, x, df=None, knots=None, degree=3,\n+                       include_intercept=False,\n+                       lower_bound=None, upper_bound=None):\n+        args = {\"df\": df,\n+                \"knots\": knots,\n+                \"degree\": degree,\n+                \"include_intercept\": include_intercept,\n+                \"lower_bound\": lower_bound,\n+                \"upper_bound\": upper_bound,\n+                }\n+        self._tmp[\"args\"] = args\n+        # XX: check whether we need x values before saving them\n+        x = np.atleast_1d(x)\n+        if x.ndim == 2 and x.shape[1] == 1:\n+            x = x[:, 0]\n+        if x.ndim > 1:\n+            raise ValueError(\"input to 'bs' must be 1-d, \"\n+                             \"or a 2-d column vector\")\n+        # There's no better way to compute exact quantiles than memorizing\n+        # all data.\n+        self._tmp.setdefault(\"xs\", []).append(x)\n+\n+    def memorize_finish(self):\n+        tmp = self._tmp\n+        args = tmp[\"args\"]\n+        del self._tmp\n+\n+        if args[\"degree\"] < 0:\n+            raise ValueError(\"degree must be greater than 0 (not %r)\"\n+                             % (args[\"degree\"],))\n+        if int(args[\"degree\"]) != args[\"degree\"]:\n+            raise ValueError(\"degree must be an integer (not %r)\"\n+                             % (self._degree,))\n+\n+        # These are guaranteed to all be 1d vectors by the code above\n+        x = np.concatenate(tmp[\"xs\"])\n+        if args[\"df\"] is None and args[\"knots\"] is None:\n+            raise ValueError(\"must specify either df or knots\")\n+        order = args[\"degree\"] + 1\n+        if args[\"df\"] is not None:\n+            n_inner_knots = args[\"df\"] - order\n+            if not args[\"include_intercept\"]:\n+                n_inner_knots += 1\n+            if n_inner_knots < 0:\n+                raise ValueError(\"df=%r is too small for degree=%r and \"\n+                                 \"include_intercept=%r; must be >= %s\"\n+                                 % (args[\"df\"], args[\"degree\"],\n+                                    args[\"include_intercept\"],\n+                                    # We know that n_inner_knots is negative;\n+                                    # if df were that much larger, it would\n+                                    # have been zero, and things would work.\n+                                    args[\"df\"] - n_inner_knots))\n+            if args[\"knots\"] is not None:\n+                if len(args[\"knots\"]) != n_inner_knots:\n+                    raise ValueError(\"df=%s with degree=%r implies %s knots, \"\n+                                     \"but %s knots were provided\"\n+                                     % (args[\"df\"], args[\"degree\"],\n+                                        n_inner_knots, len(args[\"knots\"])))\n+            else:\n+                # Need to compute inner knots\n+                knot_quantiles = np.linspace(0, 1, n_inner_knots + 2)[1:-1]\n+                inner_knots = _R_compat_quantile(x, knot_quantiles)\n+        if args[\"knots\"] is not None:\n+            inner_knots = args[\"knots\"]\n+        if args[\"lower_bound\"] is not None:\n+            lower_bound = args[\"lower_bound\"]\n+        else:\n+            lower_bound = np.min(x)\n+        if args[\"upper_bound\"] is not None:\n+            upper_bound = args[\"upper_bound\"]\n+        else:\n+            upper_bound = np.max(x)\n+        if lower_bound > upper_bound:\n+            raise ValueError(\"lower_bound > upper_bound (%r > %r)\"\n+                             % (lower_bound, upper_bound))\n+        inner_knots = np.asarray(inner_knots)\n+        if inner_knots.ndim > 1:\n+            raise ValueError(\"knots must be 1 dimensional\")\n+        if np.any(inner_knots < lower_bound):\n+            raise ValueError(\"some knot values (%s) fall below lower bound \"\n+                             \"(%r)\"\n+                             % (inner_knots[inner_knots < lower_bound],\n+                                lower_bound))\n+        if np.any(inner_knots > upper_bound):\n+            raise ValueError(\"some knot values (%s) fall above upper bound \"\n+                             \"(%r)\"\n+                             % (inner_knots[inner_knots > upper_bound],\n+                                upper_bound))\n+        all_knots = np.concatenate(([lower_bound, upper_bound] * order,\n+                                    inner_knots))\n+        all_knots.sort()\n+\n+        self._degree = args[\"degree\"]\n+        self._all_knots = all_knots\n+\n+    def transform(self, x, df=None, knots=None, degree=3,\n+                  include_intercept=False,\n+                  lower_bound=None, upper_bound=None):\n+        basis = _eval_bspline_basis(x, self._all_knots, self._degree)\n+        if not include_intercept:\n+            basis = basis[:, 1:]\n+\n+        if isinstance(x, (pd.Series, pd.DataFrame)):\n+            basis = pd.DataFrame(basis)\n+            basis.index = x.index\n+        return basis\n+\n+\n+# TODO: try to include other kinds of splines from patsy\n+# x = np.linspace(0, 1, 30)\n+# df = 10\n+# degree = 3\n+# from patsy.mgcv_cubic_splines import cc, cr, te\n+# all_knots, lower, upper, inner  = compute_all_knots(x, df, degree)\n+# result = cc(x, df=df, knots=all_knots, lower_bound=lower, upper_bound=upper, constraints=None)\n+#\n+# import matplotlib.pyplot as plt\n+#\n+# result = np.array(result)\n+# print(result.shape)\n+# plt.plot(result.T)\n+# plt.show()\n+\n+class UnivariateGamSmoother(with_metaclass(ABCMeta)):\n+    def __init__(self, x, constraints=None, variable_name='x'):\n+        self.x = x\n+        self.variable_name = variable_name\n+        self.n_samples, self.k_variables = len(x), 1\n+\n+        base4 = self._smooth_basis_for_single_variable()\n+        if constraints == 'center':\n+            constraints = base4[0].mean(0)[None, :]\n+        if constraints is not None:\n+            ctransf = transf_constraints(constraints)\n+            self.ctransf = ctransf\n+        else:\n+            self.ctransf = None\n+\n+        self.basis_, self.der_basis_, self.der2_basis_, self.cov_der2_ = base4\n+        if self.ctransf is not None:\n+            # transform attributes that are not None\n+            if base4[0] is not None:\n+                self.basis_ = base4[0].dot(ctransf)\n+            if base4[1] is not None:\n+                self.der_basis_ = base4[1].dot(ctransf)\n+            if base4[2] is not None:\n+                self.der2_basis_ = base4[2].dot(ctransf)\n+            if base4[3] is not None:\n+                self.cov_der2_ = ctransf.T.dot(base4[3]).dot(ctransf)\n+\n+        self.dim_basis = self.basis_.shape[1]\n+        return\n+\n+    @abstractmethod\n+    def _smooth_basis_for_single_variable(self):\n+        return\n+\n+\n+class UnivariateGenericSmoother(UnivariateGamSmoother):\n+    def __init__(self, x, basis, der_basis, der2_basis, cov_der2,\n+                 variable_name='x'):\n+        self.basis_ = basis\n+        self.der_basis_ = der_basis\n+        self.der2_basis_ = der2_basis\n+        self.cov_der2_ = cov_der2\n+\n+        super(UnivariateGenericSmoother, self).__init__(x,\n+            variable_name=variable_name)\n+\n+        return\n+\n+    def _smooth_basis_for_single_variable(self):\n+        return self.basis_, self.der_basis_, self.der2_basis_, self.cov_der2_\n+\n+\n+class UnivariatePolynomialSmoother(UnivariateGamSmoother):\n+    def __init__(self, x, degree, variable_name='x'):\n+        self.degree = degree\n+        super(UnivariatePolynomialSmoother, self).__init__(x,\n+            variable_name=variable_name)\n+\n+        return\n+\n+    def _smooth_basis_for_single_variable(self):\n+        # TODO: unclear description\n+        \"\"\"\n+        given a vector x returns poly=(1, x, x^2, ..., x^degree)\n+        and its first and second derivative\n+        \"\"\"\n+\n+        basis = np.zeros(shape=(self.n_samples, self.degree))\n+        der_basis = np.zeros(shape=(self.n_samples, self.degree))\n+        der2_basis = np.zeros(shape=(self.n_samples, self.degree))\n+        for i in range(self.degree):\n+            dg = i + 1\n+            basis[:, i] = self.x ** dg\n+            der_basis[:, i] = dg * self.x ** (dg - 1)\n+            der2_basis[:, i] = dg * (dg - 1) * self.x ** (dg - 2)\n+\n+        cov_der2 = np.dot(der2_basis.T, der2_basis)\n+\n+        return basis, der_basis, der2_basis, cov_der2\n+\n+\n+class UnivariateBSplines(UnivariateGamSmoother):\n+    def __init__(self, x, degree, df, constraints=None, variable_name='x'):\n+        self.degree = degree\n+        self.df = df\n+        super(UnivariateBSplines, self).__init__(x,\n+            constraints=constraints, variable_name=variable_name)\n+\n+        return\n+\n+    def _smooth_basis_for_single_variable(self):\n+        basis, der_basis, der2_basis = make_bsplines_basis(self.x, self.df,\n+                                                           self.degree)\n+        cov_der2 = np.dot(der2_basis.T, der2_basis)","path":"statsmodels/gam/smooth_basis.py","position":404,"original_position":404,"commit_id":"73a7031da3887fc9f1ea6c4bc265242e7422b224","original_commit_id":"73a7031da3887fc9f1ea6c4bc265242e7422b224","user":{"login":"josef-pkt","id":440735,"node_id":"MDQ6VXNlcjQ0MDczNQ==","avatar_url":"https://avatars1.githubusercontent.com/u/440735?v=4","gravatar_id":"","url":"https://api.github.com/users/josef-pkt","html_url":"https://github.com/josef-pkt","followers_url":"https://api.github.com/users/josef-pkt/followers","following_url":"https://api.github.com/users/josef-pkt/following{/other_user}","gists_url":"https://api.github.com/users/josef-pkt/gists{/gist_id}","starred_url":"https://api.github.com/users/josef-pkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josef-pkt/subscriptions","organizations_url":"https://api.github.com/users/josef-pkt/orgs","repos_url":"https://api.github.com/users/josef-pkt/repos","events_url":"https://api.github.com/users/josef-pkt/events{/privacy}","received_events_url":"https://api.github.com/users/josef-pkt/received_events","type":"User","site_admin":false},"body":"Move to attribute/method ?","created_at":"2018-10-23T00:30:27Z","updated_at":"2018-10-23T00:41:21Z","html_url":"https://github.com/statsmodels/statsmodels/pull/5296#discussion_r227183156","pull_request_url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/comments/227183156"},"html":{"href":"https://github.com/statsmodels/statsmodels/pull/5296#discussion_r227183156"},"pull_request":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296"}}},"pull_request":{"url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296","id":220112676,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIwMTEyNjc2","html_url":"https://github.com/statsmodels/statsmodels/pull/5296","diff_url":"https://github.com/statsmodels/statsmodels/pull/5296.diff","patch_url":"https://github.com/statsmodels/statsmodels/pull/5296.patch","issue_url":"https://api.github.com/repos/statsmodels/statsmodels/issues/5296","number":5296,"state":"open","locked":false,"title":"WIP/ENH Gam 2744 rebased2","user":{"login":"josef-pkt","id":440735,"node_id":"MDQ6VXNlcjQ0MDczNQ==","avatar_url":"https://avatars1.githubusercontent.com/u/440735?v=4","gravatar_id":"","url":"https://api.github.com/users/josef-pkt","html_url":"https://github.com/josef-pkt","followers_url":"https://api.github.com/users/josef-pkt/followers","following_url":"https://api.github.com/users/josef-pkt/following{/other_user}","gists_url":"https://api.github.com/users/josef-pkt/gists{/gist_id}","starred_url":"https://api.github.com/users/josef-pkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josef-pkt/subscriptions","organizations_url":"https://api.github.com/users/josef-pkt/orgs","repos_url":"https://api.github.com/users/josef-pkt/repos","events_url":"https://api.github.com/users/josef-pkt/events{/privacy}","received_events_url":"https://api.github.com/users/josef-pkt/received_events","type":"User","site_admin":false},"body":"rebased version of #4575 which was rebased version of #2744\r\nThe original GSOC PR with most of the development discussion is #2435\r\n\r\nrebase conflict in compat.python: \r\nThis has now unneeded `itertools.combinations` import but I dropped the py 2.6 compat code.\r\n","created_at":"2018-10-03T17:07:59Z","updated_at":"2018-10-23T00:41:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d60704904c72069def30e0874e46c6c0f65e382b","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":163677,"node_id":"MDU6TGFiZWwxNjM2Nzc=","url":"https://api.github.com/repos/statsmodels/statsmodels/labels/comp-genmod","name":"comp-genmod","color":"ededed","default":false},{"id":142961,"node_id":"MDU6TGFiZWwxNDI5NjE=","url":"https://api.github.com/repos/statsmodels/statsmodels/labels/type-enh","name":"type-enh","color":"99ffff","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296/commits","review_comments_url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296/comments","review_comment_url":"https://api.github.com/repos/statsmodels/statsmodels/pulls/comments{/number}","comments_url":"https://api.github.com/repos/statsmodels/statsmodels/issues/5296/comments","statuses_url":"https://api.github.com/repos/statsmodels/statsmodels/statuses/73a7031da3887fc9f1ea6c4bc265242e7422b224","head":{"label":"josef-pkt:gam_2744_rebased2","ref":"gam_2744_rebased2","sha":"73a7031da3887fc9f1ea6c4bc265242e7422b224","user":{"login":"josef-pkt","id":440735,"node_id":"MDQ6VXNlcjQ0MDczNQ==","avatar_url":"https://avatars1.githubusercontent.com/u/440735?v=4","gravatar_id":"","url":"https://api.github.com/users/josef-pkt","html_url":"https://github.com/josef-pkt","followers_url":"https://api.github.com/users/josef-pkt/followers","following_url":"https://api.github.com/users/josef-pkt/following{/other_user}","gists_url":"https://api.github.com/users/josef-pkt/gists{/gist_id}","starred_url":"https://api.github.com/users/josef-pkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josef-pkt/subscriptions","organizations_url":"https://api.github.com/users/josef-pkt/orgs","repos_url":"https://api.github.com/users/josef-pkt/repos","events_url":"https://api.github.com/users/josef-pkt/events{/privacy}","received_events_url":"https://api.github.com/users/josef-pkt/received_events","type":"User","site_admin":false},"repo":{"id":1885313,"node_id":"MDEwOlJlcG9zaXRvcnkxODg1MzEz","name":"statsmodels","full_name":"josef-pkt/statsmodels","private":false,"owner":{"login":"josef-pkt","id":440735,"node_id":"MDQ6VXNlcjQ0MDczNQ==","avatar_url":"https://avatars1.githubusercontent.com/u/440735?v=4","gravatar_id":"","url":"https://api.github.com/users/josef-pkt","html_url":"https://github.com/josef-pkt","followers_url":"https://api.github.com/users/josef-pkt/followers","following_url":"https://api.github.com/users/josef-pkt/following{/other_user}","gists_url":"https://api.github.com/users/josef-pkt/gists{/gist_id}","starred_url":"https://api.github.com/users/josef-pkt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josef-pkt/subscriptions","organizations_url":"https://api.github.com/users/josef-pkt/orgs","repos_url":"https://api.github.com/users/josef-pkt/repos","events_url":"https://api.github.com/users/josef-pkt/events{/privacy}","received_events_url":"https://api.github.com/users/josef-pkt/received_events","type":"User","site_admin":false},"html_url":"https://github.com/josef-pkt/statsmodels","description":"main repo of statsmodels","fork":true,"url":"https://api.github.com/repos/josef-pkt/statsmodels","forks_url":"https://api.github.com/repos/josef-pkt/statsmodels/forks","keys_url":"https://api.github.com/repos/josef-pkt/statsmodels/keys{/key_id}","collaborators_url":"https://api.github.com/repos/josef-pkt/statsmodels/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/josef-pkt/statsmodels/teams","hooks_url":"https://api.github.com/repos/josef-pkt/statsmodels/hooks","issue_events_url":"https://api.github.com/repos/josef-pkt/statsmodels/issues/events{/number}","events_url":"https://api.github.com/repos/josef-pkt/statsmodels/events","assignees_url":"https://api.github.com/repos/josef-pkt/statsmodels/assignees{/user}","branches_url":"https://api.github.com/repos/josef-pkt/statsmodels/branches{/branch}","tags_url":"https://api.github.com/repos/josef-pkt/statsmodels/tags","blobs_url":"https://api.github.com/repos/josef-pkt/statsmodels/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/josef-pkt/statsmodels/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/josef-pkt/statsmodels/git/refs{/sha}","trees_url":"https://api.github.com/repos/josef-pkt/statsmodels/git/trees{/sha}","statuses_url":"https://api.github.com/repos/josef-pkt/statsmodels/statuses/{sha}","languages_url":"https://api.github.com/repos/josef-pkt/statsmodels/languages","stargazers_url":"https://api.github.com/repos/josef-pkt/statsmodels/stargazers","contributors_url":"https://api.github.com/repos/josef-pkt/statsmodels/contributors","subscribers_url":"https://api.github.com/repos/josef-pkt/statsmodels/subscribers","subscription_url":"https://api.github.com/repos/josef-pkt/statsmodels/subscription","commits_url":"https://api.github.com/repos/josef-pkt/statsmodels/commits{/sha}","git_commits_url":"https://api.github.com/repos/josef-pkt/statsmodels/git/commits{/sha}","comments_url":"https://api.github.com/repos/josef-pkt/statsmodels/comments{/number}","issue_comment_url":"https://api.github.com/repos/josef-pkt/statsmodels/issues/comments{/number}","contents_url":"https://api.github.com/repos/josef-pkt/statsmodels/contents/{+path}","compare_url":"https://api.github.com/repos/josef-pkt/statsmodels/compare/{base}...{head}","merges_url":"https://api.github.com/repos/josef-pkt/statsmodels/merges","archive_url":"https://api.github.com/repos/josef-pkt/statsmodels/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/josef-pkt/statsmodels/downloads","issues_url":"https://api.github.com/repos/josef-pkt/statsmodels/issues{/number}","pulls_url":"https://api.github.com/repos/josef-pkt/statsmodels/pulls{/number}","milestones_url":"https://api.github.com/repos/josef-pkt/statsmodels/milestones{/number}","notifications_url":"https://api.github.com/repos/josef-pkt/statsmodels/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/josef-pkt/statsmodels/labels{/name}","releases_url":"https://api.github.com/repos/josef-pkt/statsmodels/releases{/id}","deployments_url":"https://api.github.com/repos/josef-pkt/statsmodels/deployments","created_at":"2011-06-12T17:30:37Z","updated_at":"2018-07-13T08:47:55Z","pushed_at":"2018-10-22T19:40:24Z","git_url":"git://github.com/josef-pkt/statsmodels.git","ssh_url":"git@github.com:josef-pkt/statsmodels.git","clone_url":"https://github.com/josef-pkt/statsmodels.git","svn_url":"https://github.com/josef-pkt/statsmodels","homepage":"http://www.statsmodels.org/devel","size":32012,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":18,"mirror_url":null,"archived":false,"open_issues_count":2,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":18,"open_issues":2,"watchers":17,"default_branch":"master"}},"base":{"label":"statsmodels:master","ref":"master","sha":"362f107ebbcd0ac7a41fa474a9a606e0d4b98f56","user":{"login":"statsmodels","id":717666,"node_id":"MDEyOk9yZ2FuaXphdGlvbjcxNzY2Ng==","avatar_url":"https://avatars3.githubusercontent.com/u/717666?v=4","gravatar_id":"","url":"https://api.github.com/users/statsmodels","html_url":"https://github.com/statsmodels","followers_url":"https://api.github.com/users/statsmodels/followers","following_url":"https://api.github.com/users/statsmodels/following{/other_user}","gists_url":"https://api.github.com/users/statsmodels/gists{/gist_id}","starred_url":"https://api.github.com/users/statsmodels/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/statsmodels/subscriptions","organizations_url":"https://api.github.com/users/statsmodels/orgs","repos_url":"https://api.github.com/users/statsmodels/repos","events_url":"https://api.github.com/users/statsmodels/events{/privacy}","received_events_url":"https://api.github.com/users/statsmodels/received_events","type":"Organization","site_admin":false},"repo":{"id":1885237,"node_id":"MDEwOlJlcG9zaXRvcnkxODg1MjM3","name":"statsmodels","full_name":"statsmodels/statsmodels","private":false,"owner":{"login":"statsmodels","id":717666,"node_id":"MDEyOk9yZ2FuaXphdGlvbjcxNzY2Ng==","avatar_url":"https://avatars3.githubusercontent.com/u/717666?v=4","gravatar_id":"","url":"https://api.github.com/users/statsmodels","html_url":"https://github.com/statsmodels","followers_url":"https://api.github.com/users/statsmodels/followers","following_url":"https://api.github.com/users/statsmodels/following{/other_user}","gists_url":"https://api.github.com/users/statsmodels/gists{/gist_id}","starred_url":"https://api.github.com/users/statsmodels/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/statsmodels/subscriptions","organizations_url":"https://api.github.com/users/statsmodels/orgs","repos_url":"https://api.github.com/users/statsmodels/repos","events_url":"https://api.github.com/users/statsmodels/events{/privacy}","received_events_url":"https://api.github.com/users/statsmodels/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/statsmodels/statsmodels","description":"Statsmodels: statistical modeling and econometrics in Python","fork":false,"url":"https://api.github.com/repos/statsmodels/statsmodels","forks_url":"https://api.github.com/repos/statsmodels/statsmodels/forks","keys_url":"https://api.github.com/repos/statsmodels/statsmodels/keys{/key_id}","collaborators_url":"https://api.github.com/repos/statsmodels/statsmodels/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/statsmodels/statsmodels/teams","hooks_url":"https://api.github.com/repos/statsmodels/statsmodels/hooks","issue_events_url":"https://api.github.com/repos/statsmodels/statsmodels/issues/events{/number}","events_url":"https://api.github.com/repos/statsmodels/statsmodels/events","assignees_url":"https://api.github.com/repos/statsmodels/statsmodels/assignees{/user}","branches_url":"https://api.github.com/repos/statsmodels/statsmodels/branches{/branch}","tags_url":"https://api.github.com/repos/statsmodels/statsmodels/tags","blobs_url":"https://api.github.com/repos/statsmodels/statsmodels/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/statsmodels/statsmodels/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/statsmodels/statsmodels/git/refs{/sha}","trees_url":"https://api.github.com/repos/statsmodels/statsmodels/git/trees{/sha}","statuses_url":"https://api.github.com/repos/statsmodels/statsmodels/statuses/{sha}","languages_url":"https://api.github.com/repos/statsmodels/statsmodels/languages","stargazers_url":"https://api.github.com/repos/statsmodels/statsmodels/stargazers","contributors_url":"https://api.github.com/repos/statsmodels/statsmodels/contributors","subscribers_url":"https://api.github.com/repos/statsmodels/statsmodels/subscribers","subscription_url":"https://api.github.com/repos/statsmodels/statsmodels/subscription","commits_url":"https://api.github.com/repos/statsmodels/statsmodels/commits{/sha}","git_commits_url":"https://api.github.com/repos/statsmodels/statsmodels/git/commits{/sha}","comments_url":"https://api.github.com/repos/statsmodels/statsmodels/comments{/number}","issue_comment_url":"https://api.github.com/repos/statsmodels/statsmodels/issues/comments{/number}","contents_url":"https://api.github.com/repos/statsmodels/statsmodels/contents/{+path}","compare_url":"https://api.github.com/repos/statsmodels/statsmodels/compare/{base}...{head}","merges_url":"https://api.github.com/repos/statsmodels/statsmodels/merges","archive_url":"https://api.github.com/repos/statsmodels/statsmodels/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/statsmodels/statsmodels/downloads","issues_url":"https://api.github.com/repos/statsmodels/statsmodels/issues{/number}","pulls_url":"https://api.github.com/repos/statsmodels/statsmodels/pulls{/number}","milestones_url":"https://api.github.com/repos/statsmodels/statsmodels/milestones{/number}","notifications_url":"https://api.github.com/repos/statsmodels/statsmodels/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/statsmodels/statsmodels/labels{/name}","releases_url":"https://api.github.com/repos/statsmodels/statsmodels/releases{/id}","deployments_url":"https://api.github.com/repos/statsmodels/statsmodels/deployments","created_at":"2011-06-12T17:04:50Z","updated_at":"2018-10-22T12:57:21Z","pushed_at":"2018-10-22T19:40:27Z","git_url":"git://github.com/statsmodels/statsmodels.git","ssh_url":"git@github.com:statsmodels/statsmodels.git","clone_url":"https://github.com/statsmodels/statsmodels.git","svn_url":"https://github.com/statsmodels/statsmodels","homepage":"http://www.statsmodels.org/devel/","size":30456,"stargazers_count":3317,"watchers_count":3317,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1406,"mirror_url":null,"archived":false,"open_issues_count":2058,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":1406,"open_issues":2058,"watchers":3317,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296"},"html":{"href":"https://github.com/statsmodels/statsmodels/pull/5296"},"issue":{"href":"https://api.github.com/repos/statsmodels/statsmodels/issues/5296"},"comments":{"href":"https://api.github.com/repos/statsmodels/statsmodels/issues/5296/comments"},"review_comments":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296/comments"},"review_comment":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/statsmodels/statsmodels/pulls/5296/commits"},"statuses":{"href":"https://api.github.com/repos/statsmodels/statsmodels/statuses/73a7031da3887fc9f1ea6c4bc265242e7422b224"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"josef-pkt"}}