Source code for pystra.distributions.maximum
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
import scipy.optimize as opt
from .distribution import Distribution
[docs]class Maximum(Distribution):
"""Distribution of maximima from the passed in parent distribution
:Attributes:
- name (str): Name of the random variable\n
- mean (float): Mean\n
- stdv (float): Standard deviation\n
- parent (Distribution): Parent distribution object
- N (float): Power to which distribution is raised
- input_type (any): Change meaning of mean and stdv\n
- startpoint (float): Start point for seach\n
"""
def __init__(self, name, parent, N, input_type=None, startpoint=None):
if not isinstance(parent, Distribution):
raise Exception(
f"Maximum parent requires input of type {type(Distribution)}"
)
if N < 1.0:
raise Exception("Maximum exponent must be >= 1.0")
self.parent = parent
self.N = N
m, s = self._get_stats()
super().__init__(
name=name,
mean=m,
stdv=s,
startpoint=startpoint,
)
self.dist_type = "Maximum"
[docs] def pdf(self, x):
"""
Probability density function
"""
pdf = self.parent.pdf(x)
cdf = 1.0
if self.N > 1.0:
cdf = self.parent.cdf(x)
p = self.N * pdf * cdf ** (self.N - 1)
return p
[docs] def cdf(self, x):
"""
Cumulative distribution function
"""
P = (self.parent.cdf(x)) ** self.N
return P
[docs] def ppf(self, p):
"""
inverse cumulative distribution function
"""
p = np.atleast_1d(p)
x = np.zeros_like(p)
x0 = self.parent.mean
for i, p_val in enumerate(p):
par = opt.fmin(self.zero_distn, x0, args=(p_val,), disp=False)
x[i] = par[0]
return x
[docs] def u_to_x(self, u):
"""
Transformation from u to x
"""
p = self.std_normal.cdf(u)
x = self.ppf(p)
return x
[docs] def x_to_u(self, x):
"""
Transformation from x to u
"""
u = self.std_normal.ppf(self.cdf(x))
return u
[docs] def jacobian(self, u, x):
"""
Compute the Jacobian (e.g. Lemaire, eq. 4.9)
"""
pdf1 = self.pdf(x)
pdf2 = self.std_normal.pdf(u)
J = np.diag(pdf1 / pdf2)
return J
def _get_stats(self):
"""
Since the closed form expression of mean and stdv for the distribution of the
maxima from a parent distribution is complex, and since we really only need
them for default starting points, just estimate through simulation.
"""
p = np.random.random(100)
x = self.ppf(p)
mean = x.mean()
stdv = x.std()
return mean, stdv
[docs] def set_location(self, loc=0):
"""
Updating the parent distribution location parameter.
"""
self.parent.set_location(loc)
self.update_stats()
[docs] def set_scale(self, scale=1):
"""
Updating the parent distribution scale parameter.
"""
self.parent.set_scale(scale)
self.update_stats()
[docs] def update_stats(self):
"""
Updates the mean and stdv estimates - used for sensitivity analysis
where the parent distribution params may change after instantiation
"""
m, s = self._get_stats()
self.mean = m
self.stdv = s
def zero_distn(self, x, *args):
p = args
cdf = self.cdf(x)
zero = np.absolute(cdf - p)
return zero