Skip to content

Commit 1cd58b6

Browse files
authored
Adding NormalFixedMean (#333)
* Implemented NormalFixedMean * Added NormalFixedVar and NormalFixedMean to test_distns.py * Match log(sigma) parameterization of Normal class * Update __init__.py Linter is demanding a blank line for ending a file (sigh)
1 parent f5c2a1e commit 1cd58b6

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

ngboost/distns/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .laplace import Laplace
88
from .lognormal import LogNormal
99
from .multivariate_normal import MultivariateNormal
10-
from .normal import Normal, NormalFixedVar
10+
from .normal import Normal, NormalFixedMean, NormalFixedVar
1111
from .poisson import Poisson
1212
from .t import T, TFixedDf, TFixedDfFixedVar
1313

@@ -24,6 +24,7 @@
2424
"LogNormal",
2525
"MultivariateNormal",
2626
"Normal",
27+
"NormalFixedMean",
2728
"NormalFixedVar",
2829
"Poisson",
2930
"T",

ngboost/distns/normal.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,65 @@ def __init__(self, params):
150150
def fit(Y):
151151
m, _ = sp.stats.norm.fit(Y)
152152
return m
153+
154+
155+
# ### Fixed Mean Normal ###
156+
class NormalFixedMeanLogScore(LogScore):
157+
def score(self, Y):
158+
return -self.dist.logpdf(Y)
159+
160+
def d_score(self, Y):
161+
D = np.zeros((len(Y), 1))
162+
D[:, 0] = 1 - ((self.loc - Y) ** 2) / self.var
163+
return D
164+
165+
def metric(self):
166+
FI = np.zeros((self.var.shape[0], 1, 1))
167+
FI[:, 0, 0] = 2
168+
return FI
169+
170+
171+
class NormalFixedMeanCRPScore(CRPScore):
172+
def score(self, Y):
173+
Z = (Y - self.loc) / self.scale
174+
return self.scale * (
175+
Z * (2 * sp.stats.norm.cdf(Z) - 1)
176+
+ 2 * sp.stats.norm.pdf(Z)
177+
- 1 / np.sqrt(np.pi)
178+
)
179+
180+
def d_score(self, Y):
181+
Z = (Y - self.loc) / self.scale
182+
D = np.zeros((len(Y), 1))
183+
D[:, 0] = self.score(Y) + (Y - self.loc) * -1 * (2 * sp.stats.norm.cdf(Z) - 1)
184+
return D
185+
186+
def metric(self):
187+
I = np.c_[self.var]
188+
I = I.reshape((self.var.shape[0], 1, 1))
189+
I = 1 / (2 * np.sqrt(np.pi)) * I
190+
return I
191+
192+
193+
class NormalFixedMean(Normal):
194+
"""
195+
Implements the normal distribution with mean=0 for NGBoost.
196+
197+
The fixed-mean normal distribution has one parameter, scale which is the standard deviation.
198+
This distribution has both LogScore and CRPScore implemented for it.
199+
"""
200+
201+
n_params = 1
202+
scores = [NormalFixedMeanLogScore, NormalFixedMeanCRPScore]
203+
204+
# pylint: disable=super-init-not-called
205+
def __init__(self, params):
206+
self.loc = np.zeros_like(params[0])
207+
self.scale = np.exp(params[0])
208+
self.var = self.scale**2
209+
self.shape = self.loc.shape
210+
self.dist = dist(loc=self.loc, scale=self.scale)
211+
212+
def fit(Y):
213+
_, s = sp.stats.norm.fit(Y)
214+
return s

tests/test_distns.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
LogNormal,
1717
MultivariateNormal,
1818
Normal,
19+
NormalFixedMean,
20+
NormalFixedVar,
1921
T,
2022
TFixedDf,
2123
TFixedDfFixedVar,
@@ -61,7 +63,18 @@ def is_t_distribution(
6163
@pytest.mark.slow
6264
@pytest.mark.parametrize(
6365
"dist",
64-
[Normal, LogNormal, Exponential, Gamma, T, TFixedDf, TFixedDfFixedVar, Cauchy],
66+
[
67+
Normal,
68+
NormalFixedVar,
69+
NormalFixedMean,
70+
LogNormal,
71+
Exponential,
72+
Gamma,
73+
T,
74+
TFixedDf,
75+
TFixedDfFixedVar,
76+
Cauchy,
77+
],
6578
)
6679
@pytest.mark.parametrize(
6780
"learner",

0 commit comments

Comments
 (0)