Skip to content

Commit 0b2b9b2

Browse files
committed
Add (log)uniform
1 parent 0c647b4 commit 0b2b9b2

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,4 @@ testpaths = [
9999

100100
[tool.ruff.lint.per-file-ignores]
101101
"**/__init__.py" = ["F403", "F405"]
102+
"**/normal.py" = ["F821"]

src/skstats/log_uniform.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import numpy as np
2+
from numpy import inf
3+
4+
from scipy import special
5+
from scipy.stats._distribution_infrastructure import (
6+
ContinuousDistribution,
7+
_RealDomain,
8+
_RealParameter,
9+
_Parameterization,
10+
)
11+
12+
13+
def _log_diff(log_p, log_q):
14+
return special.logsumexp([log_p, log_q + np.pi * 1j], axis=0)
15+
16+
17+
class _LogUniform(ContinuousDistribution):
18+
r"""Log-uniform distribution.
19+
20+
The probability density function of the log-uniform distribution is:
21+
22+
.. math::
23+
24+
f(x; a, b) = \frac{1}
25+
{x (\log(b) - \log(a))}
26+
27+
If :math:`\log(X)` is a random variable that follows a uniform distribution
28+
between :math:`\log(a)` and :math:`\log(b)`, then :math:`X` is log-uniformly
29+
distributed with shape parameters :math:`a` and :math:`b`.
30+
31+
"""
32+
33+
_a_domain = _RealDomain(endpoints=(0, inf))
34+
_b_domain = _RealDomain(endpoints=("a", inf))
35+
_log_a_domain = _RealDomain(endpoints=(-inf, inf))
36+
_log_b_domain = _RealDomain(endpoints=("log_a", inf))
37+
_x_support = _RealDomain(endpoints=("a", "b"), inclusive=(True, True))
38+
39+
_a_param = _RealParameter("a", domain=_a_domain, typical=(1e-3, 0.9))
40+
_b_param = _RealParameter("b", domain=_b_domain, typical=(1.1, 1e3))
41+
_log_a_param = _RealParameter(
42+
"log_a", symbol=r"\log(a)", domain=_log_a_domain, typical=(-3, -0.1)
43+
)
44+
_log_b_param = _RealParameter(
45+
"log_b", symbol=r"\log(b)", domain=_log_b_domain, typical=(0.1, 3)
46+
)
47+
_x_param = _RealParameter("x", domain=_x_support, typical=("a", "b"))
48+
49+
_b_domain.define_parameters(_a_param)
50+
_log_b_domain.define_parameters(_log_a_param)
51+
_x_support.define_parameters(_a_param, _b_param)
52+
53+
_parameterizations = [
54+
_Parameterization(_log_a_param, _log_b_param),
55+
_Parameterization(_a_param, _b_param),
56+
]
57+
_variable = _x_param
58+
59+
def __init__(self, *, a=None, b=None, log_a=None, log_b=None, **kwargs):
60+
super().__init__(a=a, b=b, log_a=log_a, log_b=log_b, **kwargs)
61+
62+
def _process_parameters(self, a=None, b=None, log_a=None, log_b=None, **kwargs):
63+
a = np.exp(log_a) if a is None else a
64+
b = np.exp(log_b) if b is None else b
65+
log_a = np.log(a) if log_a is None else log_a
66+
log_b = np.log(b) if log_b is None else log_b
67+
kwargs.update(dict(a=a, b=b, log_a=log_a, log_b=log_b))
68+
return kwargs
69+
70+
# def _logpdf_formula(self, x, *, log_a, log_b, **kwargs):
71+
# return -np.log(x) - np.log(log_b - log_a)
72+
73+
def _pdf_formula(self, x, *, log_a, log_b, **kwargs):
74+
return ((log_b - log_a) * x) ** -1
75+
76+
# def _cdf_formula(self, x, *, log_a, log_b, **kwargs):
77+
# return (np.log(x) - log_a)/(log_b - log_a)
78+
79+
def _moment_raw_formula(self, order, log_a, log_b, **kwargs):
80+
if order == 0:
81+
return self._one
82+
t1 = self._one / (log_b - log_a) / order
83+
t2 = np.real(np.exp(_log_diff(order * log_b, order * log_a)))
84+
return t1 * t2

src/skstats/uniform.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import numpy as np
2+
from numpy import inf
3+
4+
from scipy.stats._distribution_infrastructure import (
5+
ContinuousDistribution,
6+
_RealDomain,
7+
_RealParameter,
8+
_Parameterization,
9+
)
10+
11+
12+
class Uniform(ContinuousDistribution):
13+
r"""Uniform distribution.
14+
15+
The probability density function of the uniform distribution is:
16+
17+
.. math::
18+
19+
f(x; a, b) = \frac{1}
20+
{b - a}
21+
22+
"""
23+
24+
_a_domain = _RealDomain(endpoints=(-inf, inf))
25+
_b_domain = _RealDomain(endpoints=("a", inf))
26+
_x_support = _RealDomain(endpoints=("a", "b"), inclusive=(False, False))
27+
28+
_a_param = _RealParameter("a", domain=_a_domain, typical=(1e-3, 0.9))
29+
_b_param = _RealParameter("b", domain=_b_domain, typical=(1.1, 1e3))
30+
_x_param = _RealParameter("x", domain=_x_support, typical=("a", "b"))
31+
32+
_b_domain.define_parameters(_a_param)
33+
_x_support.define_parameters(_a_param, _b_param)
34+
35+
_parameterizations = [_Parameterization(_a_param, _b_param)]
36+
_variable = _x_param
37+
38+
def __init__(self, *, a=None, b=None, **kwargs):
39+
super().__init__(a=a, b=b, **kwargs)
40+
41+
def _process_parameters(self, a=None, b=None, ab=None, **kwargs):
42+
ab = b - a
43+
kwargs.update(dict(a=a, b=b, ab=ab))
44+
return kwargs
45+
46+
def _pdf_formula(self, x, *, ab, **kwargs):
47+
return np.full(x.shape, 1 / ab)
48+
49+
def _icdf_formula(self, x, a, b, ab, **kwargs):
50+
return a + ab * x
51+
52+
def _mode_formula(self, *, a, b, ab, **kwargs):
53+
return a + 0.5 * ab

0 commit comments

Comments
 (0)