Fitting compound distributions
Through the SklarDist interface, it is possible to fit distributions constructed from a copula and marginals:
using Copulas
using Distributions
# Let's sample some datas:
X₁ = LogNormal()
X₂ = Pareto()
X₃ = Gamma()
X₄ = Normal()
C = SurvivalCopula(FrankCopula(4,7),(2,4))
D = SklarDist(C,(X₁,X₂,X₃,X₄))
data = rand(D,1000)4×1000 Matrix{Float64}:
0.0784939 0.997003 0.604455 … 1.82822 1.2783 0.446217
76.4781 1.39965 3.77571 1.5642 4.16744 31.9205
0.122785 0.634351 0.84995 2.41706 1.46092 0.0589099
0.694961 0.331649 0.998912 -0.209601 0.263081 1.21281The fit function uses a type as its first argument that describes the structure of the model :
MyCop = SurvivalCopula{4,ClaytonCopula,(2,4)}
MyMargs = Tuple{LogNormal,Pareto,Gamma,Normal}
MyD = SklarDist{MyCop, MyMargs}
fitted_model = fit(MyD,data)SklarDist{SurvivalCopula{4, ClaytonCopula{4, Float64}, (2, 4)}, Tuple{Distributions.LogNormal{Float64}, Distributions.Pareto{Float64}, Distributions.Gamma{Float64}, Distributions.Normal{Float64}}}(
C: SurvivalCopula(ClaytonCopula{4, Float64}(θ = 2.6778664734677733,))
m: (Distributions.LogNormal{Float64}(μ=-0.011822157090623123, σ=1.003768975981645), Distributions.Pareto{Float64}(α=1.0066863250981448, θ=1.001945355908185), Distributions.Gamma{Float64}(α=1.0117051489414404, θ=0.9176748986655682), Distributions.Normal{Float64}(μ=0.04878310534814991, σ=1.019810914820893))
)Another possibility is to use an empirical copula and only fit the marginals:
other_fitted_model = fit(SklarDist{EmpiricalCopula,MyMargs},data)SklarDist{EmpiricalCopula{4, Matrix{Float64}}, Tuple{Distributions.LogNormal{Float64}, Distributions.Pareto{Float64}, Distributions.Gamma{Float64}, Distributions.Normal{Float64}}}(
C: EmpiricalCopula{d}(4, 1000)
m: (Distributions.LogNormal{Float64}(μ=-0.011822157090623123, σ=1.003768975981645), Distributions.Pareto{Float64}(α=1.0066863250981448, θ=1.001945355908185), Distributions.Gamma{Float64}(α=1.0117051489414404, θ=0.9176748986655682), Distributions.Normal{Float64}(μ=0.04878310534814991, σ=1.019810914820893))
)This simple interface leverages the fit function from Distributions.jl. According to their documentation, this function is not supposed to use a particular method but to fit "quick and dirty" some distributions.
So you have to be careful: the fit method might not be the same for different copulas or different marginals. For example, Archimedean copulas are fitted through inversion of the Kendall tau function, while the Gaussian copula is fitted by maximum likelihood.
Scatter of original data (first two dims)
using Plots
scatter(data[1,:], data[2,:]; ms=2, alpha=0.6, title="First two marginals (original scale)", legend=false)
Pseudo-observations vs simulated from fitted copula
U = Copulas.pseudos(data) # pseudo-observations (uniforms)
Usim = rand(fitted_model.C, size(data,2)) # simulate same length from fitted copula
P1 = scatter(U[1,:], U[2,:]; ms=2, alpha=0.6, xlim=(0,1), ylim=(0,1), title="Empirical uniforms", legend=false)
P2 = scatter(Usim[1,:], Usim[2,:]; ms=2, alpha=0.6, xlim=(0,1), ylim=(0,1), title="Fitted copula uniforms", legend=false)
plot(P1, P2; layout=(1,2), size=(850,350))