Skip to content

Copula families

The base class

rcopula.core.base.Copula

Copula(params: ArrayLike, dim: int = 2, *, free: ArrayLike | None = None)

Bases: ABC

Common interface shared by every copula family in the package.

A copula describes only how variables move together, separately from what each variable looks like on its own. It works on values in the unit interval [0, 1] (probabilities / ranks), one column per variable. You do not create a Copula directly; you create a concrete family such as :class:~rcopula.ClaytonCopula or :class:~rcopula.GaussianCopula, which all share the methods documented here (pdf, cdf, rvs, tau, from_tau and so on).

Copulas are immutable: methods such as :meth:with_params return a new object instead of changing the existing one.

Parameters:

Name Type Description Default
params array_like of float, shape (n_params,)

Parameter values, in the order given by :attr:param_names. A value of nan means "not yet known, to be estimated by fitting"; such a copula can be fitted but not evaluated.

required
dim int

Number of variables (columns) the copula joins. Must be at least 2.

2
free array_like of bool, shape (n_params,), or None

Keyword-only. Which parameters are estimated when fitting (True) and which are held at their current value (False). None means all parameters are free. A scalar is broadcast to every parameter.

None

Attributes:

Name Type Description
name str

Human-readable family name, e.g. "Clayton".

param_names tuple of str

Names of the parameters, in the order they appear in :attr:params.

dim int

Number of variables.

params numpy.ndarray of float, shape (n_params,)

Current parameter values (read-only).

free numpy.ndarray of bool, shape (n_params,)

Mask of parameters that are estimated when fitting.

Raises:

Type Description
ValueError

If dim < 2, if the number of parameters is wrong for the family, or if a (non-nan) parameter lies outside :attr:param_bounds.

Notes

Subclasses must define :attr:param_names, :attr:param_bounds, and implement :meth:_logpdf, :meth:_cdf and :meth:_rvs.

Examples:

>>> from rcopula import ClaytonCopula
>>> c = ClaytonCopula(2.0, dim=2)
>>> c.dim, c.param_names
(2, ('theta',))
>>> c.rvs(3, random_state=0).shape
(3, 2)

dim property

dim: int

Number of variables the copula joins (its dimension d).

Returns:

Type Description
int

The dimension, at least 2.

k_dim property

k_dim: int

Same as :attr:dim; the name statsmodels uses.

Returns:

Type Description
int

The dimension, at least 2.

params property

params: NDArray[float64]

Current parameter values, in the order of :attr:param_names.

The array is read-only; use :meth:with_params to get a copula with different values.

Returns:

Type Description
numpy.ndarray of float, shape (n_params,)

Parameter vector. nan entries are still to be estimated.

free property

free: NDArray[bool_]

Which parameters are estimated when fitting and which are held fixed.

Returns:

Type Description
numpy.ndarray of bool, shape (n_params,)

True for a free (estimated) parameter, False for a fixed one. Read-only; use :meth:fix_params to change it.

n_params property

n_params: int

How many parameters fitting will estimate (fixed ones are not counted).

Returns:

Type Description
int

Number of True entries in :attr:free.

param_bounds abstractmethod property

param_bounds: list[tuple[float, float]]

Allowed range of each parameter, as (lower, upper) pairs.

Returns:

Type Description
list of tuple of (float, float), length n_params

One (lower, upper) pair per parameter, in the order of :attr:param_names. An unbounded side is inf or -inf.

Notes

Open interval (lower, upper) for each parameter.

Bounds may depend on :attr:dim — Clayton admits negative dependence in d = 2 but not beyond, for instance.

with_params

with_params(params: ArrayLike) -> Copula

Return a copy of this copula with new parameter values.

The original copula is left unchanged. The free/fixed mask is kept.

Parameters:

Name Type Description Default
params array_like of float, shape (n_params,)

New parameter values, in the order of :attr:param_names. A scalar is accepted for one-parameter families.

required

Returns:

Type Description
Copula

A new copula of the same family and dimension.

Raises:

Type Description
ValueError

If the number of values is wrong or a value is out of range.

Examples:

>>> from rcopula import ClaytonCopula
>>> ClaytonCopula(2.0).with_params(3.0).params
array([3.])

fix_params

fix_params(free: ArrayLike) -> Copula

Return a copy in which some parameters are held fixed during fitting.

Use this when you know some parameters already (for example the degrees of freedom of a t copula) and only want to estimate the rest.

Parameters:

Name Type Description Default
free array_like of bool, shape (n_params,)

True for parameters to estimate, False for parameters to hold at their current value. A scalar is broadcast.

required

Returns:

Type Description
Copula

A new copula with the same parameter values and the new mask.

Notes

Mirrors R's fixParam / fixedParam<-. A False entry holds that parameter at its current value during estimation.

logpdf

logpdf(u: ArrayLike) -> NDArray[np.float64]

Natural logarithm of the copula density at each point.

Prefer this over log(pdf(u)) when summing over many points (as in a likelihood): it avoids underflow when densities are tiny.

Parameters:

Name Type Description Default
u array_like of float, shape (n, d) or (d,)

Points in the unit cube, one row per point and one column per variable. A 1-D array is treated as a single point.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Log density for each row. -inf for points outside the open unit cube, nan for rows containing nan.

Raises:

Type Description
ValueError

If any parameter is still nan (unfitted), or if u does not have d columns.

Notes

Points outside :math:[0,1]^d have zero density, hence -inf log density — matching R, which treats out-of-range coordinates as boundary values even when another coordinate is NaN.

Examples:

>>> from rcopula import IndependenceCopula
>>> IndependenceCopula(dim=2).logpdf([[0.3, 0.7], [0.5, 1.5]])
array([  0., -inf])

pdf

pdf(u: ArrayLike) -> NDArray[np.float64]

Copula density at each point: how concentrated the probability is there.

Values above 1 mean points like this are more common than they would be if the variables were independent; values below 1 mean less common.

Parameters:

Name Type Description Default
u array_like of float, shape (n, d) or (d,)

Points in the unit cube, one row per point and one column per variable. A 1-D array is treated as a single point.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Density for each row; 0 outside the open unit cube.

Raises:

Type Description
ValueError

If any parameter is still nan (unfitted), or if u does not have d columns.

See Also

logpdf : The same quantity on the log scale, safer for likelihoods.

Examples:

>>> from rcopula import IndependenceCopula
>>> IndependenceCopula(dim=2).pdf([0.3, 0.7])
array([1.])

cdf

cdf(u: ArrayLike) -> NDArray[np.float64]

Probability that every variable is at or below the given point, C(u).

For d = 2 and a point (a, b) this is P(U_1 <= a and U_2 <= b), the copula's cumulative distribution function.

Parameters:

Name Type Description Default
u array_like of float, shape (n, d) or (d,)

Points, one row per point and one column per variable. Values outside [0, 1] are clipped into it. A 1-D array is treated as a single point.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Probabilities in [0, 1], one per row.

Raises:

Type Description
ValueError

If any parameter is still nan (unfitted), or if u does not have d columns.

Examples:

>>> from rcopula import IndependenceCopula
>>> IndependenceCopula(dim=2).cdf([[0.5, 0.5], [0.2, 1.0]])
array([0.25, 0.2 ])

rvs

rvs(size: int = 1, random_state: Generator | int | None = None) -> NDArray[np.float64]

Generate random samples that follow this copula's dependence.

Each row is one simulated observation; each column is uniformly distributed on (0, 1) on its own, but the columns are dependent in the way the copula describes. To get samples on real-world scales, transform each column with a quantile function, or use :class:~rcopula.CopulaDistribution.

Parameters:

Name Type Description Default
size int

Number of observations (rows) to draw. Must be non-negative.

1
random_state (int, Generator or None)

Seed or generator. Accepting a Generator is the modern idiom; an int is promoted via np.random.default_rng. None gives fresh, unreproducible randomness.

None

Returns:

Type Description
numpy.ndarray of float, shape (size, d)

Samples with values in (0, 1).

Raises:

Type Description
ValueError

If any parameter is still nan (unfitted), or size < 0.

Examples:

>>> from rcopula import ClaytonCopula
>>> u = ClaytonCopula(2.0, dim=3).rvs(4, random_state=0)
>>> u.shape
(4, 3)
>>> bool(((u > 0) & (u < 1)).all())
True

prob

prob(lower: ArrayLike, upper: ArrayLike) -> float

Probability that every variable falls inside a given box.

Gives P(lower_j < U_j <= upper_j for all j) — the C-volume of a box. Useful for questions like "how likely are both variables to land in their bottom 10% at the same time?".

Parameters:

Name Type Description Default
lower array_like of float, shape (d,)

Lower corner of the box, one value per variable, in [0, 1].

required
upper array_like of float, shape (d,)

Upper corner of the box, elementwise >= lower, in [0, 1].

required

Returns:

Type Description
float

Probability of the box, in [0, 1] up to rounding error.

Raises:

Type Description
ValueError

If lower or upper does not have length d, if lower > upper anywhere, if d > 20 (too many corners), or if the copula is unfitted.

Notes

Computed by the inclusion-exclusion sum over the 2**d vertices,

.. math:: \sum_{v \in {0,1}^d} (-1)^{\sum_j v_j}\, C(x_v)

where x_v takes lower_j when v_j = 1 and upper_j otherwise. Being non-negative for every box is exactly what makes C a copula.

Examples:

For the independence copula this is just the product of the side lengths:

>>> import numpy as np
>>> from rcopula import IndependenceCopula
>>> c = IndependenceCopula(dim=2)
>>> float(np.round(c.prob([0.25, 0.5], [1 / 3, 1.0]), 12))
0.041666666667

tau abstractmethod

tau() -> float

Strength of dependence as Kendall's tau, a rank correlation in [-1, 1].

Kendall's tau is the probability that two random observations are ordered the same way in both variables, minus the probability they are ordered oppositely. 0 means no (rank) association, 1 means the variables always move together, -1 always oppositely. This is the value implied by the copula itself (the population value), not an estimate from data.

Returns:

Type Description
float

Kendall's tau in [-1, 1]. For d > 2 families define it on a pair of variables; see the family's docstring.

rho abstractmethod

rho() -> float

Strength of dependence as Spearman's rho, a rank correlation in [-1, 1].

Spearman's rho is the ordinary correlation of the variables' ranks. 0 means no (rank) association, 1 perfect positive, -1 perfect negative. This is the population value implied by the copula.

Returns:

Type Description
float

Spearman's rho in [-1, 1]. For d > 2 families define it on a pair of variables; see the family's docstring.

beta

beta() -> float

Strength of dependence as Blomqvist's beta, based on medians.

Blomqvist's beta compares how often all variables sit on the same side of their medians (all below or all above) against what independence would give. 0 means independence at the centre; positive values mean the variables tend to be above or below their medians together.

Returns:

Type Description
float

Blomqvist's beta. In [-1, 1] for d = 2.

Raises:

Type Description
ValueError

If the copula is unfitted (any parameter nan).

Notes

Population Blomqvist's beta, 2**d * C(1/2, ..., 1/2) - 1 rescaled.

For d = 2 this is 4 * C(1/2, 1/2) - 1. Blomqvist's beta depends on the copula only at the centre point, which makes it cheap but insensitive to the tails.

Examples:

>>> from rcopula import IndependenceCopula
>>> IndependenceCopula(dim=2).beta()
0.0

lambda_ abstractmethod

lambda_() -> TailDependence

How strongly the variables tend to be extreme together, in each tail.

The lower coefficient is about joint extreme lows (e.g. simultaneous crashes), the upper one about joint extreme highs. Both are in [0, 1]; 0 means joint extremes become negligible far enough out in that tail. A Gaussian copula has 0 in both tails; Clayton has lower but not upper tail dependence; Gumbel the reverse.

Returns:

Type Description
TailDependence

Named tuple (lower, upper) of floats in [0, 1].

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> Copula

Create a copula of this family whose Kendall's tau equals a target value.

Handy when you know (or have estimated) the rank correlation you want and need the matching parameter, e.g. ClaytonCopula.from_tau(0.5).

Parameters:

Name Type Description Default
tau float

Target Kendall's tau. Must lie in the range the family can reach (many families allow only 0 <= tau < 1).

required
dim int

Number of variables of the copula to build.

2
**kwargs Any

Extra family-specific constructor arguments (e.g. df for a t copula), where the family supports them.

{}

Returns:

Type Description
Copula

A new copula of the calling class.

Raises:

Type Description
NotImplementedError

If the family does not support calibration from Kendall's tau.

ValueError

If tau is outside the range the family can attain (raised by the families that implement this).

Notes

The Pythonic spelling of R's iTau. A classmethod rather than an instance method because it constructs rather than mutates.

Examples:

>>> from rcopula import ClaytonCopula
>>> c = ClaytonCopula.from_tau(0.5)
>>> round(float(c.params[0]), 10), round(c.tau(), 10)
(2.0, 0.5)

from_rho classmethod

from_rho(rho: float, dim: int = 2, **kwargs: Any) -> Copula

Create a copula of this family whose Spearman's rho equals a target value.

The same idea as :meth:from_tau, but matching Spearman's rank correlation instead (R's iRho).

Parameters:

Name Type Description Default
rho float

Target Spearman's rho, within the range the family can reach.

required
dim int

Number of variables of the copula to build.

2
**kwargs Any

Extra family-specific constructor arguments, where supported.

{}

Returns:

Type Description
Copula

A new copula of the calling class.

Raises:

Type Description
NotImplementedError

If the family does not support calibration from Spearman's rho.

ValueError

If rho is outside the attainable range (raised by the families that implement this).

calibrated

calibrated(measure: str, value: float) -> Copula

Return a copy of this family re-tuned to a target tau or rho.

Like :meth:from_tau / :meth:from_rho, but called on an existing copula so its family and dimension are reused.

Parameters:

Name Type Description Default
measure ('tau', 'rho')

Which rank correlation to match: Kendall's tau or Spearman's rho.

"tau"
value float

Target value of that measure.

required

Returns:

Type Description
Copula

A new copula of the same family and dimension.

Raises:

Type Description
ValueError

If measure is not "tau" or "rho", or value is out of the family's range.

NotImplementedError

If the family does not support that calibration.

Notes

The instance-level counterpart of :meth:from_tau / :meth:from_rho, and what :func:~rcopula.fit.fit calls for method="itau" and "irho". Families whose construction needs more than (value, dim) -- a rotation has to know what it is rotating -- override this instead of the classmethods.

Examples:

>>> from rcopula import ClaytonCopula
>>> c = ClaytonCopula(1.0, dim=2).calibrated("tau", 0.5)
>>> round(float(c.params[0]), 10)
2.0

describe

describe() -> str

Short human-readable summary of the family, dimension and parameters.

In the spirit of R's describeCop. Fixed parameters are marked (fixed).

Returns:

Type Description
str

One-line description.

Examples:

>>> from rcopula import ClaytonCopula
>>> ClaytonCopula(2.0).describe()
'Clayton copula, dim 2, theta=2'

rcopula.core.base.TailDependence

Bases: NamedTuple

How likely two variables are to hit extremes together, in each tail.

The pair returned by :meth:Copula.lambda_. lower measures the chance that both variables are very small at the same time (joint crashes); upper measures the chance that both are very large at the same time (joint booms). It behaves like an ordinary tuple, so lower, upper = cop.lambda_() works.

Attributes:

Name Type Description
lower float

Lower tail-dependence coefficient, in [0, 1]. Zero means joint extreme lows become vanishingly rare relative to single extreme lows.

upper float

Upper tail-dependence coefficient, in [0, 1]. Zero means joint extreme highs become vanishingly rare relative to single extreme highs.

Notes

lower is :math:\lambda_L = \lim_{u \downarrow 0} C(u,u)/u and upper is :math:\lambda_U = \lim_{u \uparrow 1} (1 - 2u + C(u,u))/(1-u). Both lie in [0, 1]. A non-zero value means joint extremes occur with probability that does not vanish relative to marginal extremes — the property a Gaussian copula lacks and a t or Clayton copula has.

Examples:

>>> from rcopula.core.base import TailDependence
>>> td = TailDependence(lower=0.5, upper=0.0)
>>> td.lower, td.upper
(0.5, 0.0)

Elliptical

rcopula.core.elliptical.GaussianCopula

GaussianCopula(params: ArrayLike = np.nan, dim: int = 2, dispstr: str = 'ex', *, free: ArrayLike | None = None)

Bases: EllipticalCopula

The Gaussian (normal) copula: the dependence structure of a multivariate normal.

It links variables through a correlation matrix, exactly as a multivariate normal does, but lets each variable keep whatever marginal distribution you choose. It is the standard default and is easy to fit and interpret, but it assumes extreme values in different variables are essentially unrelated (see below); use :class:StudentCopula when joint extremes matter.

Parameters:

Name Type Description Default
params float or array_like of float

Correlation parameter(s) in [-1, 1]; how many depends on dispstr. nan means "to be estimated".

nan
dim int

Number of variables d; at least 2.

2
dispstr ('ex', 'ar1', 'toep', 'un')

Correlation structure: exchangeable (1 parameter), autoregressive (1), Toeplitz (d - 1) or unstructured (d * (d - 1) / 2). See :class:EllipticalCopula.

"ex"
free array_like of bool or None

Keyword-only mask of parameters :func:~rcopula.fit.fit may change.

None

Attributes:

Name Type Description
dispstr str

The correlation structure.

params numpy.ndarray of float

The correlation parameters (read-only).

Raises:

Type Description
ValueError

For an unknown dispstr, the wrong number of parameters, values out of range, or a correlation matrix that is not positive definite.

Notes

:math:C(\mathbf{u}) = \Phi_\Sigma(\Phi^{-1}(u_1), \dots, \Phi^{-1}(u_d)).

No tail dependence in either tail, for any correlation short of 1. That is its defining weakness: it says joint extremes are asymptotically independent, which is empirically false for financial returns and is the reason its use for CDO pricing aged so badly.

Examples:

>>> import numpy as np
>>> from rcopula import GaussianCopula
>>> g = GaussianCopula(0.5, dim=2)
>>> float(round(g.tau(), 12))
0.333333333333
>>> g.lambda_()
TailDependence(lower=0.0, upper=0.0)
>>> float(round(g.cdf([[0.5, 0.5]])[0], 12))
0.333333333333

Calibration round-trips:

>>> float(round(GaussianCopula.from_tau(1 / 3).params[0], 12))
0.5

lambda_

lambda_() -> TailDependence

Tail dependence: always zero in both tails for the Gaussian copula.

Tail dependence is the chance that one variable is extreme given that another is equally extreme, in the limit. The Gaussian copula has none for any correlation below 1 -- its defining limitation.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=0.0).

Raises:

Type Description
ValueError

If the parameters are still unspecified (nan).

rcopula.core.elliptical.StudentCopula

StudentCopula(params: ArrayLike = np.nan, dim: int = 2, dispstr: str = 'ex', *, df: float = 4.0, df_fixed: bool = False, free: ArrayLike | None = None)

Bases: EllipticalCopula

The Student-t copula: like the Gaussian, but extremes tend to happen together.

It uses a correlation matrix like :class:GaussianCopula plus a degrees-of-freedom parameter df that controls how often variables hit extremes at the same time: small df means strong joint extremes, large df approaches the Gaussian. It is the usual choice for financial returns, where crashes are shared.

Parameters:

Name Type Description Default
params float or array_like of float

Correlation parameter(s) in [-1, 1], as for :class:GaussianCopula. May optionally include df as one extra final element, in which case the df keyword is ignored. nan means "to be estimated".

nan
dim int

Number of variables d; at least 2.

2
dispstr ('ex', 'ar1', 'toep', 'un')

Correlation structure; see :class:EllipticalCopula.

"ex"
df float

Keyword-only. Degrees of freedom, any positive real (at least 0.01); need not be an integer.

4.0
df_fixed bool

Keyword-only. If True, :func:~rcopula.fit.fit keeps df at its given value and estimates only the correlations. Ignored when free is given.

False
free array_like of bool or None

Keyword-only mask of parameters (correlations then df) that :func:~rcopula.fit.fit may change.

None

Attributes:

Name Type Description
df float

Degrees of freedom.

df_fixed bool

Whether df was requested fixed.

dispstr str

The correlation structure.

params numpy.ndarray of float

Correlation parameters followed by df (read-only).

Raises:

Type Description
ValueError

For an unknown dispstr, the wrong number of parameters, values out of range, or a correlation matrix that is not positive definite.

Notes

:math:C(\mathbf{u}) = t_{\nu,\Sigma}(t_\nu^{-1}(u_1), \dots).

Symmetric tail dependence in both tails,

.. math::

\lambda_L = \lambda_U
  = 2\, t_{\nu+1}\!\left(-\sqrt{\tfrac{(\nu+1)(1-\rho)}{1+\rho}}\right),

which is strictly positive for every finite df — even at :math:\rho = 0. As df grows the copula converges to the Gaussian and the tail dependence vanishes.

Non-integer degrees of freedom are supported (R's pmvt refuses them, though fitCopula happily produces them).

Examples:

>>> import numpy as np
>>> from rcopula import StudentCopula
>>> t = StudentCopula(0.5, df=4, dim=2)
>>> float(round(t.tau(), 12))        # identical to the Gaussian's
0.333333333333
>>> float(round(t.lambda_().lower, 10))
0.2531699951

Tail dependence survives zero correlation, unlike the Gaussian:

>>> float(round(StudentCopula(0.0, df=3).lambda_().upper, 10))
0.1161165235

and vanishes as the degrees of freedom grow:

>>> bool(StudentCopula(0.5, df=1e6).lambda_().upper < 1e-4)
True

df property

df: float

Degrees of freedom: smaller means more joint extremes.

Returns:

Type Description
float

The last entry of :attr:params; nan if not yet estimated.

param_bounds property

param_bounds: list[tuple[float, float]]

Allowed range (lower, upper) for each parameter.

Returns:

Type Description
list of tuple of (float, float), length n_corr + 1

The correlation bounds of :class:EllipticalCopula, followed by (0.01, inf) for df.

lambda_

lambda_() -> TailDependence

Tail dependence: how likely joint extremes are, equal in both tails.

Tail dependence is the chance that one variable is extreme given that another is equally extreme, in the limit. For the t copula it is the same in the lower and upper tail and positive for every finite df.

Returns:

Type Description
TailDependence

lower == upper, each in [0, 1], from :math:2\, t_{\nu+1}(-\sqrt{(\nu+1)(1-\rho)/(1+\rho)}).

Raises:

Type Description
ValueError

If the parameters are still unspecified (nan), or if dim > 2 and the pairs do not all share one correlation (see Notes).

Notes

Tail dependence is a pairwise quantity. It is a single number when dim == 2 or when every pair has the same correlation (always so for dispstr="ex"). Otherwise each pair has its own value and no single answer is honest, so this raises rather than silently reporting one pair; take the pair you want with :func:~rcopula.marginal_copula, e.g. marginal_copula(cop, [0, 2]).lambda_().

Examples:

>>> from rcopula import StudentCopula, marginal_copula
>>> cop = StudentCopula([0.2, 0.5, 0.7], dim=3, dispstr="un", df=4)
>>> cop.lambda_()
Traceback (most recent call last):
    ...
ValueError: tail dependence differs between pairs of this 3-dimensional t copula...
>>> pair = marginal_copula(cop, [1, 2])
>>> bool(pair.lambda_().upper > 0)
True

rho

rho() -> Any

Spearman's rho: the correlation of the variables' ranks, computed numerically.

Spearman's rho is the ordinary correlation of the uniform-scale variables, between -1 and 1. The t copula has no closed form for it, so it is computed by numerical integration (accurate to about twelve digits).

Returns:

Type Description
float or numpy.ndarray of float, shape (d * (d - 1) / 2,)

A float when dim=2 or dispstr="ex"; otherwise one value per pair, in :func:P2p order.

Raises:

Type Description
ValueError

If the parameters are still unspecified (nan).

Notes

Kendall's tau is :math:\frac{2}{\pi}\arcsin\rho for every elliptical copula, so the t copula inherits the Gaussian expression. Spearman's rho does not. The relation :math:\rho_S = \frac{6}{\pi}\arcsin(\rho/2) is specific to the Gaussian copula, and applying it to a t copula is simply wrong: at :math:\rho = 0.5 with 4 degrees of freedom it gives 0.4826 where the true value is 0.4690.

R declines the question and returns NA. There is no closed form, but :math:\rho_S = 12\int\int C - 3 is a two-dimensional integral of a smooth function, and tanh-sinh quadrature settles it to twelve digits. The result is cached, since the integral costs about 80 ms.

Examples:

Below the Gaussian value at the same correlation, and converging up to it as the degrees of freedom grow:

>>> from rcopula import GaussianCopula, StudentCopula
>>> float(round(StudentCopula(0.5, df=4).rho(), 6))
0.46902
>>> float(round(GaussianCopula(0.5).rho(), 6))
0.482584
>>> bool(abs(StudentCopula(0.5, df=1e6).rho() - GaussianCopula(0.5).rho()) < 1e-5)
True

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> StudentCopula

Build a t copula whose Kendall's tau equals the value you ask for.

Uses :math:\rho = \sin(\pi\tau/2), which holds for every elliptical copula; df does not affect tau, so set it through kwargs.

Parameters:

Name Type Description Default
tau float

Target Kendall's tau, strictly between -1 and 1.

required
dim int

Number of variables.

2
**kwargs Any

Passed to the constructor, e.g. df=3 or dispstr="ar1". With "toep" or "un" every pair gets the same correlation, as in :meth:EllipticalCopula.from_tau.

{}

Returns:

Type Description
StudentCopula

Raises:

Type Description
ValueError

If tau is not in (-1, 1), or the correlation is not admissible for dim.

Examples:

>>> from rcopula import StudentCopula
>>> cop = StudentCopula.from_tau(0.5, df=3)
>>> float(round(cop.tau(), 12)), cop.df
(0.5, 3.0)

from_rho classmethod

from_rho(rho: float, dim: int = 2, **kwargs: Any) -> StudentCopula

Build a t copula whose Spearman's rho equals the value you ask for.

Because the t copula's Spearman's rho has no closed form, the correlation is found numerically (Brent's method on :meth:rho), so this takes around a second.

Parameters:

Name Type Description Default
rho float

Target Spearman's rho, strictly between -1 and 1.

required
dim int

Number of variables.

2
**kwargs Any

Passed to the constructor. df (default 4.0) is also used in the inversion, since the answer depends on it. With "toep" or "un" every pair gets the same correlation, as in :meth:EllipticalCopula.from_tau.

{}

Returns:

Type Description
StudentCopula

Raises:

Type Description
ValueError

If rho is not in (-1, 1), is too close to +-1 to be reached by any representable correlation, or the correlation found is not admissible for dim.

Notes

The Gaussian relation :math:\rho_P = 2\sin(\pi\rho_S/6) -- which R uses, and which this method used to use -- is not valid for a t copula, so the inversion is done against the quadrature value instead. Spearman's rho is strictly increasing in the correlation, so the root is bracketed by the whole half-range: [0, 1) for a positive target and (-1, 0] for a negative one. Brent's method then needs about ten quadratures, against the eighty bisection steps used before (which also searched only +-0.2 around the Gaussian value and silently returned the edge of that window when the root lay outside it).

Examples:

>>> from rcopula import StudentCopula
>>> cop = StudentCopula.from_rho(0.6, df=3)
>>> bool(abs(cop.rho() - 0.6) < 1e-8)
True

rcopula.core.elliptical.p2P

p2P(param: ArrayLike, dim: int) -> NDArray[np.float64]

Turn a flat list of pairwise correlations into a full correlation matrix.

Use this to go from the parameter vector of an unstructured (dispstr="un") Gaussian or t copula to the d x d matrix it describes. It is R's p2P; :func:P2p is the inverse.

Parameters:

Name Type Description Default
param array_like of float, shape (d * (d - 1) / 2,)

The below-diagonal entries, one per pair of variables. Any shape is accepted and flattened; only the total count matters. Values are copied in as given -- nothing checks that they lie in [-1, 1] or that the result is positive definite.

required
dim int

Size d of the matrix to build.

required

Returns:

Type Description
numpy.ndarray of float, shape (dim, dim)

Symmetric matrix with ones on the diagonal.

Raises:

Type Description
ValueError

If param does not hold exactly dim * (dim - 1) / 2 values.

Notes

Entries fill the lower triangle column by column, as R does, so for dim=4 the parameter vector is (rho_12, rho_13, rho_14, rho_23, rho_24, rho_34). This is not the row-by-row order of numpy.tril_indices; the two agree only up to dim=3.

Examples:

>>> from rcopula.core.elliptical import p2P
>>> p2P([0.6, 0.3, 0.2], 3)
array([[1. , 0.6, 0.3],
       [0.6, 1. , 0.2],
       [0.3, 0.2, 1. ]])
>>> p2P([12, 13, 14, 23, 24, 34], 4)[[0, 0, 0, 1, 1, 2], [1, 2, 3, 2, 3, 3]]
array([12., 13., 14., 23., 24., 34.])

rcopula.core.elliptical.P2p

P2p(matrix: ArrayLike) -> NDArray[np.float64]

Flatten a correlation matrix into the list of its pairwise correlations.

The inverse of :func:p2P: it reads the below-diagonal entries of a d x d matrix, column by column (R's order), into a vector. It is R's P2p.

Parameters:

Name Type Description Default
matrix array_like of float, shape (d, d)

A square matrix, normally a correlation matrix. Only the strictly lower triangle is read; nothing checks symmetry.

required

Returns:

Type Description
numpy.ndarray of float, shape (d * (d - 1) / 2,)

(m[1, 0], m[2, 0], ..., m[d-1, 0], m[2, 1], ...).

Examples:

>>> import numpy as np
>>> from rcopula.core.elliptical import P2p, p2P
>>> P2p(p2P([0.6, 0.3, 0.2], 3))
array([0.6, 0.3, 0.2])

Archimedean

rcopula.core.archimedean.ClaytonCopula

ClaytonCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: _ConcreteArchimedean

Clayton copula: dependence that is strongest when values are jointly small.

Use it when joint lows matter more than joint highs -- for example losses that crash together but rise independently. Larger theta means stronger dependence; theta = 0 is independence.

Parameters:

Name Type Description Default
theta float

Dependence parameter. Must lie in [-1, inf) for dim=2 and in [0, inf) for dim > 2. nan means "to be estimated".

nan
dim int

Number of variables, at least 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated by :func:~rcopula.fit.fit (None means free).

None

Attributes:

Name Type Description
theta float

The dependence parameter.

dim int

Number of variables.

Raises:

Type Description
ValueError

If dim < 2 or theta is outside the admissible range.

Notes

Generator :math:\psi(t) = (1+t)^{-1/\theta}, with :math:\tau = \theta/(\theta+2) and lower tail dependence :math:\lambda_L = 2^{-1/\theta}.

theta ranges over :math:[-1, \infty) in dim=2 and :math:(0, \infty) beyond, because the generator ceases to be d-monotone for negative theta in higher dimensions.

Examples:

>>> import numpy as np
>>> from rcopula import ClaytonCopula
>>> c = ClaytonCopula(theta=2.0, dim=3)
>>> float(c.tau())
0.5
>>> bool(c.lambda_().lower == 2 ** -0.5)
True
>>> u = c.rvs(1000, random_state=0)
>>> u.shape
(1000, 3)
>>> bool(np.all((u > 0) & (u < 1)))
True

Calibrating to a target dependence, R's iTau:

>>> ClaytonCopula.from_tau(0.5).theta
2.0

rcopula.core.archimedean.GumbelCopula

GumbelCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: _ConcreteArchimedean

Gumbel-Hougaard copula: dependence that is strongest when values are jointly large.

Use it when joint highs (e.g. simultaneous large claims or floods) matter most. It allows only positive dependence; theta = 1 is independence and larger theta is stronger. It is also an extreme-value copula.

Parameters:

Name Type Description Default
theta float

Dependence parameter, theta >= 1. nan means "to be estimated".

nan
dim int

Number of variables, at least 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated by :func:~rcopula.fit.fit (None means free).

None

Attributes:

Name Type Description
theta float

The dependence parameter.

dim int

Number of variables.

Raises:

Type Description
ValueError

If dim < 2 or theta < 1.

Notes

Generator :math:\psi(t) = \exp(-t^{1/\theta}), with :math:\tau = 1 - 1/\theta and upper tail dependence :math:\lambda_U = 2 - 2^{1/\theta}. Requires theta >= 1.

Examples:

>>> from rcopula import GumbelCopula
>>> GumbelCopula.from_tau(0.5).theta
2.0
>>> g = GumbelCopula(theta=2.0)
>>> float(g.tau())
0.5
>>> float(round(g.lambda_().upper, 10))
0.5857864376

rcopula.core.archimedean.FrankCopula

FrankCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: _ConcreteArchimedean

Frank copula: symmetric dependence with no extra clustering of extremes.

Use it when dependence looks the same for lows and highs and extremes are not unusually likely to coincide. In two dimensions it covers both negative (theta < 0) and positive (theta > 0) dependence; theta = 0 is independence.

Parameters:

Name Type Description Default
theta float

Dependence parameter. Any real number for dim=2; theta >= 0 for dim > 2. nan means "to be estimated".

nan
dim int

Number of variables, at least 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated by :func:~rcopula.fit.fit (None means free).

None

Attributes:

Name Type Description
theta float

The dependence parameter.

dim int

Number of variables.

Raises:

Type Description
ValueError

If dim < 2 or theta is outside the admissible range.

Notes

Generator :math:\psi(t) = -\log(1 - (1-e^{-\theta})e^{-t})/\theta. Radially symmetric, no tail dependence, and the full range :math:\tau \in (-1, 1) is attainable in dim=2.

Examples:

>>> from rcopula import FrankCopula
>>> f = FrankCopula(theta=5.0)
>>> float(round(f.tau(), 10))
0.4567009582
>>> float(round(f.rho(), 10))
0.6434871081
>>> f.lambda_()
TailDependence(lower=0.0, upper=0.0)

rcopula.core.archimedean.JoeCopula

JoeCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: _ConcreteArchimedean

Joe copula: dependence concentrated in joint large values, more so than Gumbel.

Use it, like :class:GumbelCopula, when joint highs matter, especially if upper-tail clustering is pronounced relative to the overall dependence. Positive dependence only; theta = 1 is independence.

Parameters:

Name Type Description Default
theta float

Dependence parameter, theta >= 1. nan means "to be estimated".

nan
dim int

Number of variables, at least 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated by :func:~rcopula.fit.fit (None means free).

None

Attributes:

Name Type Description
theta float

The dependence parameter.

dim int

Number of variables.

Raises:

Type Description
ValueError

If dim < 2 or theta < 1.

Notes

Generator :math:\psi(t) = 1 - (1 - e^{-t})^{1/\theta}, theta >= 1. Upper-tail dependent, :math:\lambda_U = 2 - 2^{1/\theta}.

Examples:

>>> from rcopula import JoeCopula
>>> j = JoeCopula(theta=2.0)
>>> float(round(j.tau(), 10))
0.3550659332
>>> float(round(j.lambda_().upper, 10))
0.5857864376

rcopula.core.archimedean.AMHCopula

AMHCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: _ConcreteArchimedean

Ali-Mikhail-Haq (AMH) copula: a simple family for weak dependence only.

Use it when dependence is mild: Kendall's tau can only range from about -0.18 to 1/3. theta = 0 is independence.

Parameters:

Name Type Description Default
theta float

Dependence parameter. Must lie in [-1, 1] for dim=2 and in [0, 1] for dim > 2. nan means "to be estimated".

nan
dim int

Number of variables, at least 2. (R's amhCopula allows only 2.)

2
free array_like of bool, shape (1,), or None

Whether theta is estimated by :func:~rcopula.fit.fit (None means free).

None

Attributes:

Name Type Description
theta float

The dependence parameter.

dim int

Number of variables.

Raises:

Type Description
ValueError

If dim < 2 or theta is outside the admissible range.

Notes

Generator :math:\psi(t) = (1-\theta)/(e^{t}-\theta). Reaches only weak dependence, :math:\tau \in [-0.1817, 1/3], and has no tail dependence.

Examples:

>>> from rcopula import AMHCopula
>>> a = AMHCopula(theta=0.5)
>>> float(round(a.tau(), 10))
0.128764787
>>> a.lambda_()
TailDependence(lower=0.0, upper=0.0)

rcopula.core.archimedean.ArchimedeanGenerator

Bases: ABC

The one-variable function that defines an Archimedean copula family.

An Archimedean copula is fully determined by a single decreasing function of one variable, the generator :math:\psi. This abstract class collects everything a family must know about its generator: the function itself, its inverse, the logs of its derivatives, its admissible parameter range, closed-form dependence measures and how to sample it.

You only need this class to define a new Archimedean family; to use one, take :class:ClaytonCopula, :class:GumbelCopula, :class:FrankCopula, :class:JoeCopula or :class:AMHCopula, or wrap a generator in :class:ArchimedeanCopula.

Every method is stateless: the parameter theta is passed in on each call rather than stored on the object, so a likelihood optimiser can evaluate many theta values without allocating anything.

Attributes:

Name Type Description
name str

Human-readable family name, e.g. "Clayton".

param_name str

Name of the single dependence parameter, "theta" by default.

bounds abstractmethod

bounds(dim: int) -> tuple[float, float]

Return the allowed range of theta for a given dimension.

Some families allow negative dependence only in two dimensions, so the range can shrink as dim grows.

Parameters:

Name Type Description Default
dim int

Copula dimension, at least 2.

required

Returns:

Type Description
tuple of (float, float)

(lower, upper) bounds for theta; either may be infinite.

psi abstractmethod

psi(t: NDArray[float64], theta: float) -> NDArray[np.float64]

Evaluate the generator :math:\psi(t).

Parameters:

Name Type Description Default
t numpy.ndarray of float, any shape

Non-negative arguments.

required
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``t``

Values in [0, 1]; :math:\psi(0) = 1 and :math:\psi decreases towards 0.

ipsi abstractmethod

ipsi(u: NDArray[float64], theta: float) -> NDArray[np.float64]

Evaluate the inverse generator :math:\psi^{-1}(u).

Parameters:

Name Type Description Default
u numpy.ndarray of float, any shape

Values in [0, 1].

required
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``u``

Non-negative values; :math:\psi^{-1}(1) = 0.

log_abs_dpsi abstractmethod

log_abs_dpsi(t: NDArray[float64], theta: float) -> NDArray[np.float64]

Return the log of the absolute first derivative, :math:\log|\psi'(t)|.

Parameters:

Name Type Description Default
t numpy.ndarray of float, any shape

Non-negative arguments.

required
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``t``

log|psi'(t)|; -inf where the derivative is zero.

log_abs_dpsi_d abstractmethod

log_abs_dpsi_d(t: NDArray[float64], theta: float, d: int) -> NDArray[np.float64]

Return the log of the absolute d-th derivative, :math:\log|\psi^{(d)}(t)|.

This is the numerator of the d-dimensional copula density.

Parameters:

Name Type Description Default
t numpy.ndarray of float, any shape

Non-negative arguments.

required
theta float

Dependence parameter, inside :meth:bounds.

required
d int

Order of the derivative (the copula dimension), at least 1.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``t``

log|psi^(d)(t)|.

tau abstractmethod

tau(theta: float) -> float

Return Kendall's tau (a rank correlation in [-1, 1]) implied by theta.

Parameters:

Name Type Description Default
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
float

Population Kendall's tau.

lambda_ abstractmethod

lambda_(theta: float) -> TailDependence

Return how strongly extreme values co-occur (tail dependence) at theta.

Parameters:

Name Type Description Default
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
TailDependence

Named tuple (lower, upper) of floats in [0, 1].

rvs_frailty abstractmethod

rvs_frailty(size: int, theta: float, rng: Generator) -> NDArray[np.float64]

Draw the random "frailty" variable used to simulate the copula.

The frailty :math:V_0 is the positive random variable whose Laplace transform is psi; sampling uses :math:U_j = \psi(E_j / V_0) with independent standard exponentials :math:E_j. Only called when :meth:has_frailty is true.

Parameters:

Name Type Description Default
size int

Number of draws, non-negative.

required
theta float

Dependence parameter, inside :meth:bounds.

required
rng Generator

Source of randomness.

required

Returns:

Type Description
numpy.ndarray of float, shape (size,)

Positive frailty draws.

has_frailty

has_frailty(theta: float) -> bool

Report whether the fast frailty sampler can be used at this theta.

Parameters:

Name Type Description Default
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
bool

True if the Marshall-Olkin frailty representation applies (the default rule is theta > 0); False means the copula is sampled by conditional inversion instead.

Notes

Sampling an Archimedean copula as :math:U_j = \psi(E_j / V) requires :math:\psi to be completely monotone, i.e. a Laplace transform. That holds only on the positively-dependent half of Clayton, Frank and AMH. On the negative half -- which exists in d = 2 and is the reason those families are used at all -- there is no frailty, and the copula has to be sampled by conditional inversion instead.

is_independent

is_independent(theta: float) -> bool

Report whether this theta means the variables are independent.

The independence point is where most generators divide by zero, so callers short-circuit there rather than evaluate.

Parameters:

Name Type Description Default
theta float

Dependence parameter.

required

Returns:

Type Description
bool

True if theta gives the independence copula. The default rule is |theta| < 1e-300; Gumbel and Joe use theta == 1.

Notes

Not just theta == 0: below about 5.6e-309 the 1 / theta in Clayton and Frank overflows to inf, and a subnormal theta gave nan densities and CDF margins of 0. Such a copula differs from independence by O(theta), far below anything a double can show.

rvs_log_frailty

rvs_log_frailty(size: int, theta: float, rng: Generator) -> NDArray[np.float64]

Draw the log of the frailty, :math:\log V_0, without underflow.

Used at strong dependence, where the frailty itself can be too small to represent. Default: the log of :meth:rvs_frailty.

Parameters:

Name Type Description Default
size int

Number of draws, non-negative.

required
theta float

Dependence parameter, inside :meth:bounds.

required
rng Generator

Source of randomness.

required

Returns:

Type Description
numpy.ndarray of float, shape (size,)

Log frailty draws (may be -inf in the default implementation if a draw underflows).

psi_from_log_t

psi_from_log_t(log_t: NDArray[float64], theta: float) -> NDArray[np.float64]

Evaluate the generator when only log t is available, :math:\psi(e^{\log t}).

Lets families avoid forming a huge t. Default: exponentiate, then call :meth:psi.

Parameters:

Name Type Description Default
log_t numpy.ndarray of float, any shape

Natural log of the generator argument.

required
theta float

Dependence parameter, inside :meth:bounds.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``log_t``

Generator values in [0, 1].

log_cdf

log_cdf(u: NDArray[float64], theta: float, dim: int) -> NDArray[np.float64] | None

Return log C(u) by a numerically stable family-specific formula, if one exists.

None means "no specialised form", and the caller falls back to psi(sum of psi^{-1}).

Parameters:

Name Type Description Default
u numpy.ndarray of float, shape (n, dim)

Points in the unit cube.

required
theta float

Dependence parameter, inside :meth:bounds.

required
dim int

Copula dimension.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,), or None

Log of the copula CDF at each row, or None (the default).

log_pdf

log_pdf(u: NDArray[float64], theta: float, dim: int) -> NDArray[np.float64] | None

Return log c(u) by a numerically stable family-specific formula, if one exists.

None falls back to the generic log|psi^(d)| - sum log|psi'|, which needs psi^{-1} evaluated explicitly and therefore inherits its overflow.

Parameters:

Name Type Description Default
u numpy.ndarray of float, shape (n, dim)

Points strictly inside the unit cube.

required
theta float

Dependence parameter, inside :meth:bounds.

required
dim int

Copula dimension.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,), or None

Log density at each row, or None (the default).

rho

rho(theta: float) -> float

Return Spearman's rho (a rank correlation in [-1, 1]) implied by theta.

Spearman's rho is the ordinary correlation of the ranks of the two variables. It is always computed for the bivariate (dim=2) copula.

Parameters:

Name Type Description Default
theta float

Dependence parameter, inside bounds(2).

required

Returns:

Type Description
float

Population Spearman's rho, clipped to [-1, 1].

Notes

The generic implementation evaluates :math:\rho = 12 \int_0^1\!\!\int_0^1 C(u,v)\,du\,dv - 3 on a tensor Gauss-Legendre grid. scipy.integrate.dblquad is a poor fit here: it calls the integrand one scalar at a time (so every evaluation pays the array-construction cost) and, being adaptive on a smooth-but-peaked integrand, it silently returns ~1e-3 accuracy. A fixed 256-node tensor rule is both exact to ~1e-14 and a single vectorised call.

Families with a closed form (Frank, via Debye functions) override this.

itau

itau(tau: float, dim: int = 2) -> float

Find the theta that gives a target Kendall's tau (R's iTau).

Parameters:

Name Type Description Default
tau float

Target Kendall's tau, a rank correlation in [-1, 1]. Must be reachable by this family in dimension dim.

required
dim int

Copula dimension; it can restrict the reachable range.

2

Returns:

Type Description
float

The parameter theta whose population tau equals tau.

Raises:

Type Description
ValueError

If tau is outside the range this family can reach.

Notes

The generic implementation is a bracketed root-find on the monotone tau(theta) curve; families with a closed form override it.

irho

irho(rho: float, dim: int = 2) -> float

Find the theta that gives a target Spearman's rho (R's iRho).

Parameters:

Name Type Description Default
rho float

Target Spearman's rho, a rank correlation in [-1, 1]. Must be reachable by this family.

required
dim int

Copula dimension; it restricts the admissible theta range.

2

Returns:

Type Description
float

The parameter theta whose population rho equals rho.

Raises:

Type Description
ValueError

If rho is outside the range this family can reach.

Notes

A bracketed root-find on the monotone rho(theta) curve. Each evaluation of rho is a 256x256 quadrature for families without a closed form, so this is slower than :meth:itau.

Extreme-value

rcopula.core.extreme_value.ExtremeValueCopula

ExtremeValueCopula(params: ArrayLike = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: Copula

Shared base for two-variable copulas built to model joint extremes.

An extreme-value copula describes how the largest values of two quantities (yearly flood peak and flood volume, the worst daily losses of two assets) occur together. Such copulas have dependence in the upper tail (big values tend to happen together) and none in the lower tail. You do not instantiate this class directly; use one of :class:GalambosCopula, :class:HuslerReissCopula, :class:TawnCopula or :class:TEVCopula.

Parameters:

Name Type Description Default
params float or array_like of float, shape (n_params,)

Parameter value(s) of the family. nan means "to be estimated", so fit(GalambosCopula(), u) works without inventing a starting value.

nan
dim int

Number of variables. Must be 2: only the bivariate case is implemented.

2
free array_like of bool, shape (n_params,), or None

Which parameters a fit may change; False holds that parameter at its current value. None makes every parameter free.

None

Attributes:

Name Type Description
dim int

Always 2.

params numpy.ndarray of float, shape (n_params,)

The parameter vector (read-only).

free numpy.ndarray of bool, shape (n_params,)

Mask of parameters free to be estimated.

Raises:

Type Description
ValueError

If dim is not 2, or a parameter lies outside its admissible range.

Notes

Subclasses supply :meth:A; supplying :meth:dA and :meth:d2A as well makes the density exact rather than finite-differenced (the defaults here use central differences with step 1e-4).

The copula is :math:C(u, v) = (uv)^{A(\log v / \log(uv))}, and every member has :math:\lambda_L = 0, :math:\lambda_U = 2(1 - A(1/2)).

A abstractmethod

A(w: ArrayLike) -> NDArray[np.float64]

Evaluate the curve that fully determines this copula's dependence.

This is the Pickands dependence function A. It equals 1 everywhere for independent variables and dips towards max(t, 1 - t) as dependence approaches perfect co-movement, so plotting it is a quick visual summary of the family and parameter.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1] at which to evaluate A.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w), each in [1/2, 1].

Notes

A is convex with :math:\max(t, 1-t) \le A(t) \le 1, and the copula is :math:C(u, v) = (uv)^{A(\log v / \log(uv))}.

dA

dA(w: ArrayLike) -> NDArray[np.float64]

Evaluate the slope (first derivative) of the dependence curve :meth:A.

Mostly used internally for the density and for sampling. The default is a central finite difference with step 1e-4; families that know the derivative in closed form override it.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]. In the finite-difference default, points closer than the step to 0 or 1 are evaluated at the step instead.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A'(w), each in [-1, 1].

d2A

d2A(w: ArrayLike) -> NDArray[np.float64]

Evaluate the curvature (second derivative) of the dependence curve :meth:A.

Used internally for the density and for Kendall's tau. The default is a central finite difference with step 1e-4; families with a closed form override it.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]. In the finite-difference default, points closer than the step to 0 or 1 are evaluated at the step instead.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A''(w). Non-negative, since A is convex.

tau

tau() -> float

Return Kendall's tau, a rank-based measure of how strongly the variables move together.

Kendall's tau is the probability that two random draws are ordered the same way in both variables minus the probability they are ordered oppositely. For extreme-value copulas it lies in [0, 1]: 0 is independence and 1 is perfect co-movement.

Returns:

Type Description
float

Kendall's tau of the copula.

Raises:

Type Description
ValueError

If any parameter is still nan (not yet fitted or supplied).

Notes

:math:\tau = \int_0^1 \frac{t(1-t)}{A(t)}\,\mathrm{d}A'(t), evaluated as :math:\int_0^1 t(1-t) A''(t)/A(t)\,\mathrm{d}t on a 400-point Gauss-Legendre grid.

rho

rho() -> float

Return Spearman's rho, the correlation between the two variables' ranks.

Spearman's rho is the ordinary correlation computed on the copula's uniform scale. For extreme-value copulas it lies in [0, 1]: 0 is independence and 1 is perfect co-movement.

Returns:

Type Description
float

Spearman's rho of the copula.

Raises:

Type Description
ValueError

If any parameter is still nan (not yet fitted or supplied).

Notes

:math:\rho_S = 12\int_0^1 (A(t)+1)^{-2}\,\mathrm{d}t - 3, evaluated on a 400-point Gauss-Legendre grid.

lambda_

lambda_() -> TailDependence

Return how likely the two variables are to be extreme at the same time.

These are the tail-dependence coefficients. upper is, roughly, the chance one variable is extremely large given the other is; lower is the same for extremely small values. Extreme-value copulas always have lower = 0.

Returns:

Type Description
TailDependence

Named tuple (lower, upper) of floats in [0, 1]; here always :math:(0,\ 2(1 - A(1/2))).

Raises:

Type Description
ValueError

If any parameter is still nan (not yet fitted or supplied).

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> ExtremeValueCopula

Build a copula of this family whose Kendall's tau equals a target value.

Useful when you know (or have estimated) how strongly two variables move together and want the family parameter that matches it. The parameter is found numerically by root-finding on tau(theta).

Parameters:

Name Type Description Default
tau float

Target Kendall's tau. Must be attainable by the family; for these families that means 0 <= tau < 1 (and below about 0.418 for :class:TawnCopula). tau = 0 is the independence limit and returns the family's independence parameter (see Notes).

required
dim int

Number of variables; must be 2, since these copulas are always bivariate.

2
**kwargs Any

Extra keyword arguments passed on to the constructor (for example free). They are applied once, to the result; the root search itself uses plain copulas.

{}

Returns:

Type Description
ExtremeValueCopula

A new instance of the calling class with the calibrated parameter.

Raises:

Type Description
ValueError

If tau cannot be reached by the family, or dim != 2.

Notes

Tawn reaches independence exactly, at theta = 0, and is searched down to it, so any 0 <= tau <= 0.418 is matched. Galambos and Husler-Reiss reach it only in the limit theta -> 0 and cannot be evaluated at theta = 0 itself (both divide by it), so for them tau = 0 -- or any target below the tau at the bottom of the search range, which is zero to double precision -- returns the smallest parameter in that range, 1e-8, whose copula is the independence copula to machine precision.

Examples:

>>> from rcopula import GalambosCopula
>>> g = GalambosCopula.from_tau(0.5)
>>> round(g.tau(), 6)
0.5
>>> GalambosCopula.from_tau(0.0).tau()
0.0

rcopula.core.extreme_value.GalambosCopula

GalambosCopula(params: ArrayLike = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: ExtremeValueCopula

Galambos copula: a one-parameter model for variables whose large values occur together.

A common choice for joint extremes (for example annual maxima). One parameter theta controls the strength: near 0 the variables are nearly independent, and as it grows they approach perfect co-movement in the upper tail. Bivariate only.

Parameters:

Name Type Description Default
params float

Dependence parameter theta, theta >= 0. nan means "to be estimated".

nan
dim int

Must be 2.

2
free array_like of bool, shape (1,), or None

Whether theta may be changed by a fit. None means free.

None

Attributes:

Name Type Description
theta float

The dependence parameter.

Raises:

Type Description
ValueError

If dim is not 2 or theta is negative.

Notes

:math:A(t) = 1 - \bigl(t^{-\theta} + (1-t)^{-\theta}\bigr)^{-1/\theta}, with :math:\theta > 0. Independence at :math:\theta \to 0, comonotonicity as :math:\theta \to \infty.

Examples:

>>> import numpy as np
>>> from rcopula import GalambosCopula
>>> g = GalambosCopula(2.0)
>>> float(np.round(g.A(0.5), 12))
0.646446609407
>>> float(np.round(g.lambda_().upper, 10))
0.7071067812
>>> float(round(g.tau(), 7))
0.6311589

theta property

theta: float

The dependence parameter theta (float, >= 0; larger is stronger).

param_bounds property

param_bounds: list[tuple[float, float]]

Allowed range of each parameter, as [(0.0, inf)] for theta.

Returns:

Type Description
list of tuple of (float, float), length 1

(lower, upper) bounds for theta.

A

A(w)

Evaluate the Galambos dependence curve (Pickands function) at w.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped just inside the interval.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w) in [1/2, 1].

Notes

:math:1 - g^{-1/\theta} with :math:g = t^{-\theta} + (1-t)^{-\theta}, written as -expm1 so it never cancels.

dA

dA(w)

Evaluate the slope of the Galambos dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped just inside the interval.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A'(w) in [-1, 1].

Notes

:math:A'(t) = -g^{-1/\theta - 1}\,(t^{-\theta-1} - (1-t)^{-\theta-1}).

The difference is formed in logs: the two powers span hundreds of orders of magnitude for large theta, and their signed difference is sign * e^{max} * (1 - e^{-|gap|}), which expm1 evaluates exactly even when the gap is tiny (at t = 1/2 it is zero, and so is A').

d2A

d2A(w)

Evaluate the curvature of the Galambos dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped just inside the interval.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A''(w), non-negative (may be inf at the very edges for large theta).

Notes

:math:A''(t) = (1 + \theta)\,\bigl(t(1-t)\bigr)^{-\theta-2}\,g^{-1/\theta-2}.

The direct second derivative is a difference of two enormous terms, and at theta = 30 it came out negative -- impossible, since A is convex by definition, and it drove the copula density negative for half the unit square. Expanding that difference collapses it: with :math:a = t^{-\theta}, :math:b = (1-t)^{-\theta} the bracket is

.. math:: g\bigl(t^{-\theta-2} + (1-t)^{-\theta-2}\bigr) - \bigl(t^{-\theta-1} - (1-t)^{-\theta-1}\bigr)^2 = ab\left(\tfrac{1}{t} + \tfrac{1}{1-t}\right)^2 = \bigl(t(1-t)\bigr)^{-\theta-2},

using :math:t + (1-t) = 1. What was a cancelling difference is a single positive term, so the result is non-negative by construction and overflow-free in logs.

rcopula.core.extreme_value.HuslerReissCopula

HuslerReissCopula(params: ArrayLike = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: ExtremeValueCopula

Husler-Reiss copula: the joint-extremes model that arises from normally distributed data.

If you take maxima of many correlated normal variables, their dependence converges to this copula. One parameter theta controls the strength: near 0 the extremes are nearly independent, and large values give near-perfect co-movement. Bivariate only.

Parameters:

Name Type Description Default
params float

Dependence parameter theta, theta >= 0. nan means "to be estimated".

nan
dim int

Must be 2.

2
free array_like of bool, shape (1,), or None

Whether theta may be changed by a fit. None means free.

None

Attributes:

Name Type Description
theta float

The dependence parameter.

Raises:

Type Description
ValueError

If dim is not 2 or theta is negative.

Notes

:math:A(t) = t\,\Phi\!\left(\tfrac{1}{\theta} + \tfrac{\theta}{2}\log\tfrac{t}{1-t}\right) + (1-t)\,\Phi\!\left(\tfrac{1}{\theta} - \tfrac{\theta}{2}\log\tfrac{t}{1-t}\right), with :math:\theta > 0. The extreme-value limit of the Gaussian copula.

Examples:

>>> import numpy as np
>>> from rcopula import HuslerReissCopula
>>> h = HuslerReissCopula(1.5)
>>> float(np.round(h.A(0.5), 12))
0.747507462453
>>> float(np.round(h.lambda_().upper, 7))
0.5049851

theta property

theta: float

The dependence parameter theta (float, >= 0; larger is stronger).

param_bounds property

param_bounds: list[tuple[float, float]]

Allowed range of each parameter, as [(0.0, inf)] for theta.

Returns:

Type Description
list of tuple of (float, float), length 1

(lower, upper) bounds for theta.

A

A(w)

Evaluate the Husler-Reiss dependence curve (Pickands function) at w.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w) in [1/2, 1].

dA

dA(w)

Evaluate the slope of the Husler-Reiss dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A'(w) in [-1, 1].

Notes

Exactly :math:\Phi(z_1) - \Phi(z_2).

The two density terms cancel: since :math:\phi(z_1)/\phi(z_2) = e^{-\ell} = (1-t)/t, we get :math:t\phi(z_1) = (1-t)\phi(z_2) and their contributions to :math:A' are equal and opposite.

d2A

d2A(w)

Evaluate the curvature of the Husler-Reiss dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A''(w), positive.

Notes

Obtained by differentiating :meth:dA once more. With :math:z_1' = \tfrac{\theta}{2t(1-t)} and :math:z_2' = -z_1',

.. math:: A''(t) = \phi(z_1) z_1' - \phi(z_2) z_2' = \frac{\theta}{2t(1-t)}\bigl(\phi(z_1) + \phi(z_2)\bigr),

which is manifestly positive -- as it must be, since A is convex.

rcopula.core.extreme_value.TawnCopula

TawnCopula(params: ArrayLike = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: ExtremeValueCopula

Tawn copula: a simple joint-extremes model that only allows weak-to-moderate dependence.

Its dependence curve is a parabola, which makes it easy to reason about, but it cannot represent strong dependence: Kendall's tau tops out at about 0.418 (reached at theta = 1). This is R's one-parameter form. Bivariate only.

Parameters:

Name Type Description Default
params float

Dependence parameter theta in [0, 1]. 0 is independence. nan means "to be estimated".

nan
dim int

Must be 2.

2
free array_like of bool, shape (1,), or None

Whether theta may be changed by a fit. None means free.

None

Attributes:

Name Type Description
theta float

The dependence parameter.

Raises:

Type Description
ValueError

If dim is not 2 or theta is outside [0, 1].

Notes

:math:A(t) = 1 - \theta\,t(1-t), with :math:\theta \in [0, 1]. A quadratic perturbation of independence, so like FGM it reaches only weak dependence -- R notes it is valid only for :math:\tau < 0.4184.

Examples:

>>> from rcopula import TawnCopula
>>> t = TawnCopula(0.6)
>>> float(t.A(0.5))
0.85
>>> float(round(t.tau(), 7))
0.2275623
>>> float(round(t.lambda_().upper, 12))
0.3

theta property

theta: float

The dependence parameter theta (float in [0, 1]; larger is stronger).

param_bounds property

param_bounds: list[tuple[float, float]]

Allowed range of each parameter, as [(0.0, 1.0)] for theta.

Returns:

Type Description
list of tuple of (float, float), length 1

(lower, upper) bounds for theta.

A

A(w)

Evaluate the Tawn dependence curve, :math:1 - \theta\,w(1-w).

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w) in [3/4, 1].

dA

dA(w)

Evaluate the slope of the Tawn dependence curve, :math:\theta(2w - 1).

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A'(w).

d2A

d2A(w)

Evaluate the curvature of the Tawn dependence curve, the constant :math:2\theta.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; only the shape is used.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

2 * theta at every point.

rcopula.core.extreme_value.TEVCopula

TEVCopula(rho: ArrayLike = np.nan, dim: int = 2, *, df: float = 4.0, free: ArrayLike | None = None)

Bases: ExtremeValueCopula

t-EV copula: the joint-extremes model that arises from Student-t distributed data.

If you take maxima of many variables linked by a Student-t copula (a common model for financial returns), their dependence converges to this copula. It has two parameters: a correlation rho and the degrees of freedom df (smaller df means heavier joint tails). Bivariate only.

Parameters:

Name Type Description Default
rho float or array_like of float, shape (2,)

Correlation parameter in [-1, 1]; nan means "to be estimated". A length-2 array is read as (rho, df) and then the df keyword is ignored (this is how copies are rebuilt internally).

nan
dim int

Must be 2.

2
df float

Degrees of freedom, df > 0 (at least 1e-6).

4.0
free array_like of bool, shape (2,), or None

Which of (rho, df) a fit may change. None makes both free.

None

Attributes:

Name Type Description
rho_param float

The correlation parameter (named so as not to clash with the :meth:rho method).

df float

The degrees of freedom.

Raises:

Type Description
ValueError

If dim is not 2 or a parameter is outside its range.

Notes

:math:A(t) = t\,T_{\nu+1}(z_t) + (1-t)\,T_{\nu+1}(z_{1-t}) with

.. math::

z_t = \sqrt{\tfrac{\nu+1}{1-\rho^2}}
      \left[\left(\tfrac{t}{1-t}\right)^{1/\nu} - \rho\right].

Because the t copula has tail dependence for every finite df, the t-EV copula is never the independence copula: :math:\tau > 0 always.

Examples:

>>> import numpy as np
>>> from rcopula import TEVCopula
>>> c = TEVCopula(0.5, df=4)
>>> float(np.round(c.A(0.5), 12))
0.87341500245
>>> float(round(c.tau(), 6))
0.195867

rho_param property

rho_param: float

The correlation parameter rho (float in [-1, 1]).

Named rho_param because :meth:rho returns Spearman's rho.

df property

df: float

The degrees of freedom df (float, positive; smaller means heavier tails).

param_bounds property

param_bounds: list[tuple[float, float]]

Allowed ranges, [(-1.0, 1.0), (1e-6, inf)] for (rho, df).

Returns:

Type Description
list of tuple of (float, float), length 2

(lower, upper) bounds for rho and df.

A

A(w)

Evaluate the t-EV dependence curve (Pickands function) at w.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w) in [1/2, 1].

dA

dA(w)

Evaluate the slope of the t-EV dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A'(w) in [-1, 1].

Notes

Exactly :math:T_{\nu+1}(z_1) - T_{\nu+1}(z_2).

As for Husler-Reiss, the two density terms cancel identically: :math:t f(z_1) z_1' + (1-t) f(z_2) z_2' = 0 for every t. Relying on that rather than finite-differencing matters -- the central-difference fallback left the t-EV density wrong by ~2e-4.

d2A

d2A(w)

Evaluate the curvature of the t-EV dependence curve, in closed form.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points in [0, 1]; values are clipped to [1e-12, 1 - 1e-12].

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A''(w), positive.

Notes

:math:A''(t) = f(z_1) z_1' - f(z_2) z_2', both terms positive.

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> TEVCopula

Build a t-EV copula whose Kendall's tau equals a target value, keeping df fixed.

Only the correlation rho is solved for, numerically; the degrees of freedom are whatever you pass (default 4).

Parameters:

Name Type Description Default
tau float

Target Kendall's tau. Must be attainable at the given df; the t-EV copula always has tau > 0.

required
dim int

Number of variables; must be 2 (always bivariate).

2
**kwargs Any

df (float, default 4.0) sets the degrees of freedom; any other keyword (for example free) goes to the constructor.

{}

Returns:

Type Description
TEVCopula

A new copula with the calibrated rho and the given df.

Raises:

Type Description
ValueError

If tau is not attainable at that df (in particular any tau <= 0: the t-EV copula is never independent), or dim != 2.

Examples:

>>> from rcopula import TEVCopula
>>> c = TEVCopula.from_tau(0.3, df=4)
>>> round(c.tau(), 6), c.df
(0.3, 4.0)

rcopula.core.extreme_value.gumbel_pickands

gumbel_pickands(w: ArrayLike, theta: float) -> NDArray[np.float64]

Evaluate the curve that describes the Gumbel copula's dependence, at given points.

Every extreme-value copula is summarised by one curve on [0, 1], its Pickands dependence function A: A = 1 everywhere means the two variables are independent, and the lower it dips the stronger they move together. This function returns that curve for the Gumbel-Hougaard copula, so you can compare it with :meth:ExtremeValueCopula.A of the other families in this module.

Parameters:

Name Type Description Default
w float or array_like of float, any shape

Points at which to evaluate A, each in [0, 1].

required
theta float

Gumbel dependence parameter, theta >= 1. 1 is independence and larger values mean stronger dependence.

required

Returns:

Type Description
numpy.ndarray of float, same shape as ``w``

Values of A(w), each in [1/2, 1]. A 0-d array when w is a scalar.

Notes

:math:A(t) = (t^\theta + (1-t)^\theta)^{1/\theta}.

Provided for comparison: Gumbel is the unique family that is simultaneously Archimedean and extreme-value. The copula itself is :class:~rcopula.core.archimedean.GumbelCopula.

Examples:

>>> import numpy as np
>>> from rcopula.core.extreme_value import gumbel_pickands
>>> float(np.round(gumbel_pickands(0.5, 2.0), 12))
0.707106781187

Everything else

rcopula.core.other.PlackettCopula

PlackettCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: Copula

Two-variable copula covering the whole range from negative to positive dependence.

A flexible one-parameter model for two variables with no tendency for extremes to cluster (no tail dependence). Pick it when you want a simple, symmetric dependence of any sign and strength.

Defined by a constant cross-product ratio :math:\theta > 0:

.. math::

C(u,v) = \frac{S - \sqrt{S^2 - 4uv\theta(\theta-1)}}{2(\theta - 1)},
\qquad S = 1 + (\theta - 1)(u + v).

Spans the full dependence range -- :math:\theta \to 0 gives the countermonotone bound, :math:\theta = 1 independence, :math:\theta \to \infty the comonotone bound -- with no tail dependence anywhere. Spearman's rho has a closed form; Kendall's tau does not.

Parameters:

Name Type Description Default
theta float

Cross-product ratio, any value in [0, inf). Below 1 gives negative dependence, 1 is independence, above 1 positive dependence. nan leaves it unspecified, to be estimated by fitting.

nan
dim int

Number of variables; must be exactly 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated (True) or held fixed (False) during fitting. None means free.

None

Attributes:

Name Type Description
theta float

The cross-product ratio.

Raises:

Type Description
ValueError

If dim != 2 or theta is negative.

Examples:

>>> from rcopula import PlackettCopula
>>> p = PlackettCopula(3.0)
>>> float(round(p.rho(), 10))
0.352081567
>>> p.lambda_()
TailDependence(lower=0.0, upper=0.0)

theta = 1 is exactly independence:

>>> float(PlackettCopula(1.0).cdf([[0.3, 0.4]])[0])
0.12
>>> float(PlackettCopula(1.0).pdf([[0.3, 0.4]])[0])
1.0

theta property

theta: float

The dependence parameter (cross-product ratio); 1 means independence.

Returns:

Type Description
float

theta, in [0, inf), or nan if unspecified.

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible range of theta.

Returns:

Type Description
list of tuple of (float, float)

[(0.0, inf)].

tau

tau() -> float

Return Kendall's tau, a rank correlation in [-1, 1].

Computed by numerical integration, because Plackett admits no closed form.

Uses :math:\tau = 1 - 4\int\!\!\int \partial_1 C\,\partial_2 C\,du\,dv rather than the equivalent :math:4\int\!\!\int C\,c\,du\,dv - 1. The two agree to eight digits at ordinary parameters, but the density form collapses under strong dependence: the density concentrates onto the diagonal faster than any quadrature can follow, and a 100-node Gauss-Legendre rule returned 14.6 at theta = 1e6 -- not merely inaccurate but outside the range Kendall's tau can occupy. The integrand used here is a product of conditional distribution functions, bounded in [0, 1], and lands within 0.002 of the empirical value there.

Returns:

Type Description
float

Kendall's tau.

Raises:

Type Description
ValueError

If theta is unspecified (nan).

rho

rho() -> float

Return Spearman's rho, a rank correlation in [-1, 1].

Closed form: :math:\rho = \frac{\theta+1}{\theta-1} - \frac{2\theta\log\theta}{(\theta-1)^2}.

Returns:

Type Description
float

Spearman's rho.

Raises:

Type Description
ValueError

If theta is unspecified (nan).

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

Plackett has no tail dependence at any theta.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=0.0).

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> PlackettCopula

Build a Plackett copula whose Kendall's tau equals a target value.

Use this to choose theta from a rank correlation you already know.

Plackett's tau has no closed form in either direction, so this inverts :meth:tau on a logarithmic scale -- where the map is smooth and monotone over the family's whole range, from the countermonotone limit at theta -> 0 to the comonotone one at theta -> inf.

Parameters:

Name Type Description Default
tau float

Target Kendall's tau, strictly between -1 and 1.

required
dim int

Number of variables; must be 2.

2
**kwargs Any

Passed to the constructor, e.g. free.

{}

Returns:

Type Description
PlackettCopula

A new copula with tau() equal to tau (to about 1e-6).

Raises:

Type Description
ValueError

If tau is not in (-1, 1).

Examples:

>>> from rcopula import PlackettCopula
>>> bool(abs(PlackettCopula.from_tau(-0.4).tau() + 0.4) < 1e-6)
True
>>> bool(abs(PlackettCopula.from_tau(0.7).tau() - 0.7) < 1e-6)
True

from_rho classmethod

from_rho(rho: float, dim: int = 2, **kwargs: Any) -> PlackettCopula

Build a Plackett copula whose Spearman's rho equals a target value.

Inverts the closed form of :meth:rho numerically.

Parameters:

Name Type Description Default
rho float

Target Spearman's rho, strictly between -1 and 1.

required
dim int

Number of variables; must be 2.

2
**kwargs Any

Passed to the constructor, e.g. free.

{}

Returns:

Type Description
PlackettCopula

A new copula with rho() equal to rho.

Raises:

Type Description
ValueError

If rho is not in (-1, 1), or dim != 2.

TypeError

If kwargs holds a keyword the constructor does not take.

Examples:

>>> from rcopula import PlackettCopula
>>> bool(abs(PlackettCopula.from_rho(0.5).rho() - 0.5) < 1e-8)
True

rcopula.core.other.FGMCopula

FGMCopula(theta: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: Copula

Simple two-variable copula for weak dependence (Farlie-Gumbel-Morgenstern).

A small, easy-to-compute departure from independence, positive or negative. It cannot represent strong dependence, so use it for toy models or for testing methods near independence.

.. math:: C(u,v) = uv\bigl(1 + \theta(1-u)(1-v)\bigr), \qquad |\theta| \le 1.

A perturbation of independence, and a weak one: dependence is capped at :math:|\tau| \le 2/9 and :math:|\rho| \le 1/3. Useful as a tractable toy and for testing procedures near independence, rarely as a serious model.

Parameters:

Name Type Description Default
theta float

Dependence parameter in [-1, 1]; 0 is independence, positive values give positive dependence. nan leaves it unspecified, to be estimated by fitting.

nan
dim int

Number of variables; must be exactly 2.

2
free array_like of bool, shape (1,), or None

Whether theta is estimated (True) or held fixed (False) during fitting. None means free.

None

Attributes:

Name Type Description
theta float

The dependence parameter.

Raises:

Type Description
ValueError

If dim != 2 or theta is outside [-1, 1].

Examples:

>>> from rcopula import FGMCopula
>>> f = FGMCopula(0.5)
>>> float(round(f.tau(), 12)), float(round(f.rho(), 12))
(0.111111111111, 0.166666666667)
>>> f.lambda_()
TailDependence(lower=0.0, upper=0.0)

Even maximal theta gives only weak dependence:

>>> float(round(FGMCopula(1.0).tau(), 12))
0.222222222222

theta property

theta: float

The dependence parameter; 0 means independence.

Returns:

Type Description
float

theta, in [-1, 1], or nan if unspecified.

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible range of theta.

Returns:

Type Description
list of tuple of (float, float)

[(-1.0, 1.0)].

tau

tau() -> float

Return Kendall's tau, a rank correlation; here :math:\tau = 2\theta/9.

Returns:

Type Description
float

Kendall's tau, in [-2/9, 2/9].

Raises:

Type Description
ValueError

If theta is unspecified (nan).

rho

rho() -> float

Return Spearman's rho, a rank correlation; here :math:\rho = \theta/3.

Returns:

Type Description
float

Spearman's rho, in [-1/3, 1/3].

Raises:

Type Description
ValueError

If theta is unspecified (nan).

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

FGM has no tail dependence at any theta.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=0.0).

from_tau classmethod

from_tau(tau: float, dim: int = 2, **kwargs: Any) -> FGMCopula

Build an FGM copula whose Kendall's tau equals a target value.

Uses theta = 9 * tau / 2.

Parameters:

Name Type Description Default
tau float

Target Kendall's tau, in [-2/9, 2/9] (FGM cannot go further).

required
dim int

Number of variables; must be 2.

2
**kwargs Any

Passed to the constructor, e.g. free.

{}

Returns:

Type Description
FGMCopula

A new copula with tau() equal to tau.

Raises:

Type Description
ValueError

If tau is outside [-2/9, 2/9], or dim != 2.

TypeError

If kwargs holds a keyword the constructor does not take.

Examples:

>>> from rcopula import FGMCopula
>>> FGMCopula.from_tau(0.1).theta
0.45

from_rho classmethod

from_rho(rho: float, dim: int = 2, **kwargs: Any) -> FGMCopula

Build an FGM copula whose Spearman's rho equals a target value.

Uses theta = 3 * rho.

Parameters:

Name Type Description Default
rho float

Target Spearman's rho, in [-1/3, 1/3] (FGM cannot go further).

required
dim int

Number of variables; must be 2.

2
**kwargs Any

Passed to the constructor, e.g. free.

{}

Returns:

Type Description
FGMCopula

A new copula with rho() equal to rho.

Raises:

Type Description
ValueError

If rho is outside [-1/3, 1/3], or dim != 2.

TypeError

If kwargs holds a keyword the constructor does not take.

rcopula.core.other.MarshallOlkinCopula

MarshallOlkinCopula(alpha1: ArrayLike = np.nan, alpha2: float = np.nan, dim: int = 2, *, free: ArrayLike | None = None)

Bases: Copula

Two-variable copula for things that can fail from a common shock (Marshall-Olkin).

Models two components that each fail from their own causes or together from a shared event, so there is a real chance they fail at exactly the same moment. Use it for joint-default or joint-failure settings where simultaneous events matter.

.. math::

C(u,v) = \min\bigl(u\, v^{1-\alpha_2},\ u^{1-\alpha_1}\, v\bigr),
\qquad \alpha_1, \alpha_2 \in [0, 1].

Arises from a shared-shock model: two components fail from their own private shocks or from a common one. Not absolutely continuous -- it places mass on the curve :math:u^{\alpha_1} = v^{\alpha_2}, so :meth:pdf describes only the continuous part and is undefined there. Asymmetric unless :math:\alpha_1 = \alpha_2, and upper-tail dependent with :math:\lambda_U = \min(\alpha_1, \alpha_2).

Parameters:

Name Type Description Default
alpha1 float or array_like of float, shape (2,)

Share of the first component's failures caused by the common shock, in [0, 1]. May instead be both parameters at once, [alpha1, alpha2], in which case alpha2 must be left out. nan leaves it unspecified, to be estimated by fitting.

nan
alpha2 float

The same share for the second component, in [0, 1].

nan
dim int

Number of variables; must be exactly 2.

2
free array_like of bool, shape (2,), or None

Which of (alpha1, alpha2) are estimated (True) or held fixed (False) during fitting. None means both free.

None

Attributes:

Name Type Description
alpha numpy.ndarray of float, shape (2,)

The two shock parameters [alpha1, alpha2].

Raises:

Type Description
ValueError

If dim != 2, either parameter is outside [0, 1], alpha1 has a length other than 1 or 2, or alpha1 holds both parameters and alpha2 is given as well (it would otherwise be silently ignored).

Examples:

>>> from rcopula import MarshallOlkinCopula
>>> mo = MarshallOlkinCopula(0.2, 0.8)
>>> float(round(mo.tau(), 10))
0.1904761905
>>> mo.lambda_()
TailDependence(lower=0.0, upper=0.2)

Asymmetry is real -- swapping the arguments changes the value:

>>> a = float(mo.cdf([[0.3, 0.7]])[0])
>>> b = float(mo.cdf([[0.7, 0.3]])[0])
>>> bool(abs(a - b) > 1e-3)
True

alpha property

alpha: NDArray[float64]

The two common-shock parameters [alpha1, alpha2] (read-only).

Returns:

Type Description
numpy.ndarray of float, shape (2,)

Values in [0, 1], or nan where unspecified.

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible ranges of alpha1 and alpha2.

Returns:

Type Description
list of tuple of (float, float)

[(0.0, 1.0), (0.0, 1.0)].

tau

tau() -> float

Return Kendall's tau, a rank correlation in [0, 1] for this family.

Closed form: :math:\tau = \alpha_1\alpha_2/(\alpha_1 + \alpha_2 - \alpha_1\alpha_2), taken as 0 when both parameters are 0.

Returns:

Type Description
float

Kendall's tau.

Raises:

Type Description
ValueError

If a parameter is unspecified (nan).

rho

rho() -> float

Return Spearman's rho, a rank correlation in [0, 1] for this family.

Closed form: :math:\rho = 3\alpha_1\alpha_2/(2\alpha_1 + 2\alpha_2 - \alpha_1\alpha_2), taken as 0 when both parameters are 0.

Returns:

Type Description
float

Spearman's rho.

Raises:

Type Description
ValueError

If a parameter is unspecified (nan).

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

Joint large values are likely (upper tail dependence min(alpha1, alpha2)); joint small values are not.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=min(alpha1, alpha2)).

Raises:

Type Description
ValueError

If a parameter is unspecified (nan).

rcopula.core.other.IndependenceCopula

IndependenceCopula(dim: int = 2)

Bases: Copula

Copula for variables that have no dependence on each other at all.

Use it as a baseline: joint probabilities are simply the product of the individual probabilities, :math:\Pi(u) = \prod_j u_j. It has no parameters.

The reference point for every dependence measure: all of tau, rho, beta and both tail-dependence coefficients are zero.

Parameters:

Name Type Description Default
dim int

Number of variables d; must be at least 2.

2

Raises:

Type Description
ValueError

If dim < 2.

TypeError

If given any keyword other than dim -- a misspelled keyword is an error rather than being silently ignored.

Examples:

>>> import numpy as np
>>> from rcopula import IndependenceCopula
>>> c = IndependenceCopula(dim=3)
>>> float(c.cdf([[0.5, 0.5, 0.5]])[0])
0.125
>>> float(c.pdf([[0.2, 0.7, 0.9]])[0])
1.0
>>> float(c.tau()), float(c.rho())
(0.0, 0.0)

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible parameter ranges: always an empty list (no parameters).

Returns:

Type Description
list of tuple of (float, float)

[].

tau

tau() -> float

Return Kendall's tau, a rank correlation in [-1, 1]; here 0.0.

Returns:

Type Description
float

0.0.

rho

rho() -> float

Return Spearman's rho, a rank correlation in [-1, 1]; here 0.0.

Returns:

Type Description
float

0.0.

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

Independent variables never crash or boom together beyond chance, so both coefficients are zero.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=0.0).

rcopula.core.other.FrechetUpperCopula

FrechetUpperCopula(dim: int = 2)

Bases: Copula

Copula for perfect positive dependence: all variables move in lock-step.

Every variable is an increasing function of every other, so knowing one determines the rest. This is the strongest possible positive dependence -- the Frechet-Hoeffding upper bound :math:M(\mathbf{u}) = \min_j u_j. It has no parameters. Use it as an extreme reference case or for perfectly comonotone risks.

Comonotonicity: every margin is an increasing function of every other. No density exists -- all the mass sits on the diagonal -- so :meth:pdf raises.

Parameters:

Name Type Description Default
dim int

Number of variables d; must be at least 2.

2

Raises:

Type Description
ValueError

If dim < 2.

TypeError

If given any keyword other than dim -- a misspelled keyword is an error rather than being silently ignored.

NotImplementedError

From :meth:pdf / :meth:logpdf at interior points, since no density exists.

Examples:

>>> import numpy as np
>>> from rcopula import FrechetUpperCopula
>>> m = FrechetUpperCopula(dim=3)
>>> float(m.cdf([[0.3, 0.7, 0.5]])[0])
0.3
>>> float(m.tau()), float(m.rho())
(1.0, 1.0)
>>> m.lambda_()
TailDependence(lower=1.0, upper=1.0)

Every margin of a draw is identical:

>>> u = m.rvs(5, random_state=0)
>>> bool(np.all(u[:, 0] == u[:, 2]))
True

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible parameter ranges: always an empty list (no parameters).

Returns:

Type Description
list of tuple of (float, float)

[].

tau

tau() -> float

Return Kendall's tau, a rank correlation in [-1, 1]; here 1.0.

Returns:

Type Description
float

1.0.

rho

rho() -> float

Return Spearman's rho, a rank correlation in [-1, 1]; here 1.0.

Returns:

Type Description
float

1.0.

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

The variables move in lock-step, so an extreme in one is always an extreme in the others: both coefficients are one.

Returns:

Type Description
TailDependence

TailDependence(lower=1.0, upper=1.0).

rcopula.core.other.FrechetLowerCopula

FrechetLowerCopula(dim: int = 2)

Bases: Copula

Copula for perfect negative dependence between two variables.

When one variable is large the other is exactly correspondingly small: one is a decreasing function of the other. This is the strongest possible negative dependence -- the Frechet-Hoeffding lower bound :math:W(u, v) = \max(u + v - 1, 0). It has no parameters.

Countermonotonicity. Bivariate only -- in three or more dimensions :math:W is not a copula at all (its C-volume can be negative), which is why R restricts lowfhCopula the same way. Singular, so no density.

Parameters:

Name Type Description Default
dim int

Number of variables; must be exactly 2.

2

Raises:

Type Description
ValueError

If dim != 2.

TypeError

If given any keyword other than dim -- a misspelled keyword is an error rather than being silently ignored.

NotImplementedError

From :meth:pdf / :meth:logpdf at interior points, since no density exists.

Examples:

>>> import numpy as np
>>> from rcopula import FrechetLowerCopula
>>> w = FrechetLowerCopula()
>>> float(w.cdf([[0.3, 0.4]])[0])
0.0
>>> float(w.cdf([[0.7, 0.8]])[0])
0.5
>>> float(w.tau()), float(w.rho())
(-1.0, -1.0)

Draws are exactly countermonotone:

>>> u = w.rvs(5, random_state=0)
>>> bool(np.allclose(u[:, 0] + u[:, 1], 1.0))
True

Higher dimensions are refused rather than returning something invalid:

>>> FrechetLowerCopula(dim=3)
Traceback (most recent call last):
    ...
ValueError: the Frechet-Hoeffding lower bound is only a copula for dim=2, got dim=3

param_bounds property

param_bounds: list[tuple[float, float]]

Admissible parameter ranges: always an empty list (no parameters).

Returns:

Type Description
list of tuple of (float, float)

[].

tau

tau() -> float

Return Kendall's tau, a rank correlation in [-1, 1]; here -1.0.

Returns:

Type Description
float

-1.0.

rho

rho() -> float

Return Spearman's rho, a rank correlation in [-1, 1]; here -1.0.

Returns:

Type Description
float

-1.0.

lambda_

lambda_() -> TailDependence

Return how strongly extreme values occur together (tail dependence).

One variable is small exactly when the other is large, so the two are never jointly extreme in the same direction: both coefficients are zero.

Returns:

Type Description
TailDependence

TailDependence(lower=0.0, upper=0.0).

Empirical

rcopula.core.empirical.EmpiricalCopula

EmpiricalCopula(data: ArrayLike, smoothing: str = 'none', *, offset: float = 0.0, ties_method: str = 'average')

Bases: Copula

A copula read directly off your data, with no assumed family.

It describes the dependence in a sample using only the ranks of the observations. Use it as a model-free reference: to compare a fitted parametric copula against, to compute sample dependence measures, or to resample the dependence structure of the data.

Parameters:

Name Type Description Default
data array_like of float, shape (n, d)

The observations, one row per observation and one column per variable (n >= 2, d >= 2). They are converted to ranks internally (pseudo-observations), so raw data on any scale is fine.

required
smoothing ('none', 'beta', 'checkerboard')

"none" is the classical step-function estimator. "beta" and "checkerboard" are smoothed and are genuine copulas; only "beta" admits a density (so :meth:pdf works only with "beta").

"none"
offset float

Keyword-only. Added to the denominator n of the unsmoothed estimator, as in R's empCopula. Rarely needed.

0.0
ties_method str

Keyword-only. How tied values are ranked; passed to :func:~rcopula.dependence.pseudo_obs. Methods that keep ties ("average", "min", "max") make the smoothed estimators spread each tied group over the block of ranks it occupies (see Notes); "first" and "random" break ties, so every observation keeps a rank of its own.

"average"

Attributes:

Name Type Description
smoothing str

The smoothing in use.

offset float

The denominator offset.

ties_method str

The tie-breaking rule used for the ranks.

n_obs int

Number of observations.

pseudo_observations numpy.ndarray of float, shape (n, d)

The rank-transformed data.

Raises:

Type Description
ValueError

If smoothing is not one of the three options, data is not a 2-D array, there are fewer than two observations, or the data has fewer than two columns.

TypeError

If given an unknown keyword argument (they used to be silently ignored, so a misspelling such as smothing= went unnoticed).

Notes

Ties. The beta and checkerboard estimators are indexed by ranks 1..n. When k observations tie in a column they jointly occupy the ranks r, ..., r + k - 1; each of them is given the average of the k beta distributions (beta smoothing) or the union of the k grid cells (checkerboard) for those ranks, rather than one rank rounded from the mid-rank. This keeps the margins exactly uniform, so both smoothings remain genuine copulas with ties. Without ties it is the usual estimator.

Examples:

>>> import numpy as np
>>> from rcopula import ClaytonCopula, EmpiricalCopula
>>> truth = ClaytonCopula(2.0)
>>> x = truth.rvs(2000, random_state=0)
>>> emp = EmpiricalCopula(x)
>>> grid = np.array([[0.25, 0.25], [0.5, 0.5], [0.75, 0.75]])
>>> bool(np.max(np.abs(emp.cdf(grid) - truth.cdf(grid))) < 0.02)
True

The beta-smoothed version has a density, which the raw estimator does not:

>>> smooth = EmpiricalCopula(x, smoothing="beta")
>>> bool(np.all(smooth.pdf(grid) > 0))
True
>>> EmpiricalCopula(x).pdf(grid)
Traceback (most recent call last):
    ...
NotImplementedError: the unsmoothed empirical copula is a step function...

Dependence measures come from the sample, not from a parametric form:

>>> bool(abs(emp.tau() - truth.tau()) < 0.05)
True

n_obs property

n_obs: int

Number of observations (rows of data) the estimator was built from.

Returns:

Type Description
int

pseudo_observations property

pseudo_observations: NDArray[float64]

The data converted to ranks scaled into (0, 1).

These pseudo-observations are what the estimator is actually built from; each column is roughly uniform.

Returns:

Type Description
numpy.ndarray of float, shape (n, d)

param_bounds property

param_bounds: list[tuple[float, float]]

Parameter bounds; always empty, since the estimator has no parameters.

Returns:

Type Description
list

[].

dCdu

dCdu(u: ArrayLike, bandwidth: float | None = None) -> NDArray[np.float64]

Estimate how fast the empirical copula changes in each coordinate.

Returns approximate partial derivatives :math:\partial C_n/\partial u_j at the given points, by finite differences (R's dCn). Mostly needed by goodness-of-fit tests (the multiplier bootstrap), since a step function has no true derivatives.

Parameters:

Name Type Description Default
u array_like of float, shape (m, d) or (d,)

Points in the unit cube at which to evaluate.

required
bandwidth float or None

Half-width b of the difference step, in (0, 1). None (or 0) uses :math:n^{-1/2}.

None

Returns:

Type Description
numpy.ndarray of float, shape (m, d)

Column j holds the estimated derivative with respect to u_j.

Raises:

Type Description
ValueError

If u has the wrong number of columns.

Notes

Uses the Remillard-Scaillet (2009) central difference

.. math:: \frac{C_n(\dots, u_j + b, \dots) - C_n(\dots, u_j - b, \dots)}{2b},

with default bandwidth :math:b = n^{-1/2}. These are what the multiplier bootstrap needs, since the true derivatives of a step function do not exist.

Examples:

>>> import numpy as np
>>> from rcopula import EmpiricalCopula, IndependenceCopula
>>> x = IndependenceCopula(2).rvs(20_000, random_state=0)
>>> d = EmpiricalCopula(x).dCdu([[0.5, 0.5]])
>>> bool(np.allclose(d, 0.5, atol=0.05))       # dC/du = v = 0.5
True

tau

tau() -> float

Kendall's tau of the data: a rank correlation between -1 and 1.

It is the probability that two observations are ordered the same way in both variables minus the probability they are ordered oppositely.

Returns:

Type Description
float

The sample value; for dim > 2, the average over all pairs.

rho

rho() -> float

Spearman's rho of the data: the correlation of the ranks, between -1 and 1.

Returns:

Type Description
float

The sample value; for dim > 2, the average over all pairs.

beta

beta() -> float

Blomqvist's beta of the data: dependence measured at the medians.

It compares how often all variables fall on the same side of their medians with what independence would give; between -1 and 1.

Returns:

Type Description
float

The sample value from :func:~rcopula.dependence.beta_n.

lambda_

lambda_() -> TailDependence

Estimate from the data how likely the two variables are to be extreme together.

Tail dependence is the chance that one variable is extreme given that the other is equally extreme, in the limit; this gives rough estimates for the lower and upper tails. Two-dimensional data only.

Returns:

Type Description
TailDependence

lower and upper estimates, nominally in [0, 1].

Raises:

Type Description
NotImplementedError

If dim != 2.

Notes

Uses the standard threshold estimators at :math:p = n^{-1/2}: :math:\hat\lambda_L = C_n(p,p)/p and :math:\hat\lambda_U = (1 - 2(1-p) + C_n(1-p, 1-p))/p.

These converge slowly -- tail dependence is estimated from the handful of points in the corner, so treat them as indicative rather than precise.

describe

describe() -> str

Return a one-line summary: dimension, sample size and smoothing.

Returns:

Type Description
str

E.g. "Empirical copula, dim 2, n=500, smoothing='none'".

Joint distributions

rcopula.distribution.CopulaDistribution

CopulaDistribution(copula: Copula, margins: Margin | Sequence[Margin] | NDArray[Any], names: list[str] | None = None)

A joint distribution made from a copula plus one distribution per variable.

Use this when you want realistic joint samples or joint probabilities on the original scale of your data: choose each variable's own distribution (the margins, e.g. a normal for one, an exponential for another) and a copula for how they move together. The result has those exact margins and that exact dependence (Sklar's theorem).

Parameters:

Name Type Description Default
copula Copula

The dependence structure, e.g. ClaytonCopula(2.0, dim=2). Must be fully specified (no nan parameters) before evaluating or sampling.

required
margins Margin or sequence of Margin

One distribution per variable, in column order, typically scipy frozen distributions such as stats.norm(loc=1, scale=2). Any sequence -- a list, a tuple, a 1-D object numpy.ndarray, a pandas.Series -- must have exactly copula.dim entries; a single distribution is used for every variable. Each margin needs cdf, ppf and either pdf (continuous) or pmf (discrete).

required
names list of str or None

Column names, length copula.dim. When given, :meth:rvs returns a pandas.DataFrame with these columns instead of an array, and a DataFrame passed to :meth:cdf, :meth:pdf, :meth:logpdf or :meth:marginal_cdf is read by these column labels (so its columns may be in any order, and extra columns are ignored).

None

Attributes:

Name Type Description
copula Copula

The copula passed in.

margins list of Margin

The margins, one per variable (a single margin is repeated).

discrete numpy.ndarray of bool, shape (d,)

True for variables whose margin is discrete (has pmf but no pdf).

names list of str or None

Column names, or None.

dim int

Number of variables.

Raises:

Type Description
TypeError

If copula is not a :class:~rcopula.core.base.Copula, or a margin lacks the required methods.

ValueError

If the number of margins or names does not match copula.dim.

Notes

How a pandas.DataFrame input is read: with names set, columns are selected by label and a missing label is an error; without names, columns are taken by position.

Examples:

>>> import numpy as np
>>> from scipy import stats
>>> from rcopula import ClaytonCopula, CopulaDistribution
>>> mv = CopulaDistribution(
...     ClaytonCopula(2.0, dim=2),
...     margins=[stats.norm(loc=1, scale=2), stats.expon(scale=1 / 3)],
... )
>>> x = mv.rvs(5000, random_state=0)
>>> x.shape
(5000, 2)

The margins come out as specified:

>>> bool(abs(x[:, 0].mean() - 1.0) < 0.1)
True
>>> bool(abs(x[:, 1].mean() - 1 / 3) < 0.02)
True

...while the dependence is the copula's:

>>> from scipy.stats import kendalltau
>>> bool(abs(kendalltau(x[:, 0], x[:, 1]).statistic - 0.5) < 0.03)
True

Evaluation at a point:

>>> float(round(mv.cdf([[1.0, 0.5]])[0], 10))
0.4633938511
>>> float(round(mv.pdf([[1.0, 0.5]])[0], 10))
0.1460418727

A single margin is broadcast:

>>> CopulaDistribution(ClaytonCopula(2.0, dim=3), stats.norm()).dim
3

dim property

dim: int

Number of variables in the distribution (same as the copula's dim).

Returns:

Type Description
int

The dimension, at least 2.

cdf

cdf(x: ArrayLike) -> NDArray[np.float64]

Probability that every variable is at or below the given values.

For two variables and a point (a, b) this is P(X_1 <= a and X_2 <= b), the joint cumulative distribution function :math:C(F_1(x_1), \dots, F_d(x_d)).

Parameters:

Name Type Description Default
x array_like of float or pandas.DataFrame, shape (n, d) or (d,)

Points on the original scale of the variables, one row per point. A 1-D input is treated as a single point. A DataFrame is read by the column labels in :attr:names when those are set, otherwise by column position.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Probabilities in [0, 1].

Raises:

Type Description
ValueError

If x does not have d columns, a DataFrame lacks one of :attr:names, or the copula is unfitted.

logpdf

logpdf(x: ArrayLike) -> NDArray[np.float64]

Natural logarithm of the joint density at each point.

Use this rather than log(pdf(x)) for likelihoods: it stays accurate when the density is extremely small.

Parameters:

Name Type Description Default
x array_like of float or pandas.DataFrame, shape (n, d) or (d,)

Points on the original scale of the variables, one row per point. A 1-D input is treated as a single point. DataFrames are read as for :meth:cdf.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Log density (or log mass, with discrete margins); -inf where the density is zero.

Raises:

Type Description
ValueError

If x does not have d columns, or the copula is unfitted.

Notes

By the chain rule, :math:h(\mathbf{x}) = c(F_1(x_1),\dots)\prod_j f_j(x_j), so the log density is the copula log density plus the marginal log densities. Doing it in logs matters: in even moderate dimensions the product of marginal densities underflows long before the joint density is genuinely zero.

With a discrete margin the quantity is a mass rather than a density and there is no log-domain form to stay in -- the inclusion-exclusion sum has to be taken in the linear domain before the log -- so that case falls back to log(pdf(x)) and inherits its underflow behaviour.

pdf

pdf(x: ArrayLike) -> NDArray[np.float64]

Joint density at each point (or probability mass, if margins are discrete).

Parameters:

Name Type Description Default
x array_like of float or pandas.DataFrame, shape (n, d) or (d,)

Points on the original scale of the variables, one row per point. A 1-D input is treated as a single point. DataFrames are read as for :meth:cdf.

required

Returns:

Type Description
numpy.ndarray of float, shape (n,)

Joint density for continuous margins, probability mass for all discrete margins, or the mixed density/mass otherwise.

Raises:

Type Description
ValueError

If x does not have d columns, or the copula is unfitted.

Notes

Joint density, or mass, or the mixture of the two.

With continuous margins this is :math:c(F_1(x_1),\dots)\prod_j f_j(x_j). With any discrete margin it is not a derivative in that coordinate but a finite difference, so the work is handed to :func:rcopula.discrete.mixed_pdf -- see that module for what identifiability means once a margin has atoms.

rvs

rvs(size: int = 1, random_state: Generator | int | None = None) -> NDArray[np.float64] | pd.DataFrame

Generate random samples on the original scale of the variables.

Parameters:

Name Type Description Default
size int

Number of observations (rows) to draw. Must be non-negative.

1
random_state (int, Generator or None)

Seed or generator for reproducible draws; None gives fresh randomness.

None

Returns:

Type Description
numpy.ndarray of float, shape (size, d), or pandas.DataFrame

Samples, one row per observation. A DataFrame with columns :attr:names is returned when names was given.

Raises:

Type Description
ValueError

If the copula is unfitted or size < 0.

Notes

Samples the copula, then pushes each coordinate through the corresponding marginal quantile function.

marginal_cdf

marginal_cdf(x: ArrayLike) -> NDArray[np.float64]

Convert data to the unit scale by applying each margin's CDF to its column.

The result is what the copula itself sees: column j becomes F_j(x[:, j]), a value in [0, 1] -- the copula's own arguments.

Parameters:

Name Type Description Default
x array_like of float or pandas.DataFrame, shape (n, d) or (d,)

Points on the original scale, one row per point. DataFrames are read as for :meth:cdf.

required

Returns:

Type Description
numpy.ndarray of float, shape (n, d)

Marginal probabilities in [0, 1].

Raises:

Type Description
ValueError

If x does not have d columns.

describe

describe() -> str

Short human-readable summary of the copula and the margins.

Returns:

Type Description
str

One line, e.g. 'Clayton copula, dim 2, theta=2 with margins [norm, expon]'.

rcopula.distribution.Margin

Bases: Protocol

The methods a single-variable distribution needs to be used as a margin.

A margin describes one variable on its own (its scale and shape), for example scipy.stats.norm(loc=0, scale=1). You never instantiate Margin; it only documents what :class:CopulaDistribution expects.

Satisfied by every scipy.stats frozen distribution, and by anything else exposing the same three methods:

  • cdf(x): cumulative probability P(X <= x) for array_like x.
  • pdf(x): density at x.
  • ppf(q): quantile function, the inverse of cdf, for q in [0, 1].

:class:CopulaDistribution also accepts discrete margins that provide pmf instead of pdf (such as scipy's frozen discrete distributions).

Examples:

>>> from scipy import stats
>>> from rcopula.distribution import Margin
>>> isinstance(stats.norm(), Margin)
True

Dependence measures

rcopula.dependence

Rank transforms and sample dependence measures.

The entry point for nearly every copula analysis is :func:pseudo_obs: copula methods work on the unit cube, but data does not arrive there. Ranks scaled by :math:n+1 are the standard bridge, and using :math:n+1 rather than :math:n is not cosmetic — dividing by :math:n would place a point at exactly 1, where most copula densities are infinite.

References

Genest, C. and Favre, A.-C. (2007). Everything you always wanted to know about copula modeling but were afraid to ask. Journal of Hydrologic Engineering 12(4), 347-368. The standard reference for rank-based copula inference. Kojadinovic, I. (2017). Some copula inference procedures adapted to the presence of ties. Computational Statistics & Data Analysis 112, 24-41. For the tie-handling options. Blomqvist, N. (1950). On a measure of dependence between two random variables. Annals of Mathematical Statistics 21(4), 593-600.

TailEstimate dataclass

TailEstimate(lower: float, upper: float, lower_se: float, upper_se: float, k: int, n: int, method: str, path: NDArray[float64])

How likely two variables are to be extreme together, estimated from data alone.

Returned by :func:fit_lambda. lower estimates the chance that one variable is in its extreme low tail given that the other is (joint crashes); upper the same for the high tail. Both are between 0 (no tendency to be extreme together) and 1.

Attributes:

Name Type Description
lower, upper float

The lower- and upper-tail dependence estimates at the chosen threshold, each in [0, 1].

lower_se, upper_se float

Asymptotic standard errors. The counts behind them are binomial, so these are only meaningful when k is not tiny -- below about 20 exceedances, use a bootstrap instead.

k int

Number of order statistics used (how many of the most extreme observations define "the tail").

n int

Sample size.

method str

The estimator used: "schmidt-stadtmuller" or "log".

path numpy.ndarray of float, shape (m, 3)

(k, lower, upper) over a range of thresholds. Look at this: a threshold-dependent estimator is only believable where the path is flat, and the plateau is the estimate. See :func:~rcopula.plots.tail_plot.

summary

summary() -> str

Return a printable text report, with the 95% interval each estimate implies.

The intervals are estimate +/- 1.96 standard errors, clipped to [0, 1].

Returns:

Type Description
str

A multi-line table of estimate, standard error and interval for each tail.

pseudo_obs

pseudo_obs(x: ArrayLike, ties_method: str = 'average', lower_tail: bool = True, random_state: Generator | int | None = None) -> NDArray[np.float64] | pd.DataFrame

Turn each column of data into ranks scaled to lie between 0 and 1.

These are the "pseudo-observations" (R's pobs). Copula methods work on values between 0 and 1, but data does not arrive that way; replacing each value by its rank within its column puts it there while keeping only the information about how the columns move together. Call this first on raw data before fitting a copula.

Each column is replaced by :math:r_{ij} / (n+1), where :math:r_{ij} is the rank of observation :math:i within column :math:j.

Parameters:

Name Type Description Default
x array_like or pandas.DataFrame of float, shape (n, d) or (n,)

Observations, one row per observation and one column per variable. A 1-D input is treated as a single column, shape (n, 1). A pandas frame is returned as a frame with its columns and index preserved. Must have at least one row.

required
ties_method str

How to rank tied values: one of "average", "min", "max", "dense", "ordinal", "random". "average" gives tied values the mean of the ranks they span, as in R. "random" puts each group of tied values in a uniformly random order (distinct values keep their order), whatever the magnitude of the data.

"average"
lower_tail bool

If False, return 1 - pseudo_obs(x), which is the transform of the survival copula (the copula of the data flipped upside down).

True
random_state (int, Generator or None)

Seed or generator for breaking ties. Only used when ties_method="random".

None

Returns:

Type Description
numpy.ndarray of float, shape (n, d), or pandas.DataFrame

Values strictly inside (0, 1). A DataFrame (same index and columns) when x was a DataFrame, otherwise an ndarray.

Raises:

Type Description
ValueError

If ties_method is not one of the listed names, or x has no rows.

Notes

The scaling by n + 1 keeps every value strictly below 1. Dividing by n would put the largest observation at exactly 1, where Archimedean and elliptical densities diverge.

Examples:

>>> import numpy as np
>>> from rcopula import pseudo_obs
>>> pseudo_obs([[3.0, 10.0], [1.0, 30.0], [2.0, 20.0]])
array([[0.75, 0.25],
       [0.25, 0.75],
       [0.5 , 0.5 ]])

The transform is invariant to any increasing transformation of a margin — which is precisely what makes copulas separate dependence from margins:

>>> rng = np.random.default_rng(0)
>>> x = rng.normal(size=(50, 2))
>>> bool(np.array_equal(pseudo_obs(x), pseudo_obs(np.exp(x) * 3.0)))
True

Column names survive:

>>> import pandas as pd
>>> df = pd.DataFrame({"a": [1.0, 3.0, 2.0], "b": [7.0, 5.0, 9.0]})
>>> list(pseudo_obs(df).columns)
['a', 'b']

cor_kendall

cor_kendall(x: ArrayLike) -> NDArray[np.float64]

Measure how strongly each pair of columns rises and falls together, by rank.

Returns the matrix of pairwise Kendall's tau (R's corKendall). Kendall's tau is the probability that two observations are ordered the same way in both columns minus the probability they are ordered oppositely: 1 means the columns always move together, -1 always opposite, 0 no tendency. It depends only on ranks, so it is unaffected by the marginal distributions, and many copula families have a closed-form link between tau and their parameter (see from_tau on the copula classes).

Parameters:

Name Type Description Default
x array_like of float, shape (n, d)

Data or pseudo-observations, one column per variable. Must be 2-D.

required

Returns:

Type Description
numpy.ndarray of float, shape (d, d)

Symmetric matrix with ones on the diagonal; entry [i, j] is Kendall's tau between columns i and j, in [-1, 1]. Ties are handled as tau-b, exactly as :func:scipy.stats.kendalltau; a pair involving a constant column or a missing value is NaN.

Raises:

Type Description
ValueError

If x is not 2-D.

Notes

The result is identical, to the last bit, to calling :func:scipy.stats.kendalltau on every pair of columns. For many columns and a moderate number of rows the whole matrix is computed at once from blocked products of pairwise-sign matrices (O(n^2 d^2) work, but in BLAS), which is hundreds of times faster than d(d-1)/2 separate scipy calls -- 800 columns of 1000 rows take seconds rather than minutes. For long series with few columns the per-pair O(n log n) scipy route is used.

Examples:

>>> import numpy as np
>>> from rcopula import ClaytonCopula, cor_kendall
>>> u = ClaytonCopula(2.0, dim=3).rvs(2000, random_state=0)
>>> m = cor_kendall(u)
>>> m.shape
(3, 3)
>>> bool(np.allclose(np.diag(m), 1.0))
True
>>> bool(abs(m[0, 1] - 0.5) < 0.05)          # population tau is 0.5
True

cor_spearman

cor_spearman(x: ArrayLike) -> NDArray[np.float64]

Measure how strongly each pair of columns is correlated, after ranking them.

Returns the matrix of pairwise Spearman's rho: the ordinary (Pearson) correlation of the ranks. Like Kendall's tau it ranges from -1 to 1 and is unaffected by the marginal distributions.

Parameters:

Name Type Description Default
x array_like of float, shape (n, d)

Data or pseudo-observations, one column per variable. Must be 2-D.

required

Returns:

Type Description
numpy.ndarray of float, shape (d, d)

Symmetric matrix with ones on the diagonal; entry [i, j] is Spearman's rho between columns i and j, in [-1, 1].

Examples:

>>> import numpy as np
>>> from rcopula import GaussianCopula, cor_spearman
>>> u = GaussianCopula(0.7, dim=2).rvs(4000, random_state=1)
>>> bool(abs(cor_spearman(u)[0, 1] - GaussianCopula(0.7).rho()) < 0.05)
True

beta_n

beta_n(u: ArrayLike) -> float

Measure dependence by how often all columns are on the same side of their medians.

This is the sample Blomqvist's beta (R's betan). :math:\beta measures dependence at the centre only: the proportion of observations in the two concordant quadrants around the median, rescaled to :math:[-1, 1]. Cheap to compute and robust, but blind to the tails.

Parameters:

Name Type Description Default
u array_like of float, shape (n, d)

Data or pseudo-observations, d >= 2 columns. Only the position of each value relative to its column median matters, so raw data works as well as ranks.

required

Returns:

Type Description
float

The estimate, in [-1, 1] for d = 2: 1 when every observation is in the all-below or all-above corner, about 0 under independence.

Notes

For d columns the statistic is :math:(2^{d-1}(p_{\le} + p_{>}) - 1) / (2^{d-1} - 1), where :math:p_{\le} and :math:p_{>} are the fractions of rows with every coordinate at or below, or every coordinate above, its column median.

Examples:

>>> import numpy as np
>>> from rcopula import ClaytonCopula, beta_n
>>> u = ClaytonCopula(2.0).rvs(20_000, random_state=3)
>>> bool(abs(beta_n(u) - ClaytonCopula(2.0).beta()) < 0.03)
True

Independence gives approximately zero:

>>> rng = np.random.default_rng(0)
>>> bool(abs(beta_n(rng.uniform(size=(20_000, 2)))) < 0.03)
True

fit_lambda

fit_lambda(x: ArrayLike, k: int | None = None, *, method: str = 'schmidt-stadtmuller', ties_method: str = 'average') -> TailEstimate

Estimate how likely two variables are to be extreme together, assuming no family.

Use this to decide which family to fit: whether the data shows joint crashes (lower tail), joint booms (upper tail), both, or neither.

Every parametric estimate of :math:\lambda is really an estimate of the family: fit a Gaussian copula and you will get zero whatever the data says, fit a t copula and you will get something positive. This asks the data directly, which is the right way round when the question is which family.

Two estimators, both built on the empirical copula near a corner:

"schmidt-stadtmuller" :math:\hat\lambda_U = \frac{n}{k}\,\hat C\bigl(\text{corner of size } k/n\bigr), the proportion of points in the corner divided by what independence would put there. Asymptotically normal, and the standard choice. "log" :math:\hat\lambda_U = 2 - \log \hat C(u,u) / \log u at :math:u = 1 - k/n (Caperaa, Fougeres and Genest). Less variable in the far tail, more biased when the copula is not extreme-value.

Parameters:

Name Type Description Default
x array_like of float, shape (n, 2)

Data or pseudo-observations for exactly two variables; ranks are taken either way.

required
k int or None

Number of most-extreme observations to use, with 1 <= k < n. None means :math:\lfloor \sqrt n \rfloor, which is a convention rather than a result -- look at the path.

None
method ('schmidt-stadtmuller', 'log')

Which estimator to use; see above.

"schmidt-stadtmuller"
ties_method str

How to rank tied values; one of the names accepted by :func:pseudo_obs, which it is passed to.

"average"

Returns:

Type Description
TailEstimate

Lower and upper estimates with standard errors, k, n, method and the threshold path.

Raises:

Type Description
ValueError

If x does not have exactly two columns, method is unknown, or k is outside 1 <= k < n.

Notes

There is no threshold-free estimator of a tail dependence coefficient, and no automatic choice of k that is right in general: small k is unbiased and noisy, large k is stable and measures the middle of the distribution rather than the tail. Frahm, Junker and Schmidt recommend reading the estimate off a plateau in k, and the returned path exists so that can be done rather than assumed.

Examples:

It separates the two tails of an asymmetric family, which no single number for "dependence" can:

>>> import numpy as np, rcopula as rc
>>> from rcopula.dependence import fit_lambda
>>> clayton = rc.ClaytonCopula.from_tau(0.5).rvs(20000, random_state=0)
>>> estimate = fit_lambda(clayton)
>>> bool(estimate.lower > 0.5 and estimate.upper < 0.2)
True

The Gaussian case is the one to understand before trusting any single number. Its true tail dependence is zero, but it vanishes only logarithmically, so at any feasible threshold the estimate is visibly positive -- here 0.22 against a t copula's 0.43 at the same Kendall's tau:

>>> heavy = rc.StudentCopula.from_tau(0.5, df=3.0).rvs(20000, random_state=0)
>>> light = rc.GaussianCopula.from_tau(0.5).rvs(20000, random_state=0)
>>> round(fit_lambda(heavy).upper, 2), round(fit_lambda(light).upper, 2)
(0.43, 0.22)

What tells them apart is the path, not the point. Real tail dependence gives a flat plateau; the Gaussian's estimate slides steadily downwards as the threshold is pushed out, because it is converging to zero:

>>> def slope(u):
...     path = fit_lambda(u).path
...     keep = path[:, 0] >= 50
...     return float(np.polyfit(np.log(path[keep, 0]), path[keep, 2], 1)[0])
>>> bool(slope(heavy) < 0.5 * slope(light))
True

to_emp_margins

to_emp_margins(u: ArrayLike, data: ArrayLike) -> NDArray[np.float64]

Turn values between 0 and 1 back into values that look like a reference dataset.

Each column of u is mapped onto the observed values of the matching column of data (the empirical margins).

The inverse direction of :func:pseudo_obs, and the last step of a simulation that wants to keep the data's own marginal shapes rather than impose parametric ones. Draw from a fitted copula, push the draws through here, and the result has the copula's dependence with the sample's margins -- the copula analogue of filtered historical simulation.

Formally each coordinate is passed through the empirical quantile function :math:\hat F_j^{-1}, so the output can only take values the reference sample actually contains.

Parameters:

Name Type Description Default
u array_like of float, shape (n, d)

Values in :math:[0, 1], typically from copula.rvs. A 1-D input is treated as a single row.

required
data array_like of float, shape (m, d)

The reference sample. Needs the same number of columns, not the same number of rows.

required

Returns:

Type Description
numpy.ndarray of float, shape (n, d)

Column j holds empirical quantiles of data[:, j] at levels u[:, j]; every value is one that occurs in data.

Raises:

Type Description
ValueError

If u and data have different numbers of columns, or u has values outside [0, 1].

Notes

The output is confined to the reference sample's range by construction: an empirical quantile function cannot extrapolate. That is a feature for a historical-simulation study and a serious limitation for a tail one -- a 99.9% capital number computed this way can never exceed the worst loss already observed. Use parametric margins when the question is about events larger than anything in the sample.

Examples:

>>> import numpy as np, rcopula as rc
>>> from rcopula.dependence import to_emp_margins
>>> rng = np.random.default_rng(0)
>>> history = rng.lognormal(size=(2000, 2)) * [1.0, 5.0]
>>> drawn = to_emp_margins(rc.ClaytonCopula(2.0).rvs(5000, random_state=0), history)

The margins are the sample's, so the medians match -- compared relatively, since the second column is scaled by five and an absolute bound would be measuring the scaling rather than the agreement:

>>> ratio = np.median(drawn, axis=0) / np.median(history, axis=0)
>>> bool(np.all(np.abs(ratio - 1.0) < 0.1))
True

...and the dependence is the copula's, which the history did not have:

>>> bool(abs(rc.cor_kendall(drawn)[0, 1] - 0.5) < 0.03)
True

Nothing outside the reference range can come out:

>>> bool(drawn.max() <= history.max() and drawn.min() >= history.min())
True

Datasets

rcopula.datasets

Datasets for copula examples, fetched rather than vendored.

Why nothing is bundled. R's copula package is GPL-3, and so are the datasets inside it (loss, rdj, gasoil, uranium, SMI.12); lcopula::danube is GPL-3 and survival's are LGPL. Copying any of them into an MIT package would be a licence conflict, however convenient. Numbers generated by running GPL software are facts and are fine to commit -- which is why the golden fixtures are in the repository and the datasets are not.

So this module ships URLs, licences and SHA-256 digests, and fetches on first use. Every source here is public domain or permissively licensed, and the digest is checked on arrival: a silently changed upstream file would otherwise turn a reproducible example into an irreproducible one.

============================ ================================================ :func:available What can be loaded, with licence and source. :func:load Fetch (once), verify, cache and parse. :func:cache_dir Where the files live. :func:clear_cache Remove them. ============================ ================================================

Nothing here is imported at package load, and no network call happens until :func:load is called.

Examples:

>>> from rcopula.datasets import available
>>> sorted(available())
['ghcn_temperature', 'nwis_peaks', 'uci_abalone', 'uci_airfoil', 'uci_wine_quality_red']
>>> available()["nwis_peaks"].licence
'public domain (work of the US Government)'

DatasetSpec dataclass

DatasetSpec(name: str, description: str, url: str, licence: str, sha256: str | None, reader: str, columns: tuple[str, ...], citation: str)

A catalogue entry describing one downloadable dataset: its source, licence and columns.

You get these from :func:available; you do not normally build one. The class is a frozen (read-only) dataclass.

Attributes:

Name Type Description
name str

Key in :func:available.

description str

One line on what the columns mean.

url str

Where it is fetched from.

licence str

Why it is legitimate to redistribute a link to it and cache it.

sha256 str or None

Digest of the raw bytes, checked on every download and every time the cached copy is read (a cached file that no longer matches is re-downloaded, or refused when download=False). None means the upstream file is not byte-stable (a live query endpoint), so no byte check is possible; only the parsed table's structure is checked -- every column in columns must be present, or :func:load raises.

reader {'usgs_rdb', 'ghcn_csv', 'csv'}

Which parser to use.

columns tuple of str

The columns the loader returns, in order.

citation str

What to cite when a result depends on it.

available

available() -> dict[str, DatasetSpec]

List the datasets that :func:load can fetch, with their source and licence.

No network access is needed; this only reads the built-in catalogue.

Returns:

Type Description
dict of str to DatasetSpec

Dataset name (the argument to :func:load) to its description. The dictionary is a fresh copy, so changing it does not affect the catalogue.

Examples:

>>> from rcopula.datasets import available
>>> spec = available()["nwis_peaks"]
>>> spec.columns
('water_year', 'peak_flow', 'gauge_height')
>>> "public domain" in spec.licence
True

cache_dir

cache_dir() -> Path

Return the folder where downloaded dataset files are stored.

Honours RCOPULA_DATA if set, otherwise XDG_CACHE_HOME, otherwise ~/.cache. In the last two cases the folder is rcopula inside that base directory. The folder is not created here; :func:load creates it.

Returns:

Type Description
Path

The cache directory (it may not exist yet).

Examples:

>>> from rcopula.datasets import cache_dir
>>> cache_dir().name
'rcopula'

clear_cache

clear_cache() -> int

Delete every downloaded dataset file from the cache folder.

The next :func:load call for a dataset will download it again. Only files directly inside :func:cache_dir are removed; subfolders are left alone.

Returns:

Type Description
int

How many files were removed (0 if the folder does not exist).

load

load(name: str, *, download: bool = True, refresh: bool = False, **kwargs: Any) -> pd.DataFrame

Load one of the example datasets as a pandas DataFrame, downloading it the first time.

The raw file is saved in :func:cache_dir, so later calls work offline. Files with a recorded SHA-256 digest are checked when downloaded and again whenever the cached copy is read, so a silently changed upstream file -- or a cache file that was corrupted or edited -- is caught rather than used. A cached copy that fails the check is downloaded afresh (and the fresh copy checked); with download=False it raises instead. Datasets without a digest are checked only for having the expected columns.

Parameters:

Name Type Description Default
name str

A key from :func:available, e.g. "nwis_peaks".

required
download bool

If False, use the cache only and raise rather than reach the network. Useful in tests and offline builds.

True
refresh bool

Re-fetch even if a cached copy exists.

False
**kwargs Any

Passed to the dataset's parser. No current parser uses them.

{}

Returns:

Type Description
DataFrame

With the columns named in the spec (available()[name].columns), rows containing missing values in those columns dropped, and the index reset. frame.attrs holds "dataset", "licence" and "citation" strings.

Raises:

Type Description
KeyError

If name is not in the registry -- listing what is.

OSError

If the download fails its digest check, the file cannot be parsed into the expected columns, or a download is needed and download=False (including a cached copy that fails its digest check). Network failures surface as :class:urllib.error.URLError, itself a subclass of OSError.

Examples:

>>> import rcopula as rc
>>> flood = rc.datasets.load("nwis_peaks")
>>> rc.cor_kendall(flood[["peak_flow", "gauge_height"]])