Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Replace the deprecated `friedman_mse` criterion with `squared_error` for the default tree learner and distribution tests (issue #408, PR #409)
* Support a distinct base learner for each distribution parameter, including scikit-learn nested parameters and feature importances for all-tree configurations (issue #338, PR #411)
* Fix the `CRPScore` metric of `Normal`, `NormalFixedMean` and `LogNormal`, which was the correct metric multiplied by the scale. It was too large where the predicted scale is above 1 and too small where it is below 1. Fits that use `CRPScore` with these distributions change wherever the predicted scale is not 1 (issue #421)
* Add `df` to the `params` of `T`, `TFixedDf`, `TFixedDfFixedVar` and the `Cauchy` classes built on them, so that `scipy.stats.t(**dist.params)` rebuilds the predicted distribution. Before, `params` held only `loc` and `scale`

## Version 0.5.10

Expand Down
6 changes: 3 additions & 3 deletions ngboost/distns/t.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __getattr__(self, name):

@property
def params(self):
return {"loc": self.loc, "scale": self.scale}
return {"loc": self.loc, "scale": self.scale, "df": self.df}


class TFixedDfLogScore(LogScore):
Expand Down Expand Up @@ -141,7 +141,7 @@ def __getattr__(self, name):

@property
def params(self):
return {"loc": self.loc, "scale": self.scale}
return {"loc": self.loc, "scale": self.scale, "df": self.df}


class TFixedDfFixedVarLogScore(LogScore):
Expand Down Expand Up @@ -199,4 +199,4 @@ def __getattr__(self, name):

@property
def params(self):
return {"loc": self.loc, "scale": self.scale}
return {"loc": self.loc, "scale": self.scale, "df": self.df}
2 changes: 1 addition & 1 deletion notebooks/example_t.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@
"ax = axes[1]\n",
"x = np.linspace(-5, 12, 300)\n",
"for i in [0, 50, 100]:\n",
" pdf = scipy.stats.t.pdf(x, df=dists.df[i], loc=dists.loc[i], scale=dists.scale[i])\n",
" pdf = scipy.stats.t.pdf(x, **dists[i].params)\n",
" ax.plot(x, pdf, label=f\"sample {i}\")\n",
" ax.axvline(Y_test[i], color=ax.lines[-1].get_color(), linestyle=\":\", alpha=0.5)\n",
"ax.set_xlabel(\"y\")\n",
Expand Down
33 changes: 33 additions & 0 deletions tests/test_distns.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import numpy as np
import pytest
import scipy.stats
from sklearn.datasets import fetch_california_housing, load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
Expand All @@ -25,6 +26,7 @@
Weibull,
k_categorical,
)
from ngboost.distns.cauchy import CauchyFixedVar
from ngboost.scores import CRPScore, LogScore, Score

# test all the dist methods and score implementation methods,#
Expand Down Expand Up @@ -224,3 +226,34 @@ def test_multivariatenormal(k: int, learner):
mean = y_dist.mean
sample = y_dist.rv()
scipy_list = y_dist.scipy_distribution()


@pytest.mark.parametrize(
"dist_class, expected_df",
[
(T, None),
(TFixedDf, 3.0),
(TFixedDfFixedVar, 3.0),
(Cauchy, 1.0),
(CauchyFixedVar, 1.0),
],
)
def test_t_family_params_match_scipy(dist_class, expected_df):
rng = np.random.default_rng(0)
n = 20
raw_params = rng.normal(size=(dist_class.n_params, n))
dist = dist_class(raw_params)
params = dist.params

# df is reported next to loc and scale, and loc is the first raw parameter
assert set(params) == {"loc", "scale", "df"}
np.testing.assert_allclose(params["loc"], raw_params[0])
np.testing.assert_allclose(params["df"], dist.df)
if expected_df is not None:
np.testing.assert_allclose(params["df"], expected_df)

# the params are enough to rebuild the same distribution in scipy
y = rng.normal(size=n)
frozen = scipy.stats.t(**params)
np.testing.assert_allclose(frozen.logpdf(y), dist.logpdf(y))
np.testing.assert_allclose(frozen.ppf(0.9), dist.ppf(0.9))
Loading