Fitting interface
Fitting turns an observed sample into a dependence model. This sounds like a single operation, but it involves several distinct choices: whether the margins are known or estimated, which copula family is plausible, which feature of the data identifies its parameters, and how uncertainty should be reported. This page introduces those choices before presenting their Julia interface.
Throughout this page, observations are columns of a SklarDist fit starts from observations on their original marginal scales.
Given pseudo-observations
Maximum likelihood (method=:mle) maximizes the copula density over values already observed on the uniform copula scale. Maximum pseudo-likelihood (method=:mpl) first replaces raw marginal observations by their empirical ranks and then maximizes the same numerical objective. The point optimizer is the same, but the two estimators make different assumptions about how the uniform observations were obtained.
From a point estimate to a statistical model
A fitted copula
When only the estimated distribution is needed, fit returns it directly:
using Copulas, Random, StatsBase, Distributions, Plots
Ctrue = GumbelCopula(2, 3.0)
U = rand(Ctrue, 300)
Ĉ = fit(GumbelCopula, U; method=:mle)GumbelCopula{2, Float64}(3.296347597534482,)Keeping the evidence behind the fit
An estimated parameter without information about how it was obtained is often not enough. CopulaModel deliberately retains only four ingredients: the fitted distribution, the original data supplied by the user, the fitted log-likelihood, and the minimal recipe needed to replay the estimator:
M = fit(CopulaModel, GumbelCopula, U; method=:mle)────────────────────────────────────────────────────────────────────────────────
[ CopulaModel: Archimedean d=2 ]
────────────────────────────────────────────────────────────────────────────────
Method: mle
Number of observations: 300
Degrees of freedom: 1
────────────────────────────────────────────────────────────────────────────────
[ Fit metrics ]
────────────────────────────────────────────────────────────────────────────────
Loglikelihood: 239.9921
AIC: -477.984
BIC: -474.281
────────────────────────────────────────────────────────────────────────────────
[ Dependence metrics ]
────────────────────────────────────────────────────────────────────────────────
Kendall τ: 0.6966
Spearman ρ: 0.8734
────────────────────────────────────────────────────────────────────────────────
[ Copula parameters ]
────────────────────────────────────────────────────────────────────────────────
θ: 3.296347597534482The fitted distribution is available through the standard model interface; the printed report summarizes the estimator, likelihood, information criteria, parameters and available dependence measures. Transformed observations, the independence likelihood and parameter blocks are reconstructed only when an accessor needs them. Optimizer traces and convergence diagnostics are not model state: an optimizer-backed estimator either returns an accepted fit or throws.
fit(Family, U) is deliberately lightweight and returns only the fitted copula. fit(CopulaModel, Family, U) fits the same model while retaining the statistical evidence and estimator specification. The latter is not a different estimator unless different keywords are supplied.
Information criteria
CopulaModel deliberately exposes AIC, BIC, AICc, and HQC for every fitted model, not only for ordinary maximum-likelihood estimates. This is a 1.0 API choice: the functions are always operationally defined from the fitted model's stored log-likelihood, number of fitted parameters, and number of observations.
For a fitted model M, with dof(M), and nobs(M), Copulas.jl uses
with the package's existing AICc and HQC corrections applied analogously. Automatic candidate selection uses exactly the same scores and remains available for every supported fitting method.
Statistical interpretation
Outside ordinary MLE these are operational comparison scores
When M comes from ordinary maximum likelihood for the likelihood being scored, the usual classical interpretation of AIC/BIC/AICc/HQC applies. For other estimators, Copulas.jl still evaluates the same algebraic score at the fitted parameters, but that number does not automatically inherit the classical MLE asymptotic justification.
This distinction applies in particular to:
maximum pseudo-likelihood (
method=:mpl), where empirical ranks are formed before maximizing the copula density;inversion estimators such as
:itau,:irho, and:ibeta, which do not maximize the likelihood at all;sequential
SklarDistIFM fitting, which is not joint maximum likelihood for all copula and marginal parameters;SklarDistECDF/rank fitting and related semiparametric procedures.
The fitted log-likelihood remains useful in all of these cases, and the common penalized score is useful for a stable comparison interface. Comparisons should still be made between models evaluated on the same observations and the same likelihood contribution.
For composite/pseudo-likelihood inference, estimator-specific information criteria use a bias correction involving sensitivity and variability (Godambe or sandwich) quantities rather than blindly substituting the ordinary AIC penalty; see Varin and Vidoni [ DocumenterCitations.CitationSiteNode("varin2005composite-cite-1")
]. For two-stage copula estimation, Ko and Hjort develop a Copula Information Criterion (CIC) that accounts for the IFM/two-stage structure [ DocumenterCitations.CitationSiteNode("ko2019copula-cite-1")
].
These adapted criteria are not implemented as part of the 1.0 contract. Future implementations should use names that make their statistical meaning explicit—for example a composite-likelihood information criterion using the appropriate Godambe correction, and CIC (or a closely related criterion) for IFM. They must not silently change the established operational meaning of aic, bic, aicc, or hqc on CopulaModel.
Examples
using Copulas, Distributions, StatsBase
U = [
0.12 0.31 0.54 0.73 0.89 0.42
0.81 0.22 0.63 0.47 0.15 0.68
]
Mmle = fit(CopulaModel, ClaytonCopula, U; method=:mle)
Mτ = fit(CopulaModel, ClaytonCopula, U; method=:itau)
(aic(Mmle), bic(Mmle), aic(Mτ), bic(Mτ))(2.0, 1.791759469228055, Inf, Inf)The second pair remains valid API output. It should be read as the documented penalized fitted-log-likelihood score at the Kendall-inversion estimate, not as a claim that the classical MLE derivation of AIC/BIC applies unchanged to that estimator.
Comparing candidate families
Choosing a family is part of modelling, not a consequence of optimization. When several scientifically defensible families remain, they can be fitted to the same data and compared by an information criterion.
For each candidate family, fit a model and compute a penalized likelihood criterion. The selected candidate is the successful fit with the smallest eligible finite criterion. AIC emphasizes estimated predictive loss; BIC and HQC penalize model dimension more strongly as the sample grows, while AICc corrects AIC in small samples.
When the copula family is unknown, an explicit collection of candidate families can be compared automatically. This returns a CopulaSelection, keeping the comparison report separate from the winning CopulaModel:
Ctrue = ClaytonCopula(2, 4.0)
data = rand(Ctrue, 300)
Msel = fit(
CopulaModel,
Copulas.Copula,
data;
candidates=(ClaytonCopula, GumbelCopula, FrankCopula),
criterion=:bic,
)
Msel────────────────────────────────────────────────────────────────────────────────
[ CopulaModel: Archimedean d=2 ]
────────────────────────────────────────────────────────────────────────────────
Method: mle
Number of observations: 300
Degrees of freedom: 1
────────────────────────────────────────────────────────────────────────────────
[ Fit metrics ]
────────────────────────────────────────────────────────────────────────────────
Loglikelihood: 226.3657
AIC: -450.731
BIC: -447.028
────────────────────────────────────────────────────────────────────────────────
[ Dependence metrics ]
────────────────────────────────────────────────────────────────────────────────
Kendall τ: 0.6476
Spearman ρ: 0.8303
────────────────────────────────────────────────────────────────────────────────
[ Copula parameters ]
────────────────────────────────────────────────────────────────────────────────
θ: 3.6749857888138537
────────────────────────────────────────────────────────────────────────────────
[ Model selection ]
────────────────────────────────────────────────────────────────────────────────
Criterion: BIC
Selected family: Archimedean d=2Retrieve the reusable fitted model with selected_model:
Mbest = selected_model(Msel)
fitted_distribution(Mbest)ClaytonCopula{2, Float64}(3.6749857888138537,)The available criteria are:
:bic— Bayesian information criterion,:aic— Akaike information criterion,:aicc— finite-sample corrected AIC,:hqc— Hannan–Quinn criterion.
BIC is the default criterion.
The winning fit is reused. Selection itself performs no uncertainty calculation.
The complete comparison can be inspected with selection_table:
selection_table(Msel)3-element Vector{NamedTuple}:
(candidate = ClaytonCopula, status = :ok, method = :mle, nparams = 1, loglikelihood = 226.36569711082865, aic = -450.7313942216573, aicc = -450.7179714028654, bic = -447.0276117470011, hqc = -449.2491351280461, error = nothing)
(candidate = GumbelCopula, status = :ok, method = :mle, nparams = 1, loglikelihood = 129.58726235456274, aic = -257.1745247091255, aicc = -257.16110189033355, bic = -253.47074223446927, hqc = -255.69226561551426, error = nothing)
(candidate = FrankCopula, status = :ok, method = :mle, nparams = 1, loglikelihood = -3.441691376337987e-15, aic = 2.000000000000007, aicc = 2.0134228187919536, bic = 5.703782474656208, hqc = 3.4822590936112223, error = nothing)Each row stores the candidate family, fitting status and method, log-likelihood, number of parameters, and all four information criteria. Candidates that fail to fit can be skipped with on_error=:skip (the default) or propagated immediately with on_error=:throw.
Candidate lists are scientific assumptions
The candidate collection is intentionally explicit. Automatic selection does not make every implemented family plausible for every dimension, tail regime or scientific question. Nonfinite scores and failed fits are excluded; on_error=:throw is useful when a failed candidate should invalidate the comparison rather than merely be recorded in selection_table.
Use maximum-likelihood fitting for the usual information-criterion interpretation; passing another fitting method merely compares the scores at those estimates. The shorter fit(Copulas.Copula, U; candidates=(...)) returns only the selected copula.
GOFCopulaTest(Msel) and GOFCopulaTest(Msel, U) are deliberately unsupported: a valid selection-aware bootstrap must repeat family selection in every replicate, not just refit the winning family. These calls throw rather than silently omit the selection step.
What exactly is being fitted?
Usually, the model is identified by a copula or Sklar type, for example fit(GumbelCopula, U) or fit(CopulaModel, SklarDist{ClaytonCopula,Tuple{Normal,LogNormal}}, X). With direct copula calls default to method=:mle; explicitly supported alternatives depend on the family.
The form SklarDist{CopulaType,Tuple{MarginTypes...}} is intentionally public syntax for this purpose: it selects the copula family and the ordered marginal families to estimate. This is a narrow exception to the usual rule that storage type parameters are implementation details. It does not expose the fields, additional representation choices, or arbitrary concrete type parameters of a constructed SklarDist.
::: note Structural models
Most calls identify a model by its type. A structure chosen at runtime, such as a nested Archimedean tree, cannot be reconstructed from its type alone and is therefore fitted from a template instance. This distinction also matters when a bootstrap must reproduce the original estimator. Use fit(typeof(C0), U) when the type completely describes the model and fit(C0, U) when the instance contains the structure to preserve.
:::
Reading and diagnosing a fitted model
CopulaModel implements StatsBase.StatisticalModel. Its accessors answer different questions about the fit:
| Function | Description |
|---|---|
fitted_distribution(M) | Fitted copula or Sklar distribution. |
nobs(M) | Number of observations used in the fit. |
loglikelihood(M) | Cached log-likelihood evaluated at the fitted distribution. |
deviance(M) | Deviance, equal to minus twice the fitted log-likelihood. |
nullloglikelihood(M) | Lazily computed log-likelihood under independence, preserving fitted margins for Sklar models. |
nulldeviance(M) | Deviance of the null model (−2 · nullloglikelihood(M)). |
aic(M) / bic(M) | Information criteria from StatsBase.jl. |
coef(M) / coefnames(M) | Estimated parameters and their names. |
residuals(M; transform=:uniform | :normal) | Rosenblatt residuals on [0,1] or Normal scale when the fit retains the required observations. |
The table is a reference; in practice, diagnostics are best read together. Information criteria compare fitted models on the same observations, while Rosenblatt residuals probe whether the fitted conditional structure has removed the dependence. Parameter uncertainty belongs to a separate inference result.
Examples
# Information criteria
StatsBase.aic(M)
StatsBase.bic(M)-474.28051073388286# Rosenblatt residuals
R = StatsBase.residuals(M; transform=:uniform)
RN = StatsBase.residuals(M; transform=:normal)
(size(R), size(RN))((2, 300), (2, 300))Inference after estimation
Fitting and uncertainty quantification are separate operations. fit produces the point estimate and records how it was obtained; it never computes a covariance matrix. Apply infer to that fitted model when uncertainty is needed:
| Symbol | Description |
|---|---|
:hessian | Inverse observed information (−Hessian of the log-likelihood). Default for method = :mle. |
:godambe | Scalar-moment Godambe for supported bivariate rank-matching fits; accepts nresamples and rng. |
:godambe_pairwise | Pairwise-moment Godambe for supported multivariate rank fits; accepts nresamples and rng. |
:jackknife | Leave-one-out refitting of the complete recorded estimator. |
:bootstrap | Bootstrap refitting of the complete recorded estimator; accepts nresamples and rng. |
The default is :hessian after supported maximum-likelihood fits, :godambe after supported bivariate rank-matching fits, and :godambe_pairwise when a supported multivariate rank estimator is defined by pairwise moments. A downstream fitting extension does not implicitly opt into analytical inference. There is no generic silent fallback: if a method is mathematically unavailable or its sensitivity matrix is rank deficient, infer throws and the user must choose another procedure explicitly.
Maximum pseudo-likelihood currently has no implicit covariance method. A sandwich estimator must reflect the rank preprocessing and is tracked separately; use an explicit full-estimator bootstrap in the meantime when that procedure is appropriate for the analysis.
I = infer(M)
StatsBase.vcov(I)
StatsBase.stderror(I)
StatsBase.confint(I; level=0.95)([2.9860516283766447], [3.606643566692319])The same fitted model can be passed to several inference procedures without optimizing it again or mutating it. Bootstrap and Godambe procedures that use resampling accept explicit controls, for example infer(M; method=:bootstrap, nresamples=500, rng=Xoshiro(42)) or infer(M; method=:godambe, nresamples=500, rng=Xoshiro(42)).
Estimating margins and dependence together
For raw observations, fitting a SklarDist separates two questions: how each margin should be estimated, and how the transformed observations should be used to estimate dependence.
You can pass the sklar_method parameter as:
:ifm: fits parametric margins and maps data to pseudo-scale via their CDFs.:ecdf: uses empirical pseudo-observations (ranks).
The default is sklar_method=:ifm. In either route the copula step defaults to copula_method=:mle whenever that estimator is supported. Empirical or extension-defined families without MLE retain their first advertised method. The default can be replaced by any method supported by the chosen family, such as :itau or :irho.
Use
sklar_method = :ifmwhen margins are plausibly parametric and you want a model-based projection; use:ecdfto avoid margin misspecification.margins_kwargsis a singleNamedTupleapplied to every marginal fit. For heterogeneous options, fit margins manually and then fit the copula on the resulting pseudo-data.nullloglikelihood(M)reconstructs independence lazily and preserves the same fitted margins.Neither route jointly maximizes the complete Sklar likelihood: IFM is sequential, while ECDF estimates dependence from ranks.
S = SklarDist(ClaytonCopula(2, 5), (Normal(), LogNormal(0, 0.5)))
X = rand(S, 300)
Ŝ = fit(CopulaModel, SklarDist{ClaytonCopula,Tuple{Normal,LogNormal}}, X;
sklar_method=:ifm, # or :ecdf
copula_method=:default, # MLE when available; otherwise the family's default
margins_kwargs=NamedTuple(), copula_kwargs=NamedTuple()) # options will be passed down to fitting functions.
Ŝ────────────────────────────────────────────────────────────────────────────────
[ CopulaModel: SklarDist ] (Copula=Archimedean d=2, Margins=(Normal, LogNormal))
────────────────────────────────────────────────────────────────────────────────
Copula: Archimedean d=2
Margins: (Normal, LogNormal)
Methods: copula=mle, sklar=ifm
Number of observations: 300
Degrees of freedom: 5
────────────────────────────────────────────────────────────────────────────────
[ Fit metrics ]
────────────────────────────────────────────────────────────────────────────────
Loglikelihood: -333.2117
AIC: 676.423
BIC: 694.942
────────────────────────────────────────────────────────────────────────────────
[ Dependence metrics ]
────────────────────────────────────────────────────────────────────────────────
Kendall τ: 0.7421
Spearman ρ: 0.9047
────────────────────────────────────────────────────────────────────────────────
[ Copula parameters ]
────────────────────────────────────────────────────────────────────────────────
θ: 5.756179760381719
────────────────────────────────────────────────────────────────────────────────
[ Marginals ]
────────────────────────────────────────────────────────────────────────────────
#1 Normal (-0.027204959973550664, 1.0120320297631529)
#2 LogNormal (-0.0024530498356368463, 0.5106769988915705)For a fitted Sklar model, infer(Ŝ) defaults to a full-estimator bootstrap. Each resample starts from X, refits every margin, rebuilds the transformed sample according to sklar_method, and then refits the copula. Thus the full covariance includes uncertainty in the margins, uncertainty in the copula, and their cross-covariances:
coef(Ŝ) and coefnames(Ŝ) use the same order: copula parameters first, then the parameters of each margin in coordinate order. This ordering is what makes the covariance blocks interpretable.
Isklar = infer(Ŝ; method=:bootstrap, nresamples=10, rng=Xoshiro(43))
Vall = StatsBase.vcov(Isklar)
Vcopula = StatsBase.vcov(Isklar; component=:copula)
Vmargins = StatsBase.vcov(Isklar; component=:margins)
(size(Vall), size(Vcopula), size(Vmargins))((5, 5), (1, 1), (4, 4))Analytical Sklar covariance methods throw explicitly. The arbitrary estimators behind Distributions.fit for marginal families do not provide a common score, Hessian, or parameter-transform contract from which such formulas could be derived safely.
plot(fitted_distribution(Ŝ))
Choosing an estimating principle
The names and availability of fitting methods depend on the family. Direct parametric copula fitting defaults to method=:mle whenever MLE is available; choose another estimator explicitly. Structural and empirical families without an MLE retain the first method registered internally for that family. This preserves family-defined defaults without ever selecting :mpl implicitly; estimator registration and execution remain internal.
The fitting method determines which feature of the sample identifies the parameters. No method dominates in every family and sample size.
:mle— Maximum likelihood overU. Recommended when a stable density and a good reparameterization exist.:mpl— Maximum pseudo-likelihood. Withpseudo_values=false, raw observations are converted bypseudosbefore the copula likelihood is maximized. This method is available whenever:mleis available, but is never selected by default.:itau— Kendall inverse: matches theoreticaltau(C)to empiricaltau(U). Ideal for single-parameter families with a monotone inverse. For the elliptical families it is closed form in every dimension: each entry of the correlation matrix issinpi(τ̂/2)of the corresponding pairwise sample coefficient, repaired to the nearest positive-definite correlation matrix when the pairwise entries are not jointly consistent. ForTCopulathe degrees of freedom are then the maximizer of the likelihood with that correlation held fixed.:irho— Spearman inverse: analogous torho; can use scalar or matrix objectives. ForGaussianCopulait is the closed form2 sinpi(ρ̂_S/6)entrywise.:ibeta— Blomqvist inverse: scalar; only valid for families with ≤ 1 free parameter.:itau_irho— joint Kendall/Spearman matching for a bivariateTCopula: Kendall's tau determines the correlation parameter and Spearman's rho determines the degrees of freedom.
The two likelihood names are kept consistent with the preprocessing request. Asking for method=:mle, pseudo_values=false silently records the effective method as :mpl. Asking for method=:mpl, pseudo_values=true records :mle and warns, because no rank transformation occurs.
A generic joint optimizer would need a smooth unconstrained parameterization for every requested marginal family. Distributions.jl does not expose enough information to derive such mappings from params and constructors: marginal parameters may be positive, bounded, ordered, matrix-valued, mutually constrained, or may alter the support. Guessing those constraints would make a nominally generic method unreliable. A future API extension can add full Sklar MLE once marginal families can explicitly provide this optimization protocol; the continuous, discrete and mixed-margin likelihood cases must also be distinguished.
There is a second, statistical limitation: Distributions.fit is a common entry point, not a universal promise that every marginal family uses maximum likelihood. Its estimator is chosen by the individual distribution implementation and is not exposed to Copulas.jl through a stable protocol; for some families it may use another fitting principle altogether. Consequently, independently calling fit on every margin neither identifies a joint MLE nor even guarantees that every marginal block was estimated by marginal MLE.
Rank-based methods cannot identify more free parameters than the matched coefficients contain independent information. In particular, a single scalar coefficient cannot generally identify a multi-parameter family; :ibeta enforces this restriction explicitly.
For extreme-value copulas, :mle / :iupper use the documented Pickands representation when supported by the family.
Weighted observations
Both likelihood estimators, the rank inversions and the Sklar route accept one weight per observation:
w = exp.(-0.01 .* (n:-1:1)) # exponential decay, any positive scale
M = fit(CopulaModel, ClaytonCopula, U; method=:mle, weights=w)
C = fit(GaussianCopula, X; pseudo_values=false, weights=w)
R = fit(GumbelCopula, U; method=:itau, weights=w)
S = fit(SklarDist{ClaytonCopula,Tuple{Normal,Exponential}}, X; weights=w)The fit maximizes the weighted pseudo-likelihood ∑ᵢ wᵢ log c(uᵢ). The weights are normalized once so that they sum to the number of observations n, which fixes their scale without changing the maximizer: a weight reads as "how many observations this column counts for", uniform weights reproduce the unweighted fit exactly, a zero weight removes its observation, and integer weights summing to n give the fit of the sample in which each observation is repeated that many times. The stored log-likelihood, and with it aic, bic and deviance, are the weighted ones; nobs stays n. With pseudo_values=false the rank transformation is the weighted one of pseudos(X; weights), which ranks each margin by weighted mass under the same tie conventions.
The rank inversions :itau, :irho, :ibeta and :itau_irho invert the weighted sample measure: Kendall's tau-b, Spearman's rho and Blomqvist's beta of the sample in which observation j is repeated w[j] times, written so that they extend to real weights. Kendall's tau-b is one :iupper and the nonparametric estimators refuse weights.
The Sklar route reads the same weights at every step. Margin i is fitted by Distributions.fit(Mᵢ, xᵢ, w), the weighted maximum-likelihood fit that Distributions.jl defines for the families with weighted sufficient statistics (Normal, Exponential, Gamma, Poisson, … ); a margin family without one is refused by name, and a zero-weight observation is dropped before the margin sees it, so it may lie outside the margin's support. :ecdf ranks by weighted mass, the copula is fitted with the same weights, and the stored log-likelihood is the weighted one. Unit weights reproduce the unweighted margins up to rounding, because Distributions.jl reduces its weighted sufficient statistics in another order.
Inference reads the weights with the same meaning. infer(M; method=:hessian) inverts the observed information of the weighted log-likelihood, which is that of the replicated sample, with the zero-weight columns dropped as they are before the fit; :godambe, :godambe_pairwise and :bootstrap draw each resample of size n with observation j taken with probability w[j] / n, then compute the moment or refit the estimator on the resample unweighted, which is the nonparametric bootstrap of the replicated sample. :jackknife refuses a weighted model: the delete-one jackknife of the replicated sample needs every weight to be at least one, which after normalization to n holds for unit weights only. The composite goodness-of-fit tests still refuse a weighted model.
Nothing here decides what the weights are. Reading a weight as a count is one reading, under which every procedure above is the unweighted one on the replicated sample; importance weights, whose sandwich covariance scales the scores by
When a parametric family is too restrictive
In addition to parametric families (MLE / rank-based), Copulas.jl exposes several nonparametric or empirical constructions that can be fit through the same high-level API:
EmpiricalCopula(Deheuvels)BetaCopulaBernsteinCopulaCheckerboardCopulaEmpiricalEVCopula(bivariate Pickands estimation or shape-constrained multivariate spectral estimation, selected from the data dimension)
See the dedicated page for theory, properties, and references: Empirical models.
For empirical models with a density, the StatsBase / StatsModels interface works identically: you can call coef, aic, bic, deviance, and residuals, and obtain a full CopulaModel with the same documented model interface. Use fitted_distribution(M) for distribution operations such as CDF evaluation or simulation.
The multivariate EmpiricalEVCopula projection may contain singular spectral components and therefore has no global Lebesgue density. Its quick fit interface, CDF, and sampler remain available, but likelihood-based summaries are not applicable.
The only difference is that empirical models are parameter-free (dof(M) = 0), so finite-dimensional parameter inference is unavailable and information criteria reduce to AIC = BIC = −2 · loglikelihood. Otherwise, all features—including the computation of dependence measures and the REPL summary—behave exactly the same.