← Previous: Uncertainty Analysis | Back to Index | Next: Multivariate Distributions →
Copulas separate the dependence structure of multivariate distributions from their marginal distributions. This separation is formalized by Sklar's theorem [1]: for any multivariate distribution
A copula
- Model marginal distributions independently (each can be any distribution)
- Model dependence separately via the copula
- Combine them to form any joint distribution
The Numerics library provides bivariate copula functions for modeling dependence between two random variables in risk assessment and multivariate analysis.
Elliptical copulas are derived from elliptical distributions (Normal, Student's t). They produce symmetric dependence structures.
The Normal copula is derived from the bivariate normal distribution. Its density function is:
where
The Normal copula has no tail dependence (
The relationship between Kendall's tau and the copula parameter is:
using Numerics.Distributions.Copulas;
// Normal (Gaussian) copula with correlation rho
double rho = 0.7; // Correlation coefficient
var normalCopula = new NormalCopula(rho);
// Evaluate copula density
double u1 = 0.3, u2 = 0.7;
double density = normalCopula.PDF(u1, u2);
Console.WriteLine($"Normal copula density: {density:F4}");The Student's t copula extends the Normal copula by adding symmetric tail dependence. Its density involves the bivariate Student's t distribution with
where
The tail dependence coefficient is:
As
// t-copula with 5 degrees of freedom
int nu = 5;
var tCopula = new StudentTCopula(rho, nu);
double density = tCopula.PDF(u1, u2);
Console.WriteLine($"t-copula density: {density:F4}");
Console.WriteLine($"Upper tail dependence: {tCopula.UpperTailDependence:F4}");
Console.WriteLine($"Lower tail dependence: {tCopula.LowerTailDependence:F4}");Archimedean copulas are defined through a generator function
The copula density is obtained by differentiating:
Each Archimedean family is characterized by its generator, which determines the copula's dependence properties. The Numerics library implements five Archimedean families:
The Clayton copula exhibits lower tail dependence, making it suitable for modeling the joint occurrence of low values (e.g., concurrent droughts in multiple watersheds).
| Property | Value |
|---|---|
| Generator | |
| Inverse generator | |
| CDF | |
| Parameter range | |
| Kendall's tau | |
| Lower tail dependence | |
| Upper tail dependence |
// Clayton copula (lower tail dependence), θ ∈ (0, ∞)
var claytonCopula = new ClaytonCopula(2.0);The Gumbel copula exhibits upper tail dependence, making it the natural choice for modeling joint flood events (e.g., concurrent high flows in tributaries).
| Property | Value |
|---|---|
| Generator | |
| Inverse generator | |
| Parameter range | |
| Kendall's tau | |
| Lower tail dependence | |
| Upper tail dependence |
// Gumbel copula (upper tail dependence), θ ∈ [1, ∞)
var gumbelCopula = new GumbelCopula(2.0);The Frank copula has no tail dependence and produces a symmetric dependence structure. It is useful for modeling moderate, general correlation without emphasizing joint extremes.
| Property | Value |
|---|---|
| Generator | |
| CDF | |
| Parameter range | |
| Tail dependence |
The Frank copula is the only Archimedean copula that allows both positive and negative dependence (
// Frank copula (no tail dependence), θ ∈ (-∞, ∞) \ {0}
var frankCopula = new FrankCopula(5.0);The Joe copula has upper tail dependence, similar to the Gumbel copula, but with a different dependence structure in the body of the distribution.
| Property | Value |
|---|---|
| Generator | |
| Parameter range | |
| Lower tail dependence | |
| Upper tail dependence |
// Joe copula (upper tail dependence), θ ∈ [1, ∞)
var joeCopula = new JoeCopula(2.0);The AMH copula models weak dependence structures and has no tail dependence. Its parameter range is limited to
| Property | Value |
|---|---|
| Generator | |
| Parameter range | |
| Kendall's tau range | |
| Tail dependence |
// Ali-Mikhail-Haq copula (weak dependence), θ ∈ [-1, 1]
var amhCopula = new AMHCopula(0.5);The Independence (product) copula
The copula has no parameters: NumberOfCopulaParameters is zero, SetCopulaParameters is a no-op, and the copula is permanently valid — no assignment can invalidate it. Parameter estimation via BivariateCopulaEstimation.Estimate is a benign no-op for it. Its density is identically 1, its conditional CDF is
// Independence (product) copula — no parameters
var independence = new IndependenceCopula();
double cdf = independence.CDF(0.5, 0.7); // 0.35 = 0.5 · 0.7
double pdf = independence.PDF(0.5, 0.7); // 1.0| Copula | Tail Dependence | Parameter Range | Best For |
|---|---|---|---|
| Normal | None | General symmetric dependence | |
| Student-t | Symmetric |
|
Heavy-tailed joint extremes |
| Clayton | Lower tail | Joint low extremes (droughts) | |
| Gumbel | Upper tail | Joint high extremes (floods) | |
| Frank | None | Moderate symmetric dependence | |
| Joe | Upper tail | Strong upper tail dependence | |
| AMH | None | Weak dependence structures | |
| Independence | None | (no parameters) | Independent variables; the no-dependence default |
The copula parameter can be estimated from data using the relationship between Kendall's tau and the copula parameter. The Numerics library provides the SetThetaFromTau method for Archimedean copulas:
using System.Linq;
using Numerics.Data.Statistics;
using Numerics.Distributions;
using Numerics.Distributions.Copulas;
// Sample paired observations (e.g., peak flow and volume)
double[] x = { 1200, 1500, 1100, 1800, 1350, 1600, 1250, 1450, 1900, 1300 };
double[] y = { 45, 52, 42, 65, 48, 58, 44, 51, 68, 46 };
// Step 1: Fit marginal distributions
var gevX = new GeneralizedExtremeValue();
gevX.Estimate(x, ParameterEstimationMethod.MethodOfLinearMoments);
var gevY = new GeneralizedExtremeValue();
gevY.Estimate(y, ParameterEstimationMethod.MethodOfLinearMoments);
// Step 2: Transform to uniform margins using fitted CDFs
double[] u = x.Select(xi => gevX.CDF(xi)).ToArray();
double[] v = y.Select(yi => gevY.CDF(yi)).ToArray();
// Step 3: Estimate copula parameter using rank correlation
double tau = Correlation.KendallsTau(x, y);
Console.WriteLine($"Kendall's tau: {tau:F3}");
// The tau-to-parameter relationships:
// Clayton: θ = 2τ/(1-τ) for τ > 0
// Gumbel: θ = 1/(1-τ) for τ > 0
// Normal: ρ = sin(πτ/2)Alternatively, copula parameters can be estimated by maximizing the pseudo-log-likelihood, which uses only the copula density evaluated at empirical probability-integral transforms:
// The BivariateCopula base class provides likelihood methods:
// PseudoLogLikelihood — copula-only log-likelihood
// IFMLogLikelihood — inference functions for margins (with pre-estimated marginals)
// LogLikelihood — full log-likelihood (copula + marginals)A copula's dependence structure serializes to a single XML element through ToXElement(): the copula type by enumeration name and the parameters as a pipe-delimited, invariant-culture, round-trip (G17) string. Marginal distributions are deliberately not part of the element — consumers own their marginals and attach them separately. CopulaFactory reconstructs a validated copula from either the element or a bare CopulaType:
using System.Xml.Linq;
using Numerics.Distributions.Copulas;
var copula = new ClaytonCopula(2.0);
XElement element = copula.ToXElement(); // <Copula Type="Clayton" Parameters="2" />
BivariateCopula restored = CopulaFactory.CreateCopula(element); // parameters bit-identical
BivariateCopula fresh = CopulaFactory.CreateCopula(CopulaType.Gumbel); // default parametersConstruct a bivariate distribution with arbitrary marginals and specified dependence:
using System.Linq;
using Numerics.Data.Statistics;
using Numerics.Distributions;
using Numerics.Distributions.Copulas;
// Step 1: Define marginal distributions
var margin1 = new LogNormal(4.0, 0.5); // Streamflow
var margin2 = new Gumbel(100, 20); // Peak stage
// Step 2: Define dependence via copula
double rho = 0.8; // Strong positive correlation
var copula = new NormalCopula(rho);
// Step 3: Sample from joint distribution
int n = 1000;
var samples = copula.GenerateRandomValues(n);
// Transform uniforms to actual distributions
double[] flow = new double[n];
double[] stage = new double[n];
for (int i = 0; i < n; i++)
{
flow[i] = margin1.InverseCDF(samples[i, 0]);
stage[i] = margin2.InverseCDF(samples[i, 1]);
}
Console.WriteLine($"Generated {n} correlated samples");
Console.WriteLine($"Flow range: [{flow.Min():F1}, {flow.Max():F1}]");
Console.WriteLine($"Stage range: [{stage.Min():F1}, {stage.Max():F1}]");
// Empirical correlation
var correlation = Correlation.Pearson(flow, stage);
Console.WriteLine($"Sample correlation: {correlation:F3}");The sampling algorithm uses the conditional method: generate GenerateRandomValues method uses Latin Hypercube sampling for better coverage of the probability space.
For dam safety, estimate probability of joint high flow and high stage:
// Critical thresholds
double flowThreshold = 10000; // cfs
double stageThreshold = 150; // feet
// Compute joint exceedance probability
double u1 = margin1.CDF(flowThreshold);
double u2 = margin2.CDF(stageThreshold);
// P(Flow > threshold AND Stage > threshold)
// = 1 - u1 - u2 + C(u1, u2)
double jointExceedance = copula.ANDJointExceedanceProbability(u1, u2);
Console.WriteLine($"Joint exceedance probability: {jointExceedance:E4}");
Console.WriteLine($"Return period: {1.0 / jointExceedance:F1} years");The AND joint exceedance probability is computed as
Given flow, what is the conditional distribution of stage? The forward conditional CDF (the h-function of the vine-copula literature [2]) is the partial derivative of the copula with respect to the conditioning variable:
where ConditionalCDF(u, v), with an exact analytic implementation per family:
| Copula | |
|---|---|
| Archimedean (generic) | |
| Clayton | |
| Frank | |
| Gumbel |
|
| Joe |
|
| AMH |
|
| Normal | |
| Student-t |
|
| Independence |
The scalar InverseConditionalCDF(u, t) inverts the h-function in InverseCDF returns (the Student-t copula is the exception: it allocates two Student's t distribution objects per call), and the array form InverseCDF(u, t) returns the pair [u, v] on top of it — the conditional-simulation surface. The base-class ConditionalCDF also provides a central-finite-difference fallback over CDF, so an external subclass that implements only CDF still gets a correct approximation.
// Observed flow
double observedFlow = 12000;
double uFlow = margin1.CDF(observedFlow);
// Exact conditional CDF of stage given flow
double uStage = margin2.CDF(15);
double h = copula.ConditionalCDF(uFlow, uStage);
Console.WriteLine($"Given flow = {observedFlow:F0} cfs:");
Console.WriteLine($" P(Stage > 15 | Flow = {observedFlow}) = {1 - h:P1}");
// Conditional quantile: the stage exceeded with 1% probability given the flow
double vStage = copula.InverseConditionalCDF(uFlow, 0.99);
Console.WriteLine($" 99th percentile stage given the flow = {margin2.InverseCDF(vStage):F1}");Tail dependence measures the probability of observing an extreme value in one variable given that the other is already extreme. The upper tail dependence coefficient is:
The lower tail dependence coefficient is defined analogously as
A copula has tail dependence if
// Tail dependence comparison using UpperTailDependence / LowerTailDependence properties
Console.WriteLine("Tail Dependence Properties:");
Console.WriteLine($" Student-t(ρ=0.7, ν=5): λ_U = {new StudentTCopula(0.7, 5).UpperTailDependence:F4}");
Console.WriteLine($" Student-t(ρ=0.7, ν=20): λ_U = {new StudentTCopula(0.7, 20).UpperTailDependence:F4}");
Console.WriteLine($" Normal(ρ=0.7): λ_U = {new NormalCopula(0.7).UpperTailDependence:F4}");
Console.WriteLine($" Clayton(θ=2): λ_L = {new ClaytonCopula(2.0).LowerTailDependence:F4}");
Console.WriteLine($" Gumbel(θ=2): λ_U = {new GumbelCopula(2.0).UpperTailDependence:F4}");
Console.WriteLine($" Frank(θ=5): λ_U = {new FrankCopula(5.0).UpperTailDependence:F4}");For problems with more than two variables, there are several approaches:
- Multivariate Normal distribution: Use when Gaussian dependence is appropriate
- Nested Archimedean copulas: Hierarchical structure for grouped variables
- Vine copulas: Build from pairwise bivariate copulas (C-vine, D-vine, R-vine)
For hydrologic applications with 3+ correlated variables (e.g., peak flow, volume, duration), consider using the Multivariate Normal distribution for the initial analysis, which is available in the Numerics.Distributions namespace. See the Multivariate Distributions documentation for details.
-
Check for dependence: Use scatter plots and correlation tests before applying copulas. Kendall's tau is preferred over Pearson's
$r$ because it is rank-based and invariant under monotonic transformations - Choose copula family: Match tail behavior to application — Gumbel for joint highs, Clayton for joint lows, Student-t for symmetric tail dependence
- Validate fit: Compare empirical and theoretical joint exceedance curves. Use the pseudo-log-likelihood to compare copula families
-
Sample size: Need sufficient data (
$n > 50$ –$100$) for reliable parameter estimation via Kendall's tau - Non-stationarity: Check if dependence structure is time-varying — a copula fit to historical data may not represent future conditions
- Assumes marginals are correctly specified — copula inference is only valid when the marginal distributions are well-fitted
- Bivariate copulas may not capture complex nonlinear dependencies in higher dimensions
- Parameter estimation is challenging with limited data, especially for tail dependence
- Tail dependence is difficult to estimate from finite samples — the tail region by definition has few observations
[1] R. B. Nelsen, An Introduction to Copulas, 2nd ed., New York: Springer, 2006.
[2] H. Joe, Dependence Modeling with Copulas, Boca Raton: CRC Press, 2014.
[3] C. Genest and A.-C. Favre, "Everything you always wanted to know about copula modeling but were afraid to ask," Journal of Hydrologic Engineering, vol. 12, no. 4, pp. 347-368, 2007.
[4] G. Salvadori, C. De Michele, N. T. Kottegoda and R. Rosso, Extremes in Nature: An Approach Using Copulas, Dordrecht: Springer, 2007.
← Previous: Uncertainty Analysis | Back to Index | Next: Multivariate Distributions →