'''
Which Archimedean is Best?
Extreme Value copulas formulas are based on Genest 2009
References
----------
Genest, C., 2009. Rank-based inference for bivariate extreme-value
copulas. The Annals of Statistics, 37(5), pp.2990-3022.
'''
import numpy as np
from scipy.special import expm1, log1p
[docs]def copula_bv_indep(u,v):
'''independent bivariate copula
'''
return u*v
[docs]def copula_bv_min(u,v):
'''comonotonic bivariate copula
'''
return np.minimum(u, v)
[docs]def copula_bv_max(u, v):
'''countermonotonic bivariate copula
'''
return np.maximum(u + v - 1, 0)
[docs]def copula_bv_clayton(u, v, theta):
'''Clayton or Cook, Johnson bivariate copula
'''
if not theta > 0:
raise ValueError('theta needs to be strictly positive')
return np.power(np.power(u, -theta) + np.power(v, -theta) - 1, -theta)
[docs]def copula_bv_frank(u, v, theta):
'''Cook, Johnson bivariate copula
'''
if not theta > 0:
raise ValueError('theta needs to be strictly positive')
cdfv = -np.log(1 + expm1(-theta*u) * expm1(-theta*v) / expm1(-theta))/theta
cdfv = np.minimum(cdfv, 1) #necessary for example if theta=100
return cdfv
[docs]def copula_bv_gauss(u, v, rho):
raise NotImplementedError
[docs]def copula_bv_t(u, v, rho, df):
raise NotImplementedError
#not used yet
[docs]class TransfFrank(object):
[docs] def evaluate(self, t, theta):
return - (np.log(-expm1(-theta*t)) - np.log(-expm1(-theta)))
#return - np.log(expm1(-theta*t) / expm1(-theta))
[docs] def inverse(self, phi, theta):
return -np.log1p(np.exp(-phi) * expm1(-theta)) / theta
[docs]class TransfClayton(object):
def _checkargs(theta):
return theta > 0
[docs] def evaluate(self, t, theta):
return np.power(t, -theta) - 1.
[docs] def inverse(self, phi, theta):
return np.power(1 + phi, -theta)
[docs]class TransfGumbel(object):
'''
requires theta >=1
'''
def _checkargs(theta):
return theta >= 1
[docs] def evaluate(self, t, theta):
return np.power(-np.log(t), theta)
[docs] def inverse(self, phi, theta):
return np.exp(-np.power(phi, 1. / theta))
[docs]class TransfIndep(object):
[docs] def evaluate(self, t):
return -np.log(t)
[docs] def inverse(self, phi):
return np.exp(-phi)
[docs]def copula_bv_archimedean(u, v, transform, args=()):
'''
'''
phi = transform.evaluate
phi_inv = transform.inverse
cdfv = phi_inv(phi(u, *args) + phi(v, *args), *args)
return cdfv
[docs]def copula_mv_archimedean(u, transform, args=(), axis=-1):
'''generic multivariate Archimedean copula
'''
phi = transform.evaluate
phi_inv = transform.inverse
cdfv = phi_inv(phi(u, *args).sum(axis), *args)
return cdfv
[docs]def copula_bv_ev(u, v, transform, args=()):
'''generic bivariate extreme value copula
'''
return np.exp(np.log(u * v) * (transform(np.log(v)/np.log(u*v), *args)))
#define dictionary of copulas by names and aliases
copulanames = {'indep' : copula_bv_indep,
'i' : copula_bv_indep,
'min' : copula_bv_min,
'max' : copula_bv_max,
'clayton' : copula_bv_clayton,
'cookjohnson' : copula_bv_clayton,
'cj' : copula_bv_clayton,
'frank' : copula_bv_frank,
'gauss' : copula_bv_gauss,
'normal' : copula_bv_gauss,
't' : copula_bv_frank}
[docs]class CopulaBivariate(object):
'''bivariate copula class
Instantiation needs the arguments, cop_args, that are required for copula
'''
[docs] def __init__(self, marginalcdfs, copula, copargs=()):
if copula in copulanames:
self.copula = copulanames[copula]
else:
#see if we can call it as a copula function
try:
tmp = copula(0.5, 0.5, *copargs)
except: #blanket since we throw again
raise ValueError('copula needs to be a copula name or callable')
self.copula = copula
#no checking done on marginals
self.marginalcdfs = marginalcdfs
self.copargs = copargs
[docs] def cdf(self, xy, args=None):
'''xx needs to be iterable, instead of x,y for extension to multivariate
'''
x, y = xy
if args is None:
args = self.copargs
return self.copula(self.marginalcdfs[0](x), self.marginalcdfs[1](y),
*args)