AIM-PIbd-32-Kurbanova-A-A/aimenv/Lib/site-packages/statsmodels/distributions/copula/depfunc_ev.py
2024-10-02 22:15:59 +04:00

329 lines
9.0 KiB
Python

""" Pickand's dependence functions as generators for EV-copulas
Created on Wed Jan 27 14:33:40 2021
Author: Josef Perktold
License: BSD-3
"""
import numpy as np
from scipy import stats
from statsmodels.tools.numdiff import _approx_fprime_cs_scalar, approx_hess
class PickandDependence:
def __call__(self, *args, **kwargs):
return self.evaluate(*args, **kwargs)
def evaluate(self, t, *args):
raise NotImplementedError
def deriv(self, t, *args):
"""First derivative of the dependence function
implemented through numerical differentiation
"""
t = np.atleast_1d(t)
return _approx_fprime_cs_scalar(t, self.evaluate)
def deriv2(self, t, *args):
"""Second derivative of the dependence function
implemented through numerical differentiation
"""
if np.size(t) == 1:
d2 = approx_hess([t], self.evaluate, args=args)[0]
else:
d2 = np.array([approx_hess([ti], self.evaluate, args=args)[0, 0]
for ti in t])
return d2
class AsymLogistic(PickandDependence):
'''asymmetric logistic model of Tawn 1988
special case: a1=a2=1 : Gumbel
restrictions:
- theta in (0,1]
- a1, a2 in [0,1]
'''
k_args = 3
def _check_args(self, a1, a2, theta):
condth = (theta > 0) and (theta <= 1)
conda1 = (a1 >= 0) and (a1 <= 1)
conda2 = (a2 >= 0) and (a2 <= 1)
return condth and conda1 and conda2
def evaluate(self, t, a1, a2, theta):
# if not np.all(_check_args(a1, a2, theta)):
# raise ValueError('invalid args')
transf = (1 - a2) * (1-t)
transf += (1 - a1) * t
transf += ((a1 * t)**(1./theta) + (a2 * (1-t))**(1./theta))**theta
return transf
def deriv(self, t, a1, a2, theta):
b = theta
d1 = ((a1 * (a1 * t)**(1/b - 1) - a2 * (a2 * (1 - t))**(1/b - 1)) *
((a1 * t)**(1/b) + (a2 * (1 - t))**(1/b))**(b - 1) - a1 + a2)
return d1
def deriv2(self, t, a1, a2, theta):
b = theta
d2 = ((1 - b) * (a1 * t)**(1/b) * (a2 * (1 - t))**(1/b) *
((a1 * t)**(1/b) + (a2 * (1 - t))**(1/b))**(b - 2)
)/(b * (1 - t)**2 * t**2)
return d2
transform_tawn = AsymLogistic()
class AsymNegLogistic(PickandDependence):
'''asymmetric negative logistic model of Joe 1990
special case: a1=a2=1 : symmetric negative logistic of Galambos 1978
restrictions:
- theta in (0,inf)
- a1, a2 in (0,1]
'''
k_args = 3
def _check_args(self, a1, a2, theta):
condth = (theta > 0)
conda1 = (a1 > 0) and (a1 <= 1)
conda2 = (a2 > 0) and (a2 <= 1)
return condth and conda1 and conda2
def evaluate(self, t, a1, a2, theta):
# if not np.all(self._check_args(a1, a2, theta)):
# raise ValueError('invalid args')
a1, a2 = a2, a1
transf = 1 - ((a1 * (1-t))**(-1./theta) +
(a2 * t)**(-1./theta))**(-theta)
return transf
def deriv(self, t, a1, a2, theta):
a1, a2 = a2, a1
m1 = -1 / theta
m2 = m1 - 1
# (a1^(-1/θ) (1 - t)^(-1/θ - 1) - a2^(-1/θ) t^(-1/θ - 1))*
# (a1^(-1/θ) (1 - t)^(-1/θ) + (a2 t)^(-1/θ))^(-θ - 1)
d1 = (a1**m1 * (1 - t)**m2 - a2**m1 * t**m2) * (
(a1 * (1 - t))**m1 + (a2 * t)**m1)**(-theta - 1)
return d1
def deriv2(self, t, a1, a2, theta):
b = theta
a1, a2 = a2, a1
a1tp = (a1 * (1 - t))**(1/b)
a2tp = (a2 * t)**(1/b)
a1tn = (a1 * (1 - t))**(-1/b)
a2tn = (a2 * t)**(-1/b)
t1 = (b + 1) * a2tp * a1tp * (a1tn + a2tn)**(-b)
t2 = b * (1 - t)**2 * t**2 * (a1tp + a2tp)**2
d2 = t1 / t2
return d2
transform_joe = AsymNegLogistic()
class AsymMixed(PickandDependence):
'''asymmetric mixed model of Tawn 1988
special case: k=0, theta in [0,1] : symmetric mixed model of
Tiago de Oliveira 1980
restrictions:
- theta > 0
- theta + 3*k > 0
- theta + k <= 1
- theta + 2*k <= 1
'''
k_args = 2
def _check_args(self, theta, k):
condth = (theta >= 0)
cond1 = (theta + 3*k > 0) and (theta + k <= 1) and (theta + 2*k <= 1)
return condth & cond1
def evaluate(self, t, theta, k):
transf = 1 - (theta + k) * t + theta * t*t + k * t**3
return transf
def deriv(self, t, theta, k):
d_dt = - (theta + k) + 2 * theta * t + 3 * k * t**2
return d_dt
def deriv2(self, t, theta, k):
d2_dt2 = 2 * theta + 6 * k * t
return d2_dt2
# backwards compatibility for now
transform_tawn2 = AsymMixed()
class AsymBiLogistic(PickandDependence):
'''bilogistic model of Coles and Tawn 1994, Joe, Smith and Weissman 1992
restrictions:
- (beta, delta) in (0,1)^2 or
- (beta, delta) in (-inf,0)^2
not vectorized because of numerical integration
'''
k_args = 2
def _check_args(self, beta, delta):
cond1 = (beta > 0) and (beta <= 1) and (delta > 0) and (delta <= 1)
cond2 = (beta < 0) and (delta < 0)
return cond1 | cond2
def evaluate(self, t, beta, delta):
# if not np.all(_check_args(beta, delta)):
# raise ValueError('invalid args')
def _integrant(w):
term1 = (1 - beta) * np.power(w, -beta) * (1-t)
term2 = (1 - delta) * np.power(1-w, -delta) * t
return np.maximum(term1, term2)
from scipy.integrate import quad
transf = quad(_integrant, 0, 1)[0]
return transf
transform_bilogistic = AsymBiLogistic()
class HR(PickandDependence):
'''model of Huesler Reiss 1989
special case: a1=a2=1 : symmetric negative logistic of Galambos 1978
restrictions:
- lambda in (0,inf)
'''
k_args = 1
def _check_args(self, lamda):
cond = (lamda > 0)
return cond
def evaluate(self, t, lamda):
# if not np.all(self._check_args(lamda)):
# raise ValueError('invalid args')
term = np.log((1. - t) / t) * 0.5 / lamda
from scipy.stats import norm
# use special if I want to avoid stats import
transf = ((1 - t) * norm._cdf(lamda + term) +
t * norm._cdf(lamda - term))
return transf
def _derivs(self, t, lamda, order=(1, 2)):
if not isinstance(order, (int, np.integer)):
if (1 in order) and (2 in order):
order = -1
else:
raise ValueError("order should be 1, 2, or (1,2)")
dn = 1 / np.sqrt(2 * np.pi)
a = lamda
g = np.log((1. - t) / t) * 0.5 / a
gd1 = 1 / (2 * a * (t - 1) * t)
gd2 = (0.5 - t) / (a * ((1 - t) * t)**2)
# f = stats.norm.cdf(t)
# fd1 = np.exp(-t**2 / 2) / sqrt(2 * np.pi) # stats.norm.pdf(t)
# fd2 = fd1 * t
tp = a + g
fp = stats.norm.cdf(tp)
fd1p = np.exp(-tp**2 / 2) * dn # stats.norm.pdf(t)
fd2p = -fd1p * tp
tn = a - g
fn = stats.norm.cdf(tn)
fd1n = np.exp(-tn**2 / 2) * dn # stats.norm.pdf(t)
fd2n = -fd1n * tn
if order in (1, -1):
# d1 = g'(t) (-t f'(a - g(t)) - (t - 1) f'(a + g(t))) + f(a - g(t))
# - f(a + g(t))
d1 = gd1 * (-t * fd1n - (t - 1) * fd1p) + fn - fp
if order in (2, -1):
# d2 = g'(t)^2 (t f''(a - g(t)) - (t - 1) f''(a + g(t))) +
# (-(t - 1) g''(t) - 2 g'(t)) f'(a + g(t)) -
# (t g''(t) + 2 g'(t)) f'(a - g(t))
d2 = (gd1**2 * (t * fd2n - (t - 1) * fd2p) +
(-(t - 1) * gd2 - 2 * gd1) * fd1p -
(t * gd2 + 2 * gd1) * fd1n
)
if order == 1:
return d1
elif order == 2:
return d2
elif order == -1:
return (d1, d2)
def deriv(self, t, lamda):
return self._derivs(t, lamda, 1)
def deriv2(self, t, lamda):
return self._derivs(t, lamda, 2)
transform_hr = HR()
# def transform_tev(t, rho, df):
class TEV(PickandDependence):
'''t-EV model of Demarta and McNeil 2005
restrictions:
- rho in (-1,1)
- x > 0
'''
k_args = 2
def _check_args(self, rho, df):
x = df # alias, Genest and Segers use chi, copual package uses df
cond1 = (x > 0)
cond2 = (rho > 0) and (rho < 1)
return cond1 and cond2
def evaluate(self, t, rho, df):
x = df # alias, Genest and Segers use chi, copual package uses df
# if not np.all(self, _check_args(rho, x)):
# raise ValueError('invalid args')
from scipy.stats import t as stats_t
# use special if I want to avoid stats import
term1 = (np.power(t/(1.-t), 1./x) - rho) # for t
term2 = (np.power((1.-t)/t, 1./x) - rho) # for 1-t
term0 = np.sqrt(1. + x) / np.sqrt(1 - rho*rho)
z1 = term0 * term1
z2 = term0 * term2
transf = t * stats_t._cdf(z1, x+1) + (1 - t) * stats_t._cdf(z2, x+1)
return transf
transform_tev = TEV()