Skip to content

Commit

Permalink
Merge branch 'main' into Refactor-SubsetCopula
Browse files Browse the repository at this point in the history
  • Loading branch information
lrnv authored Sep 21, 2024
2 parents cdd2454 + ac532bf commit a7e1cb4
Show file tree
Hide file tree
Showing 23 changed files with 179 additions and 249 deletions.
19 changes: 19 additions & 0 deletions docs/src/assets/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -845,4 +845,23 @@ @book{mai2014financial
author={Mai, Jan-Frederik and Scherer, Matthias},
year={2014},
publisher={Springer}
}
@article{fang2002meta,
title={The meta-elliptical distributions with given marginals},
author={Fang, Hong-Bin and Fang, Kai-Tai and Kotz, Samuel},
journal={Journal of multivariate analysis},
volume={82},
number={1},
pages={1--16},
year={2002},
publisher={Elsevier}
}
@incollection{lindskog2003kendall,
title={Kendall’s tau for elliptical distributions},
author={Lindskog, Filip and McNeil, Alexander and Schmock, Uwe},
booktitle={Credit risk: Measurement, evaluation and management},
pages={149--156},
year={2003},
publisher={Springer}
}
42 changes: 19 additions & 23 deletions docs/src/dependence_measures.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
# Measures of dependency


Although the copula is an object that summarizes completely the dependence structure of any random vector, it is an infinite dimensional object and the interpretation of its properties can be difficult when the dimension gets high. Therefore, the literature has come up with some quantification of the dependence structure that might be used as univariate summaries, of course imperfect, of certain properties of the copula at hand.
Although Copulas summarize completely the dependence structure of corresponding random vector, it is an infinite dimensional object and the interpretation of its properties can be difficult when the dimension gets high. Therefore, the literature has come up with some quantification of the dependence structure that might be used as univariate summaries, of course imperfect, of certain properties of the copula at hand.

## Kendall's τ and Spearman's ρ: bivariate and multivariate cases

!!! note "Unfinished work"
Unfortunately these dependence measures are not yet well-specified in the package and their implementation is experimental for the moment. These functions might change in the future, in particular see https://github.com/lrnv/Copulas.jl/issues/134 for future improvements.


## Kendall's Tau

> **Definition (Kendall' τ):** For a copula $C$ with a density $c$, Kendall's τ is defined as:
> **Definition (Kendall' τ):** For a copula $C$ with a density $c$, **whatever its dimension $d$**, its Kendall's τ is defined as:
>
>$$\tau = 4 \int C(\bm u) \, c(\bm u) \;d\bm u -1$$
Kendall's tau can be obtained through `τ(C::Copula)`. Its value only depends on the dependence structure and not the marginals.
> **Definition (Spearman's ρ):** For a copula $C$ with a density $c$, **whatever its dimension $d$**, the Spearman's ρ is defined as:
>
> $$\rho = 12 \int C(\bm u) d\bm u -3.$$
!!! warn "Multivariate case"
There exists several multivariate extensions of Kendall's tau. The one implemented here is the one we just defined what ever the dimension $d$, be careful as the normalization might differ from other places in the literature.
These two dependence measures make more sense in the bivariate case than in other cases, and therefore we sometimes refer to the Kendall's matrix or the Spearman's matrix for the collection of bivariate coefficients associated to a multivariate copula. We thus provide two different interfaces, one internal through `Copulas.τ(C::Copula)` and `Copulas.ρ(C::Copula)`, providing true multivariate Kendall taus and Spearman rhos, and one implementing `StatsBase.corkendall(C::Copula)` and `StatsBase.corspearman(C::Copula)` for matrices of bivariate dependence measures.

Thus, for a given copula `C`, its Kendall's tau can be obtained through `τ(C::Copula)` and its Spearman's Rho through `ρ(C::Copula)`.

In the literature however, for multivariate copulas, the matrices of bivariate Kendall taus and bivariate Spearman rhos are sometimes used, and this is these matrices that are obtained by `StatsBase.corkendall(data)` and `StatsBase.corspearman(data)` where `data::Matrix{n,d}` is a matrix of observations. The corresponding theoretical values for copulas in this package can be obtained through the `StatsBase.corkendall(C::Copula)` and `StatsBase.corspearman(C::Copula)` interface.

## Spearman's Rho

> **Definition (Spearman's ρ):** For a copula $C$ with a density $c$, the Spearman's ρ is defined as:
>
> $$\rho = 12 \int C(\bm u) d\bm u -3.$$
!!! note "Specific values of tau and rho"
Kendall's $\tau$ and Spearman's $\rho$ have values between -1 and 1, and are -1 in case of complete anticomonotony and 1 in case of comonotony. Moreover, they are 0 in case of independence. Moreover, their values only depends on the dependence structure and not the marginals. This is why we say that they measure the 'strength' of the dependency.

Spearman's Rho can be obtained through `ρ(C::Copula)`. Its value only depends on the dependence structure and not the marginals.
Many copula estimators are based on these coefficients, see e.g., [genest2011,fredricks2007,derumigny2017](@cite).

!!! warn "Multivariate case"
There exists several multivariate extensions of Spearman's rho. The one implemented here is the one we just defined what ever the dimension $d$, be careful as the normalization might differ from other places in the literature.
A few remarks on the state of the implementation:

!!! note "Specific values of tau and rho"
Kendall's $\tau$ and Spearman's $\rho$ have values between -1 and 1, and are -1 in case of complete anticomonotony and 1 in case of comonotony. Moreover, they are 0 in case of independence. This is
why we say that they measure the 'strength' of the dependency.
* Bivariate elliptical cases use $\tau = asin(\rho) * 2 / \pi$ where $\rho$ is the spearman correlation, as soon as the radial part does not have atoms. See [fang2002meta](@cite) for historical credits and [lindskog2003kendall](@cite) for a good review.
* Many Archimedean copulas have specific formulas for their Kendall tau's, but generic ones use [mcneil2009](@cite).
* Generic copulas use directly the upper formula.
* Estimation is done for some copulas wia inversion of Kendall's tau or Spearman's rho.

!!! tip "More-that-bivariate cases"
These two dependence measures make more sense in the bivariate case than in other cases, and therefore we sometimes refer to the Kendall's matrix or the Spearman's matrix for the collection of bivariate coefficients associated to a multivariate copula. Many copula estimators are based on these coefficients, see e.g., [genest2011,fredricks2007,derumigny2017](@cite).
!!! note "Spearman's rho: work in progress"
If most of the efficient family-specific formulas for Kendall's tau are already implemented in the package, Spearman's $\rho$'s tend to leverage the generic (slow) implementation much more. If you feel like a specific method for a certain copula is missing, do not hesitate to open an issue !

## Tail dependency

Expand Down
3 changes: 2 additions & 1 deletion src/ArchimedeanCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ end
function Distributions._rand!(rng::Distributions.AbstractRNG, ::ArchimedeanCopula{d,WGenerator}, x::AbstractVector{T}) where {d,T<:Real}
@assert d==2
x[1] = rand(rng)
x[2] = 1-x[1]
x[2] = 1-x[1]
return x
end
function Distributions._rand!(rng::Distributions.AbstractRNG, ::ArchimedeanCopula{d,IndependentGenerator}, A::DenseMatrix{T}) where {T<:Real, d}
Random.rand!(rng,A)
Expand Down
22 changes: 21 additions & 1 deletion src/Copula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,29 @@ function ρ(C::Copula{d}) where d
end
function τ(C::Copula)
F(x) = Distributions.cdf(C,x)
r = Distributions.expectation(F,C)
r = Distributions.expectation(F,C; nsamples=10^4)
return 4*r-1
end
function StatsBase.corkendall(C::Copula{d}) where d
# returns the matrix of bivariate kendall taus.
K = zeros(d,d)
for i in 1:d
for j in 1:d
K[i,j] = i==j ? 1 : τ(SubsetCopula(C::Copula{d},(i,j)))
end
end
return K
end
function StatsBase.corspearman(C::Copula{d}) where d
# returns the matrix of bivariate spearman rhos.
K = zeros(d,d)
for i in 1:d
for j in 1:d
K[i,j] = i==j ? 1 : ρ(SubsetCopula(C::Copula{d},(i,j)))
end
end
return K
end
function measure(C::CT, u,v) where {CT<:Copula}

# Computes the value of the cdf at each corner of the hypercube [u,v]
Expand Down
5 changes: 5 additions & 0 deletions src/EllipticalCopulas/GaussianCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ function _cdf(C::CT,u) where {CT<:GaussianCopula}
lb = repeat([T(-Inf)],d)
return MvNormalCDF.mvnormcdf(μ, C.Σ, lb, x)[1]
end

# Kendall tau of bivariate gaussian:
# Theorem 3.1 in Fang, Fang, & Kotz, The Meta-elliptical Distributions with Given Marginals Journal of Multivariate Analysis, Elsevier, 2002, 82, 1–16
τ(C::GaussianCopula{2,MT}) where MT = 2*asin(C.Σ[1,2])/π

4 changes: 4 additions & 0 deletions src/EllipticalCopulas/TCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ function Distributions.fit(::Type{CT},u) where {CT<:TCopula}
df = N.df
return TCopula(df,Σ)
end

# Kendall tau of bivariate student:
# Lindskog, F., McNeil, A., & Schmock, U. (2003). Kendall’s tau for elliptical distributions. In Credit risk: Measurement, evaluation and management (pp. 149-156). Heidelberg: Physica-Verlag HD.
τ(C::TCopula{2,MT}) where MT = 2*asin(C.Σ[1,2])/π
35 changes: 5 additions & 30 deletions src/ExtremeValueCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,6 @@ function D_B_ℓ(C::ExtremeValueCopula, t::Vector{Float64}, B::Vector{Int})
end

# Función PDF para ExtremeValueCopula usando ℓ
function _pdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
D1 = D_B_ℓ(C, t, [1])
D2 = D_B_ℓ(C, t, [2])
D12 = D_B_ℓ(C, t, [1, 2])
return c * (-D12 + D1 * D2) / (u[1] * u[2])
end
function Distributions._logpdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
Expand All @@ -106,26 +98,12 @@ function Distributions._logpdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
D12 = D_B_ℓ(C, t, [1, 2])
return log(c) + log(-D12 + D1 * D2) - log(u[1] * u[2])
end
# Definir la función para calcular τ
function τ(C::ExtremeValueCopula)
integrand(x) = begin
a = A(C, x)
da = dA(C, x)
return (x * (1 - x) / a) * da
end

integrate, _ = QuadGK.quadgk(integrand, 0.0, 1.0)
return integrate
end
# Definir la función para calcular τ and ρ
# Warning: the τ function can be veeeery unstable... it is actually very hard to compute correctly.
# In some case, it simply fails by a lot.
τ(C::ExtremeValueCopula) = QuadGK.quadgk(x -> d²A(C, x) * x * (1 - x) / A(C, x), 0.0, 1.0)[1]
ρ(C::ExtremeValueCopula) = 12 * QuadGK.quadgk(x -> 1 / (1 + A(C, x))^2, 0, 1)[1] - 3

function ρₛ(C::ExtremeValueCopula)
integrand(x) = 1 / (1 + A(C, x))^2

integral, _ = QuadGK.quadgk(integrand, 0, 1)

ρs = 12 * integral - 3
return ρs
end
# Función para calcular el coeficiente de dependencia en el límite superior
function λᵤ(C::ExtremeValueCopula)
return 2(1 - A(C, 0.5))
Expand All @@ -150,9 +128,6 @@ function Distributions._rand!(rng::Distributions.AbstractRNG, C::ExtremeValueCop
u1, u2 = rand(rng, Distributions.Uniform(0,1), 2)
z = rand(rng, ExtremeDist(C))
p = probability_z(C, z)
if p < -eps() || p > eps()
p = 0
end
c = rand(rng, Distributions.Bernoulli(p))
w = 0
if c == 1
Expand Down
13 changes: 3 additions & 10 deletions src/ExtremeValueCopulas/AsymGalambosCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,7 @@ struct AsymGalambosCopula{P} <: ExtremeValueCopula{P}
end

function A(C::AsymGalambosCopula, t::Real)
α = C.α
θ = C.θ

term1 = (θ[1] * t)^(-α)
term2 = (θ[2] * (1 - t))^(-α)

inner_term = term1 + term2

result = 1 - inner_term^(-1 / α)
return result
x₁ = - C.α * log(C.θ[1] * t)
x₂ = - C.α * log(C.θ[2] * (1-t))
return -expm1(-LogExpFunctions.logaddexp(x₁,x₂) / C.α)
end
14 changes: 4 additions & 10 deletions src/ExtremeValueCopulas/AsymLogCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ References:
"""
struct AsymLogCopula{P} <: ExtremeValueCopula{P}
α::P # Dependence Parameter
θ::Vector{P} # Asymmetry parameters (size 2)
θ₁::P
θ₂::P
function AsymLogCopula::P, θ::Vector{P}) where {P}
if length(θ) != 2
throw(ArgumentError("The vector θ must have 2 elements for the bivariate case"))
Expand All @@ -30,15 +31,8 @@ struct AsymLogCopula{P} <: ExtremeValueCopula{P}
elseif !(0 <= θ[1] <= 1) || !(0 <= θ[2] <= 1)
throw(ArgumentError("All parameters θ must be in the interval [0, 1]"))
else
return new{P}(α, θ)
return new{P}(α, θ[1],θ[2])
end
end
end

function A(C::AsymLogCopula, t::Real)
α = C.α
θ = C.θ

A = ((θ[1]^α)*(1-t)^α + (θ[2]^α)*(t^α))^(1/α)+(θ[1]- θ[2])*t + 1 -θ[1]
return A
end
A(C::AsymLogCopula, t::Real) = ((C.θ₁^C.α)*(1-t)^C.α + (C.θ₂^C.α)*(t^C.α))^(1/C.α)+(C.θ₁- C.θ₂)*t + 1 - C.θ₁
6 changes: 2 additions & 4 deletions src/ExtremeValueCopulas/AsymMixedCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ struct AsymMixedCopula{P} <: ExtremeValueCopula{P}
end

function A(C::AsymMixedCopula, t::Real)
θ = C.θ

a = θ[2]*t^3 + θ[1]*t^2-(θ[1]+θ[2])*t+1
return a
θ₁, θ₂ = C.θ[1], C.θ[2]
return θ₂*t^3 + θ₁*t^2 - (θ₁+θ₂)*t + 1
end
72 changes: 30 additions & 42 deletions src/ExtremeValueCopulas/BC2Copula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,63 @@
Fields:
- θ1::Real - parameter
- θ1::Real - parameter
- a::Real - parameter
- a::Real - parameter
Constructor
BC2Copula(θ1, θ2)
BC2Copula(a, b)
The bivariate BC₂ copula is parameterized by two parameters ``\\theta_{i} \\in [0,1], i=1,2``. It is an Extreme value copula with Pickands dependence function:
The bivariate BC2 copula is parameterized by two parameters ``a,b \\in [0,1]``. It is an Extreme value copula with Pickands dependence function:
```math
A(t) = \\max\\{\\theta_1 t, \\theta_2(1-t) \\} + \\max\\{(1-\\theta_1)t, (1-\\theta_2)(1-t)\\}
A(t) = \\max\\{a t, b (1-t) \\} + \\max\\{(1-a)t, (1-b)(1-t)\\}
```
References:
* [mai2011bivariate](@cite) Mai, J. F., & Scherer, M. (2011). Bivariate extreme-value copulas with discrete Pickands dependence measure. Extremes, 14, 311-324. Springer, 2011.
"""
struct BC2Copula{P} <: ExtremeValueCopula{P}
θ1::P
θ2::P

function BC2Copula::Vararg{Real})
if length(θ) !== 2
throw(ArgumentError("BC2Copula requires only 2 arguments."))
end
T = promote_type(typeof(θ[1]), typeof(θ[2]))
θ1, θ2 = T(θ[1]), T(θ[2])

if !(0 <= θ1 <= 1) || !(0 <= θ2 <= 1)
throw(ArgumentError("All θ parameters must be in [0,1]"))
a::P
b::P
function BC2Copula(a,b)
T = promote_type(typeof(a), typeof(b))
if !(0 <= a <= 1) || !(0 <= b <= 1)
throw(ArgumentError("Both parameters a and b must be in [0,1]"))
end
return new{T}(θ1, θ2)
return new{T}(T(a), T(b))
end
end

function (C::BC2Copula, t::Vector)
θ1, θ2 = C.θ1, C.θ2
a, b = C.a, C.b
t₁, t₂ = t
return max(θ1*t₁, θ2*t₂) + max((1-θ1)*t₁, (1-θ2)*t₂)
return max(a*t₁, b*t₂) + max((1-a)*t₁, (1-b)*t₂)
end

function A(C::BC2Copula, t::Real)
θ1, θ2 = C.θ1, C.θ2
return max(θ1*t, θ2*(1-t)) + max((1-θ1)*t, (1-θ2)*(1-t))
a, b = C.a, C.b
return max(a*t, b*(1-t)) + max((1-a)*t, (1-b)*(1-t))
end

function dA(C::BC2Copula, t::Float64)
θ1, θ2 = C.θ1, C.θ2

# Conditions for the derivative of the first part
if θ1*t >= θ2*(1-t)
f1_derivative = θ1
else
f1_derivative = -θ2
end

# Conditions for the derivative of the second part
if (1-θ1)*t >= (1-θ2)*(1-t)
f2_derivative = 1-θ1
else
f2_derivative = -(1-θ2)
end

return f1_derivative + f2_derivative
a, b = C.a, C.b
der1 = a*t >= b*(1-t) ? a : -b
der2 = (1-a)*t >= (1-b)*(1-t) ? -a : -(1-b)
return der1 + der2
end

function Distributions._rand!(rng::Distributions.AbstractRNG, C::BC2Copula, u::AbstractVector{T}) where {T<:Real}
θ1, θ2 = C.θ1, C.θ2
a, b = C.a, C.b
v1, v2 = rand(rng, Distributions.Uniform(0,1), 2)
u[1] = max(v1^(1/θ1),v2^(1/(1-θ1)))
u[2] = max(v1^(1/θ2),v2^(1/(1-θ2)))
u[1] = max(v1^(1/a), v2^(1/(1-a)))
u[2] = max(v1^(1/b), v2^(1/(1-b)))
return u
end

τ(C::BC2Copula) = 1 - abs(C.a - C.b)

function ρ(C::BC2Copula)
a,b = C.a, C.b
return 2 * (a + b + a*b + max(a,b) - 2a^2 - 2b^2) / (3 - a - b - min(a,b)) / (a + b + max(a,b))
end
2 changes: 1 addition & 1 deletion src/ExtremeValueCopulas/CuadrasAugeCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dA(C::CuadrasAugeCopula, t::Real) = t <= 0.5 ? -C.θ : C.θ

τ(C::CuadrasAugeCopula) = C.θ/(2-C.θ)

ρₛ(C::CuadrasAugeCopula) = (3*C.θ)/(4-C.θ)
ρ(C::CuadrasAugeCopula) = (3*C.θ)/(4-C.θ)

# specific ℓ específica of Cuadras-Augé Copula
function (C::CuadrasAugeCopula, t::Vector)
Expand Down
4 changes: 2 additions & 2 deletions src/ExtremeValueCopulas/GalambosCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct GalambosCopula{P} <: ExtremeValueCopula{P}
θ::P # Copula parameter
function GalambosCopula(θ)
if θ > 19.5
@warn "The parameter θ = $(θ) is large, which may lead to numerical instability in any functions. Consider regularizing the input."
@info "GalambosCopula(θ=$(θ)): θ is large, which may lead to numerical issues."
end
if θ < 0
throw(ArgumentError("Theta must be >= 0"))
Expand All @@ -41,7 +41,7 @@ struct GalambosCopula{P} <: ExtremeValueCopula{P}
end
end

A(C::GalambosCopula, t::Real) = 1 - (t^(-C.θ) + (1 - t)^(-C.θ))^(-1/C.θ)
A(C::GalambosCopula, t::Real) = -expm1(-LogExpFunctions.logaddexp(-C.θ*log(t),-C.θ*log(1-t))/C.θ)
# This auxiliary function helps determine if we need binary search or not in the generation of random samples
function needs_binary_search(C::GalambosCopula)
return C.θ > 19.5
Expand Down
Loading

0 comments on commit a7e1cb4

Please sign in to comment.