Skip to content

Commit 5faed11

Browse files
authored
Added Weibull Distribution (#386)
feat: added Weibull distribution
1 parent fcf77c9 commit 5faed11

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

ngboost/distns/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .normal import Normal, NormalFixedMean, NormalFixedVar
1111
from .poisson import Poisson
1212
from .t import T, TFixedDf, TFixedDfFixedVar
13+
from .weibull import Weibull
1314

1415
__all__ = [
1516
"Bernoulli",
@@ -30,4 +31,5 @@
3031
"T",
3132
"TFixedDf",
3233
"TFixedDfFixedVar",
34+
"Weibull",
3335
]

ngboost/distns/weibull.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""The NGBoost Weibull distribution and scores"""
2+
import numpy as np
3+
from scipy.stats import weibull_min as dist
4+
5+
from ngboost.distns.distn import RegressionDistn
6+
from ngboost.scores import LogScore
7+
8+
9+
class WeibullLogScore(LogScore):
10+
def score(self, Y):
11+
return -self.dist.logpdf(Y)
12+
13+
def d_score(self, Y):
14+
D = np.zeros((len(Y), 2))
15+
shared_term = self.shape * ((Y / self.scale) ** self.shape - 1)
16+
D[:, 0] = shared_term * np.log(Y / self.scale) - 1
17+
D[:, 1] = -shared_term
18+
19+
return D
20+
21+
def metric(self):
22+
gamma = 0.5772156649 # Euler's constant
23+
FI = np.zeros((self.scale.shape[0], 2, 2))
24+
FI[:, 0, 0] = (np.pi**2 / 6) + (1 - gamma) ** 2
25+
FI[:, 1, 0] = -self.shape * (1 - gamma)
26+
FI[:, 0, 1] = FI[:, 1, 0]
27+
FI[:, 1, 1] = self.shape**2
28+
29+
return FI
30+
31+
32+
class Weibull(RegressionDistn):
33+
"""
34+
Implements the Weibull distribution for NGBoost.
35+
36+
The Weibull distribution has two parameters, shape and scale.
37+
The scipy loc parameter is held constant for this implementation.
38+
LogScore is supported for the Weibull distribution.
39+
"""
40+
41+
n_params = 2
42+
scores = [WeibullLogScore]
43+
44+
# pylint: disable=super-init-not-called
45+
def __init__(self, params):
46+
self._params = params
47+
self.shape = np.exp(params[0]) # shape (c)
48+
self.scale = np.exp(params[1]) # scale (labmda)
49+
self.dist = dist(c=self.shape, loc=0, scale=self.scale)
50+
51+
def fit(Y):
52+
shape, _loc, scale = dist.fit(Y, floc=0) # hold loc constant
53+
return np.array([np.log(shape), np.log(scale)])
54+
55+
def sample(self, m):
56+
return np.array([self.dist.rvs() for i in range(m)])
57+
58+
def __getattr__(self, name):
59+
if name in dir(self.dist):
60+
return getattr(self.dist, name)
61+
return None
62+
63+
@property
64+
def params(self):
65+
return {"shape": self.shape, "scale": self.scale}

tests/test_distns.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
T,
2222
TFixedDf,
2323
TFixedDfFixedVar,
24+
Weibull,
2425
k_categorical,
2526
)
2627
from ngboost.scores import CRPScore, LogScore, Score
@@ -74,6 +75,7 @@ def is_t_distribution(
7475
TFixedDf,
7576
TFixedDfFixedVar,
7677
Cauchy,
78+
Weibull,
7779
],
7880
)
7981
@pytest.mark.parametrize(

tests/test_score.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
T,
1717
TFixedDf,
1818
TFixedDfFixedVar,
19+
Weibull,
1920
)
2021
from ngboost.manifold import manifold
2122
from ngboost.scores import CRPScore, LogScore, Score
@@ -100,6 +101,7 @@ def idfn(dist_score: DistScore):
100101
(Laplace, LogScore),
101102
(Poisson, LogScore),
102103
(Gamma, LogScore),
104+
(Weibull, LogScore),
103105
] + [(MultivariateNormal(i), LogScore) for i in range(2, 5)]
104106
# Fill in the dist, score pair to test the gradient
105107
# Tests all in TEST_METRIC by default

0 commit comments

Comments
 (0)