Skip to content

Commit 7d8a5c5

Browse files
committed
Add continuous class for Generalized Pareto distribution.
1 parent 6c078e8 commit 7d8a5c5

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

pymc_experimental/distributions/continuous.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,158 @@ def moment(rv, size, mu, sigma, xi):
221221
return mode
222222

223223

224+
# Generalized Pareto Distribution
225+
class GenParetoRV(RandomVariable):
226+
name: str = "Generalized Pareto Distribution"
227+
ndim_supp: int = 0
228+
ndims_params: List[int] = [0, 0, 0]
229+
dtype: str = "floatX"
230+
_print_name: Tuple[str, str] = ("Generalized Pareto Distribution", "\\operatorname{GP}")
231+
232+
def __call__(self, mu=0.0, sigma=1.0, xi=1.0, size=None, **kwargs) -> TensorVariable:
233+
return super().__call__(mu, sigma, xi, size=size, **kwargs)
234+
235+
@classmethod
236+
def rng_fn(
237+
cls,
238+
rng: Union[np.random.RandomState, np.random.Generator],
239+
mu: np.ndarray,
240+
sigma: np.ndarray,
241+
xi: np.ndarray,
242+
size: Tuple[int, ...],
243+
) -> np.ndarray:
244+
# using scipy's parameterization
245+
return stats.genpareto.rvs(c=xi, loc=mu, scale=sigma, random_state=rng, size=size)
246+
247+
248+
gp = GenParetoRV()
249+
250+
251+
class GenPareto(Continuous):
252+
r"""
253+
The Generalized Pareto Distribution
254+
255+
The pdf of this distribution is
256+
257+
.. math::
258+
259+
f(x \mid \mu, \sigma, \xi) = \frac{1}{\sigma} (1 + \xi z)^{-1/\xi-1}
260+
261+
where
262+
263+
.. math::
264+
265+
z = \frac{x - \mu}{\sigma}
266+
267+
and is defined on the set:
268+
269+
.. math::
270+
271+
\left\{x: x \geq \mu \right\} if \xi \geq 0, or \\
272+
\left\{x: \mu \leq x \leq \mu - \sigma/\xi \right\} if \xi < 0.
273+
274+
.. plot::
275+
276+
import matplotlib.pyplot as plt
277+
import numpy as np
278+
import scipy.stats as st
279+
import arviz as az
280+
plt.style.use('arviz-darkgrid')
281+
x = np.linspace(-2, 10, 200)
282+
mus = [0., 0., 0., 0., 1., ]
283+
sigmas = [1., 1., 1.,2., 1., ]
284+
xis = [1., 0., 5., 1., 1., ]
285+
for mu, sigma, xi in zip(mus, sigmas, xis):
286+
pdf = st.genpareto.pdf(x, c=xi, loc=mu, scale=sigma)
287+
plt.plot(x, pdf, label=rf'$\mu$ = {mu}, $\sigma$ = {sigma}, $\xi$={xi}')
288+
plt.xlabel('x', fontsize=12)
289+
plt.ylabel('f(x)', fontsize=12)
290+
plt.legend(loc=1)
291+
plt.show()
292+
293+
294+
======== =========================================================================
295+
Support * :math:`x \geq \mu`, when :math:`\xi \geq 0`
296+
* :math:`\mu \leq x \leq \mu - \sigma/\xi` when :math:`\xi < 0`
297+
Mean * :math:`\mu + \frac{\sigma}{1-\xi}`, when :math:`\xi < 1`
298+
Variance * :math:`\frac{\sigma^2}{(1-\xi)^2 (1-2\xi)}`, when :math:`\xi < 0.5`
299+
======== =========================================================================
300+
301+
Parameters
302+
----------
303+
mu : float
304+
Location parameter.
305+
sigma : float
306+
Scale parameter (sigma > 0).
307+
xi : float
308+
Shape parameter
309+
310+
"""
311+
312+
rv_op = gp
313+
314+
@classmethod
315+
def dist(cls, mu=0, sigma=1, xi=0, **kwargs):
316+
mu = pt.as_tensor_variable(floatX(mu))
317+
sigma = pt.as_tensor_variable(floatX(sigma))
318+
xi = pt.as_tensor_variable(floatX(xi))
319+
320+
return super().dist([mu, sigma, xi], **kwargs)
321+
322+
def logp(value, mu, sigma, xi):
323+
"""
324+
Calculate log-probability of Generalized Pareto distribution
325+
at specified value.
326+
327+
Parameters
328+
----------
329+
value: numeric
330+
Value(s) for which log-probability is calculated. If the log probabilities for multiple
331+
values are desired the values must be provided in a numpy array or Pytensor tensor
332+
333+
Returns
334+
-------
335+
TensorVariable
336+
"""
337+
scaled = (value - mu) / sigma
338+
p = (1 / sigma) * (1 + xi * scaled) ** (-1 / xi - 1)
339+
logprob = pt.switch(pt.gt(scaled, 0.0), pt.log(p), -np.inf)
340+
return check_parameters(logprob, sigma > 0, msg="sigma > 0")
341+
342+
def logcdf(value, mu, sigma, xi):
343+
"""
344+
Compute the log of the cumulative distribution function for Generalized Pareto
345+
distribution at the specified value.
346+
347+
Parameters
348+
----------
349+
value: numeric or np.ndarray or `TensorVariable`
350+
Value(s) for which log CDF is calculated. If the log CDF for
351+
multiple values are desired the values must be provided in a numpy
352+
array or `TensorVariable`.
353+
354+
Returns
355+
-------
356+
TensorVariable
357+
"""
358+
scaled = (value - mu) / sigma
359+
logc_expression = 1 - (1 + xi * scaled) ** (-1 / xi)
360+
361+
logc = pt.switch(sigma > 0, pt.log(logc_expression), -np.inf)
362+
363+
return check_parameters(logc, sigma > 0, msg="sigma > 0")
364+
365+
def moment(rv, size, mu, sigma, xi):
366+
r"""
367+
Mean is defined when :math:`\xi < 1`
368+
"""
369+
mean_expression = mu + sigma / (1 - xi)
370+
mean = pt.switch(xi < 1, mean_expression, np.inf)
371+
if not rv_size_is_none(size):
372+
mean = pt.full(size, mean)
373+
return check_parameters(mean, xi < 1, msg="xi < 1")
374+
375+
224376
class Chi:
225377
r"""
226378
:math:`\chi` log-likelihood.

0 commit comments

Comments
 (0)