Skip to content

Commit

Permalink
Add new subsetdims method (#187)
Browse files Browse the repository at this point in the history
Fixes #186
  • Loading branch information
lrnv authored Mar 23, 2024
1 parent c85a28c commit 974c579
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
8 changes: 4 additions & 4 deletions src/Copulas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ module Copulas
include("utils.jl")
include("Copula.jl")
include("SklarDist.jl")
export pseudos,
EmpiricalCopula,
export pseudos,
SklarDist

# Others.
Expand Down Expand Up @@ -80,9 +79,10 @@ module Copulas
InvGaussianCopula,
JoeCopula

# Subsetting
include("SubsetCopula.jl") # not exported yet.


using PrecompileTools
using PrecompileTools
@setup_workload begin
# Putting some things in `@setup_workload` instead of `@compile_workload` can reduce the size of the
# precompile file and potentially make loading faster.
Expand Down
3 changes: 2 additions & 1 deletion src/SklarDist.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ struct SklarDist{CT,TplMargins} <: Distributions.ContinuousMultivariateDistribut
d = length(C)
@assert length(m) == d
@assert all(mᵢ isa Distributions.UnivariateDistribution for mᵢ in m)
return new{typeof(C),typeof(m)}(C,m)
tm = Tuple(m)
return new{typeof(C),typeof(tm)}(C,tm)
end
end
Base.length(S::SklarDist{CT,TplMargins}) where {CT,TplMargins} = length(S.C)
Expand Down
56 changes: 56 additions & 0 deletions src/SubsetCopula.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
SubsetCopula{d,CT}
Fields:
- `C::CT` - The copula
- `dims::Tuple{Int64}` - a Tuple representing which dimensions are used.
Constructor
SubsetCopula(C::Copula,dims)
This class allows to construct a random vector specified as X[dims] where X is the random vector correspnding to C and dims is a selection of dimensions.
"""
struct SubsetCopula{d,CT} <: Copula{d}
C::CT
dims::NTuple{d,Int64}
function SubsetCopula(C::Copula{d},dims) where d
# if Tuple(dims) == Tuple(1:d)
# return C
# end
@assert all(dims .<= d)
return new{length(dims), typeof(C)}(C,Tuple(Int.(dims)))
end
end
Base.eltype(C::SubsetCopula{d,CT}) where {d,CT} = Base.eltype(C.C)
function Distributions._rand!(rng::Distributions.AbstractRNG, C::SubsetCopula{d,CT}, x::AbstractVector{T}) where {T<:Real, d,CT}
u = Random.rand(rng,C.C)
x .= (u[i] for i in C.dims)
return x
end
function _cdf(C::SubsetCopula{d,CT},u) where {d,CT}
# Simplyu saturate dimensions that are not choosen.
v = ones(length(C.C))
for (i,j) in enumerate(C.dims)
v[j] = u[i]
end
return Distributions.cdf(C.C,v)
end

# A few specialized constructors:
SubsetCopula(C::GaussianCopula,dims) = GaussianCopula(C.Σ[collect(dims),collect(dims)])
SubsetCopula(C::TCopula{d,df,MT},dims) where {d,df,MT} = TCopula(df, C.Σ[collect(dims),collect(dims)])
SubsetCopula(C::ArchimedeanCopula{d,TG},dims) where {d,TG} = ArchimedeanCopula(length(dims), C.G) # in particular for the independence this will work.

# We could add a few more for performance if needed: EmpiricalCopula, others...



"""
subsetdims(C::Copula,dims)
subsetdims(D::SklarDist, dims)
If X is the random vector corresponding to `C` or `D`, this returns the distributions of C[dims]. Has specialized methods for some copulas.
"""
subsetdims(C::Copula{d},dims) where d = SubsetCopula(C,dims)
subsetdims(D::SklarDist, dims) = SklarDist(subsetdims(D.C,dims), Tuple(D.m[i] for i in dims))
1 change: 1 addition & 0 deletions test/margins_uniformity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
SurvivalCopula(ClaytonCopula(2,-0.7),(1,2)),
RafteryCopula(2, 0.2),
RafteryCopula(3, 0.5),
Copulas.SubsetCopula(RafteryCopula(3, 0.5), (2,1)),
# Others ? Yes probably others too !
)

Expand Down
47 changes: 47 additions & 0 deletions test/subsetcopula.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@testitem "Check that subsetcopula works as intended" begin
using Copulas, Distributions

cops = (
IndependentCopula(3),
AMHCopula(3,0.6),
AMHCopula(4,-0.3),
ClaytonCopula(2,-0.7),
ClaytonCopula(3,-0.1),
ClaytonCopula(4,7.),
FrankCopula(2,-5.),
FrankCopula(3,12.),
FrankCopula(4,6.),
FrankCopula(4,150.),
JoeCopula(3,7.),
GumbelCopula(4,7.),
GumbelCopula(4,20.),
GumbelBarnettCopula(3,0.7),
InvGaussianCopula(4,0.05),
InvGaussianCopula(3,8.),
GaussianCopula([1 0.5; 0.5 1]),
TCopula(4, [1 0.5; 0.5 1]),
FGMCopula(2,1),
MCopula(4),
WCopula(2),
PlackettCopula(2.0),
EmpiricalCopula(randn(2,100),pseudo_values=false),
SurvivalCopula(ClaytonCopula(2,-0.7),(1,2)),
RafteryCopula(2, 0.2),
RafteryCopula(3, 0.5),
)

for C in cops
d = length(C)
D = SklarDist(C,(LogNormal() for i in 1:d))
sC = Copulas.subsetdims(C,(1,2))
sD = Copulas.subsetdims(D,(2,1))

# The following methods have to work:
u = rand(sC,10)
v = rand(sD,10)
cdf(sC,u)
cdf(sD,v)

@test sD.C == Copulas.subsetdims(C,(2,1)) # check for coherence.
end
end

0 comments on commit 974c579

Please sign in to comment.