cov

array_api_extra.cov(m, /, *, axis=-1, correction=1, fweights=None, aweights=None, xp=None)

Estimate a covariance matrix (or a stack of covariance matrices).

Covariance indicates the level to which two variables vary together. If we examine N-dimensional samples, \(X = [x_1, x_2, ... x_N]^T\), each with M observations, then element \(C_{ij}\) of the \(N \times N\) covariance matrix is the covariance of \(x_i\) and \(x_j\). The element \(C_{ii}\) is the variance of \(x_i\).

Extends numpy.cov() with support for batch input. Naming follows the array API conventions used elsewhere in this library (axis, correction) rather than the NumPy spellings (rowvar, bias, ddof); see Notes for the mapping.

Parameters:
  • m (object) – An array of shape (..., N, M) whose innermost two dimensions contain M observations of N variables by default. The axis of observations is controlled by axis.

  • axis (int) – Axis of m containing the observations. Default: -1 (the last axis), matching the array API convention. Use axis=-2 (or 0 for 2-D input) to treat each column as a variable, which corresponds to rowvar=False in numpy.cov().

  • correction (float) – Degrees of freedom correction: normalization divides by N - correction (for unweighted input). Default: 1, which gives the unbiased estimate (matches numpy.cov() default of bias=False). Set to 0 for the biased estimate (N normalization). Corresponds to ddof in numpy.cov() and to correction in numpy.var()/numpy.std() and torch.cov(). Non-integer values are allowed for advanced use cases: the unbiased correction for weighted observations depends on the sum and dispersion of the weights and is generally not an integer, and autocorrelated data may also require a fractional correction. Non-integer correction routes through the generic implementation because numpy.cov()’s ddof and torch.cov()’s correction both require integers.

  • fweights (object | None) – 1-D array of integer frequency weights: the number of times each observation is repeated. Same as fweights in numpy.cov()/torch.cov().

  • aweights (object | None) – 1-D array of observation-vector weights (analytic weights). Larger values mark more important observations. Same as aweights in numpy.cov()/torch.cov().

  • xp (ModuleType | None) – The standard-compatible namespace for m. Default: infer.

Returns:

An array having shape (..., N, N) whose innermost two dimensions represent the covariance matrix of the variables.

Return type:

object

Notes

Mapping from numpy.cov() to this function:

numpy.cov(m, rowvar=True)           -> cov(m, axis=-1)   # default
numpy.cov(m, rowvar=False)          -> cov(m, axis=-2)
numpy.cov(m, bias=True)             -> cov(m, correction=0)
numpy.cov(m, ddof=k)                -> cov(m, correction=k)
numpy.cov(m, fweights=f)            -> cov(m, fweights=f)
numpy.cov(m, aweights=a)            -> cov(m, aweights=a)

A RuntimeWarning is emitted for non-positive effective degrees of freedom when the effective normalizer can be checked without materializing a lazy array. When the normalizer itself is lazy (e.g. for weighted Dask inputs), this check is skipped; choose correction and weights such that it is positive.

Examples

>>> import array_api_strict as xp
>>> import array_api_extra as xpx

Consider two variables, \(x_0\) and \(x_1\), which correlate perfectly, but in opposite directions:

>>> x = xp.asarray([[0, 2], [1, 1], [2, 0]]).T
>>> x
Array([[0, 1, 2],
       [2, 1, 0]], dtype=array_api_strict.int64)

Note how \(x_0\) increases while \(x_1\) decreases. The covariance matrix shows this clearly:

>>> xpx.cov(x, xp=xp)
Array([[ 1., -1.],
       [-1.,  1.]], dtype=array_api_strict.float64)

Note that element \(C_{0,1}\), which shows the correlation between \(x_0\) and \(x_1\), is negative.

Further, note how x and y are combined:

>>> x = xp.asarray([-2.1, -1,  4.3])
>>> y = xp.asarray([3,  1.1,  0.12])
>>> X = xp.stack((x, y), axis=0)
>>> xpx.cov(X, xp=xp)
Array([[11.71      , -4.286     ],
       [-4.286     ,  2.14413333]], dtype=array_api_strict.float64)
>>> xpx.cov(x, xp=xp)
Array(11.71, dtype=array_api_strict.float64)
>>> xpx.cov(y, xp=xp)
Array(2.14413333, dtype=array_api_strict.float64)

Input with more than two dimensions is treated as a stack of two-dimensional input.

>>> stack = xp.stack((X, 2*X))
>>> xpx.cov(stack)
Array([[[ 11.71      ,  -4.286     ],
        [ -4.286     ,   2.14413333]],
       [[ 46.84      , -17.144     ],
        [-17.144     ,   8.57653333]]], dtype=array_api_strict.float64)

The normalization can be adjusted with correction, and observations can be weighted with integer frequencies fweights or importance weights aweights:

>>> x = xp.asarray([0., 1., 2., 3., 4.])
>>> xpx.cov(x, xp=xp)  # unbiased variance: divide by N - 1
Array(2.5, dtype=array_api_strict.float64)
>>> xpx.cov(x, correction=0, xp=xp)  # biased variance: divide by N
Array(2., dtype=array_api_strict.float64)

Giving the two extreme observations frequency 2 via fweights is equivalent to repeating them in x:

>>> xpx.cov(x, fweights=xp.asarray([2, 1, 1, 1, 2]), xp=xp)
Array(3., dtype=array_api_strict.float64)
>>> xpx.cov(xp.asarray([0., 0., 1., 2., 3., 4., 4.]), xp=xp)
Array(3., dtype=array_api_strict.float64)

aweights instead adjusts the relative importance of observations, here down-weighting the two extremes:

>>> xpx.cov(x, aweights=xp.asarray([0.5, 1., 1., 1., 0.5]), xp=xp)
Array(1.92, dtype=array_api_strict.float64)