Source code for pystra.distributions.gev
import numpy as np
from scipy.stats import genextreme
from scipy.special import gamma
from pystra.distributions import Distribution
[docs]class GEVmax(Distribution):
"""Generalized Extreme Value (GEV) distribution for maxima.
This distribution unifies the different types of extreme value distributions: Gumbel (Type I), Fréchet (Type II), and Weibull (Type III).
:Arguments:
- name (str): Name of the random variable\n
- mean (float): Mean\n
- stdv (float): Standard deviation\n
- shape (float): Shape parameter. shape < 0.0 is Weibull, shape > 0 is Frechet.\n
- input_type (any): Change meaning of mean and stdv\n
- startpoint (float): Start point for seach\n
:Raises:
- ValueError: If `shape` is greater than or equal to 0.5
:Notes:
- The shape parameter `shape` must be less than 0.5 for finite variance.
- `shape` < 0 is the Weibull case, `shape` = 0 is the Gumbel case, and `shape` > 0 is the Fréchet case.
- This distribution is to model maxima.
"""
def __init__(self, name, mean, stdv, shape, input_type=None, startpoint=None):
if shape >= 0.5:
raise ValueError("`shape` must be less than 0.5 for finite variance")
g1 = gamma(1 - shape)
g2 = gamma(1 - 2 * shape)
if input_type is None:
if np.isclose(shape, 0):
scale = stdv * np.sqrt(6) / np.pi
loc = mean - scale * np.euler_gamma
else:
scale = stdv * np.abs(shape) / np.sqrt(g2 - g1**2)
loc = mean - scale / shape * (g1 - 1)
else:
loc = mean
scale = stdv
if np.isclose(shape, 0):
self.mean = loc + scale * np.euler_gamma
self.stdv = scale * np.pi / np.sqrt(6)
else:
self.mean = loc + (g1 - 1) * scale / shape
self.stdv = np.sqrt((g2 - g1**2) * (scale / shape) ** 2)
# use scipy to do the heavy lifting
self.dist_obj = genextreme(c=-shape, loc=loc, scale=scale)
super().__init__(
name=name,
dist_obj=self.dist_obj,
startpoint=startpoint,
)
self.dist_type = "GEVmax"
[docs]class GEVmin(Distribution):
"""Generalized Extreme Value (GEV) distribution for minima.
This distribution unifies the different types of extreme value distributions: Gumbel (Type I), Fréchet (Type II), and Weibull (Type III).
:Arguments:
- name (str): Name of the random variable\n
- mean (float): Mean\n
- stdv (float): Standard deviation\n
- shape (float): Shape parameter. shape < 0.0 is Weibull, shape > 0 is Frechet.\n
- input_type (any): Change meaning of mean and stdv\n
- startpoint (float): Start point for seach\n
:Raises:
- ValueError: If `shape` is greater than or equal to 0.5
:Notes:
- The shape parameter `shape` must be less than 0.5 for finite variance.
- `shape` < 0 is the Weibull case, `shape` = 0 is the Gumbel case, and `shape` > 0 is the Fréchet case.
- This distribution is to model minima.
"""
def __init__(self, name, mean, stdv, shape, input_type=None, startpoint=None):
if shape >= 0.5:
raise ValueError("`shape` must be less than 0.5 for finite variance")
g1 = gamma(1 - shape)
g2 = gamma(1 - 2 * shape)
if input_type is None:
# mean and stdv passed in
self.mean = mean
self.stdv = stdv
if np.isclose(shape, 0):
scale = self.stdv * np.sqrt(6) / np.pi
loc = self.mean - scale * np.euler_gamma
else:
scale = self.stdv * np.abs(shape) / np.sqrt(g2 - g1**2)
loc = self.mean - (scale / shape) * (g1 - 1)
else:
# loc and scale are actual GEV parameters
loc = mean
scale = stdv
if np.isclose(shape, 0):
self.mean = loc + scale * np.euler_gamma
self.stdv = scale * np.pi / np.sqrt(6)
else:
self.mean = loc + (g1 - 1) * scale / shape
self.stdv = np.sqrt((g2 - g1**2) * (scale / shape) ** 2)
# use scipy to do the heavy lifting; note reverse shape sign convention
self.dist_obj = genextreme(c=-shape, loc=-loc, scale=scale)
super().__init__(
name=name,
dist_obj=self.dist_obj,
startpoint=startpoint,
)
self.dist_type = "GEVmin"
[docs] def pdf(self, x):
"""
Probability density function
"""
return self.dist_obj.pdf(-x)
[docs] def cdf(self, x):
"""
Cumulative distribution function
"""
return 1 - self.dist_obj.cdf(-x)
[docs] def ppf(self, u):
"""
Inverse cumulative distribution function
"""
x = self.dist_obj.ppf(u)
return -x