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: |
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 ( |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Human-readable family name, e.g. |
param_names |
tuple of str
|
Names of the parameters, in the order they appear in :attr: |
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 |
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. |
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,)
|
|
n_params
property
¶
n_params: int
How many parameters fitting will estimate (fixed ones are not counted).
Returns:
| Type | Description |
|---|---|
int
|
Number of |
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 |
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: |
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,)
|
|
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. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any parameter is still |
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; |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any parameter is still |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n,)
|
Probabilities in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any parameter is still |
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 |
None
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (size, d)
|
Samples with values in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any parameter is still |
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 |
required |
upper
|
array_like of float, shape (d,)
|
Upper corner of the box, elementwise |
required |
Returns:
| Type | Description |
|---|---|
float
|
Probability of the box, in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
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 |
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 |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the copula is unfitted (any parameter |
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 |
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 |
required |
dim
|
int
|
Number of variables of the copula to build. |
2
|
**kwargs
|
Any
|
Extra family-specific constructor arguments (e.g. |
{}
|
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 |
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 |
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 |
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 |
upper |
float
|
Upper tail-dependence coefficient, in |
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 |
nan
|
dim
|
int
|
Number of variables |
2
|
dispstr
|
('ex', 'ar1', 'toep', 'un')
|
Correlation structure: exchangeable (1 parameter), autoregressive
(1), Toeplitz ( |
"ex"
|
free
|
array_like of bool or None
|
Keyword-only mask of parameters :func: |
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 |
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
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the parameters are still unspecified ( |
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 |
nan
|
dim
|
int
|
Number of variables |
2
|
dispstr
|
('ex', 'ar1', 'toep', 'un')
|
Correlation structure; see :class: |
"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 |
False
|
free
|
array_like of bool or None
|
Keyword-only mask of parameters (correlations then |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
df |
float
|
Degrees of freedom. |
df_fixed |
bool
|
Whether |
dispstr |
str
|
The correlation structure. |
params |
numpy.ndarray of float
|
Correlation parameters followed by |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown |
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: |
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: |
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
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the parameters are still unspecified ( |
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 |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the parameters are still unspecified ( |
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. |
{}
|
Returns:
| Type | Description |
|---|---|
StudentCopula
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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. |
{}
|
Returns:
| Type | Description |
|---|---|
StudentCopula
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
required |
dim
|
int
|
Size |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (dim, dim)
|
Symmetric matrix with ones on the diagonal. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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,)
|
|
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 |
nan
|
dim
|
int
|
Number of variables, at least 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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, |
nan
|
dim
|
int
|
Number of variables, at least 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
nan
|
dim
|
int
|
Number of variables, at least 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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, |
nan
|
dim
|
int
|
Number of variables, at least 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
nan
|
dim
|
int
|
Number of variables, at least 2. (R's |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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. |
param_name |
str
|
Name of the single dependence parameter, |
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)
|
|
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``t``
|
Values in |
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 |
required |
theta
|
float
|
Dependence parameter, inside :meth: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``u``
|
Non-negative values; :math: |
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``t``
|
|
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: |
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``
|
|
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: |
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: |
required |
Returns:
| Type | Description |
|---|---|
TailDependence
|
Named tuple |
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: |
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: |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
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
|
|
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: |
required |
rng
|
Generator
|
Source of randomness. |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (size,)
|
Log frailty draws (may be |
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``log_t``
|
Generator values in |
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: |
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 |
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: |
required |
dim
|
int
|
Copula dimension. |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n,), or None
|
Log density at each row, or |
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 |
required |
Returns:
| Type | Description |
|---|---|
float
|
Population Spearman's rho, clipped to |
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 |
required |
dim
|
int
|
Copula dimension; it can restrict the reachable range. |
2
|
Returns:
| Type | Description |
|---|---|
float
|
The parameter |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
required |
dim
|
int
|
Copula dimension; it restricts the admissible |
2
|
Returns:
| Type | Description |
|---|---|
float
|
The parameter |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
|
dim
|
int
|
Number of variables. Must be |
2
|
free
|
array_like of bool, shape (n_params,), or None
|
Which parameters a fit may change; |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
dim |
int
|
Always |
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 |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
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 |
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 |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any parameter is still |
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 |
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
|
{}
|
Returns:
| Type | Description |
|---|---|
ExtremeValueCopula
|
A new instance of the calling class with the calibrated parameter. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
nan
|
dim
|
int
|
Must be |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
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
|
|
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
nan
|
dim
|
int
|
Must be |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
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
|
|
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
nan
|
dim
|
int
|
Must be |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
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
|
|
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
|
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 |
nan
|
dim
|
int
|
Must be |
2
|
df
|
float
|
Degrees of freedom, |
4.0
|
free
|
array_like of bool, shape (2,), or None
|
Which of |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
rho_param |
float
|
The correlation parameter (named so as not to clash with the
:meth: |
df |
float
|
The degrees of freedom. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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.
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
|
|
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
required |
dim
|
int
|
Number of variables; must be 2 (always bivariate). |
2
|
**kwargs
|
Any
|
|
{}
|
Returns:
| Type | Description |
|---|---|
TEVCopula
|
A new copula with the calibrated |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
required |
theta
|
float
|
Gumbel dependence parameter, |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, same shape as ``w``
|
Values of |
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 |
nan
|
dim
|
int
|
Number of variables; must be exactly 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The cross-product ratio. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
|
|
param_bounds
property
¶
param_bounds: list[tuple[float, float]]
Admissible range of theta.
Returns:
| Type | Description |
|---|---|
list of tuple of (float, float)
|
|
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 |
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 |
lambda_ ¶
lambda_() -> TailDependence
Return how strongly extreme values occur together (tail dependence).
Plackett has no tail dependence at any theta.
Returns:
| Type | Description |
|---|---|
TailDependence
|
|
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. |
{}
|
Returns:
| Type | Description |
|---|---|
PlackettCopula
|
A new copula with |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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. |
{}
|
Returns:
| Type | Description |
|---|---|
PlackettCopula
|
A new copula with |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
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 |
nan
|
dim
|
int
|
Number of variables; must be exactly 2. |
2
|
free
|
array_like of bool, shape (1,), or None
|
Whether |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
theta |
float
|
The dependence parameter. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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
|
|
param_bounds
property
¶
param_bounds: list[tuple[float, float]]
Admissible range of theta.
Returns:
| Type | Description |
|---|---|
list of tuple of (float, float)
|
|
tau ¶
tau() -> float
Return Kendall's tau, a rank correlation; here :math:\tau = 2\theta/9.
Returns:
| Type | Description |
|---|---|
float
|
Kendall's tau, in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
rho ¶
rho() -> float
Return Spearman's rho, a rank correlation; here :math:\rho = \theta/3.
Returns:
| Type | Description |
|---|---|
float
|
Spearman's rho, in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
lambda_ ¶
lambda_() -> TailDependence
Return how strongly extreme values occur together (tail dependence).
FGM has no tail dependence at any theta.
Returns:
| Type | Description |
|---|---|
TailDependence
|
|
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 |
required |
dim
|
int
|
Number of variables; must be 2. |
2
|
**kwargs
|
Any
|
Passed to the constructor, e.g. |
{}
|
Returns:
| Type | Description |
|---|---|
FGMCopula
|
A new copula with |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
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 |
required |
dim
|
int
|
Number of variables; must be 2. |
2
|
**kwargs
|
Any
|
Passed to the constructor, e.g. |
{}
|
Returns:
| Type | Description |
|---|---|
FGMCopula
|
A new copula with |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
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 |
nan
|
alpha2
|
float
|
The same share for the second component, in |
nan
|
dim
|
int
|
Number of variables; must be exactly 2. |
2
|
free
|
array_like of bool, shape (2,), or None
|
Which of |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
alpha |
numpy.ndarray of float, shape (2,)
|
The two shock parameters |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
param_bounds
property
¶
param_bounds: list[tuple[float, float]]
Admissible ranges of alpha1 and alpha2.
Returns:
| Type | Description |
|---|---|
list of tuple of (float, float)
|
|
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 ( |
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 ( |
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
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If a parameter is unspecified ( |
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 |
2
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If given any keyword other than |
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
|
|
rho ¶
rho() -> float
Return Spearman's rho, a rank correlation in [-1, 1]; here 0.0.
Returns:
| Type | Description |
|---|---|
float
|
|
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
|
|
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 |
2
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If given any keyword other than |
NotImplementedError
|
From :meth: |
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
|
|
rho ¶
rho() -> float
Return Spearman's rho, a rank correlation in [-1, 1]; here 1.0.
Returns:
| Type | Description |
|---|---|
float
|
|
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
|
|
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 |
TypeError
|
If given any keyword other than |
NotImplementedError
|
From :meth: |
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
|
|
rho ¶
rho() -> float
Return Spearman's rho, a rank correlation in [-1, 1]; here -1.0.
Returns:
| Type | Description |
|---|---|
float
|
|
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
|
|
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
( |
required |
smoothing
|
('none', 'beta', 'checkerboard')
|
|
"none"
|
offset
|
float
|
Keyword-only. Added to the denominator |
0.0
|
ties_method
|
str
|
Keyword-only. How tied values are ranked; passed to
:func: |
"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 |
TypeError
|
If given an unknown keyword argument (they used to be silently
ignored, so a misspelling such as |
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 |
None
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (m, d)
|
Column |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
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 |
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: |
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
|
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
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. |
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. |
required |
margins
|
Margin or sequence of Margin
|
One distribution per variable, in column order, typically scipy frozen
distributions such as |
required |
names
|
list of str or None
|
Column names, length |
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,)
|
|
names |
list of str or None
|
Column names, or |
dim |
int
|
Number of variables. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If the number of margins or names does not match |
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n,)
|
Probabilities in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n,)
|
Log density (or log mass, with discrete margins); |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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: |
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 |
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
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (size, d), or pandas.DataFrame
|
Samples, one row per observation. A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the copula is unfitted or |
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: |
required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n, d)
|
Marginal probabilities in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
describe ¶
describe() -> str
Short human-readable summary of the copula and the margins.
Returns:
| Type | Description |
|---|---|
str
|
One line, e.g.
|
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 probabilityP(X <= x)for array_likex.pdf(x): density atx.ppf(q): quantile function, the inverse ofcdf, forqin[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 |
lower_se, upper_se |
float
|
Asymptotic standard errors. The counts behind them are binomial, so
these are only meaningful when |
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: |
path |
numpy.ndarray of float, shape (m, 3)
|
|
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 |
required |
ties_method
|
str
|
How to rank tied values: one of |
"average"
|
lower_tail
|
bool
|
If |
True
|
random_state
|
(int, Generator or None)
|
Seed or generator for breaking ties. Only used when
|
None
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray of float, shape (n, d), or pandas.DataFrame
|
Values strictly inside |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
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, |
required |
Returns:
| Type | Description |
|---|---|
float
|
The estimate, in |
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 |
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: |
"average"
|
Returns:
| Type | Description |
|---|---|
TailEstimate
|
Lower and upper estimates with standard errors, |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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: |
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 |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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: |
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 |
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: |
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: |
required |
download
|
bool
|
If |
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 ( |
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
OSError
|
If the download fails its digest check, the file cannot be parsed
into the expected columns, or a download is needed and
|
Examples:
>>> import rcopula as rc
>>> flood = rc.datasets.load("nwis_peaks")
>>> rc.cor_kendall(flood[["peak_flow", "gauge_height"]])