A covariance matrix summarizes pairwise linear co-variation among numeric variables. Diagonal entries are variances; off-diagonal entries are covariances. The matrix is symmetric when every pair uses the same observations and estimator.

Covariance is scale-dependent and does not establish causation. Its sign indicates the direction of linear co-variation; magnitude depends on units. Use correlation when a unitless linear association is the intended summary, while remembering that correlation also does not imply causality.

Choose the estimator

For observations x₁,…,xₙ with mean vector , the usual unbiased sample covariance is S = (1/(n−1)) Σᵢ (xᵢ−x̄)(xᵢ−x̄)ᵀ. Dividing by n gives the maximum-likelihood population covariance under an independent multivariate normal model and is also used when the supplied records are treated as the full population.

State whether rows or columns are observations, which variables are included, the denominator, weights, missing-data rule, and preprocessing. Software defaults differ.

A small worked example

Suppose rows are observations and columns are variables:

X = [[1, 2],
     [2, 4],
     [3, 5]]

The column means are 2 and 11/3. Using denominator n−1 = 2, the sample covariance matrix is approximately [[1.000, 1.500], [1.500, 2.333]]. The positive off-diagonal entry describes positive linear co-variation for this sample; it does not identify a mechanism.

Calculate covariance in Python

import numpy as np

X = np.array([[1., 2.], [2., 4.], [3., 5.]])
sample_cov = np.cov(X, rowvar=False, ddof=1)
population_cov = np.cov(X, rowvar=False, ddof=0)
print(sample_cov)

rowvar=False declares that columns are variables. ddof=1 produces the sample denominator for unweighted data. NumPy also supports frequency and analytic weights, whose normalization requires separate review.

import pandas as pd

df = pd.DataFrame(X, columns=["a", "b"])
sample_cov = df.cov(min_periods=len(df))

DataFrame.cov uses pairwise complete observations by default. With different missing rows for different pairs, the result can fail to be positive semidefinite. Requiring complete cases, modeling missingness, or using a justified covariance estimator may be preferable.

Calculate covariance in R

X <- matrix(c(1, 2, 2, 4, 3, 5), ncol = 2, byrow = TRUE)
colnames(X) <- c("a", "b")
sample_cov <- cov(X, use = "complete.obs")
print(sample_cov)

R’s cov returns the sample covariance. Choose the use rule deliberately; pairwise-complete covariance can have the same coherence problem as other pairwise estimates.

Validate the result

  • Confirm orientation, numeric parsing, units, weights, duplicates, and observation independence assumptions.
  • Check symmetry within floating-point tolerance and nonnegative diagonal entries.
  • Inspect eigenvalues. A theoretically valid covariance matrix is positive semidefinite, although tiny negative values can arise from rounding.
  • Investigate missingness, near-constant variables, extreme observations, and numerical scale.
  • Record preprocessing fitted on training data only when covariance feeds a predictive pipeline.

When variables outnumber observations, the sample covariance is singular. Near-collinearity can make it ill-conditioned. Shrinkage or structured estimators may improve downstream stability, but their assumptions and tuning must be reported.

Interpretation and downstream use

Covariance appears in portfolio models, multivariate normal models, Kalman filters, generalized least squares, and principal component analysis. In PCA, centering and the choice between a covariance and standardized correlation matrix materially affect components; see dimensionality reduction techniques.

Sampling uncertainty matters, especially with small or dependent samples. A matrix of point estimates does not supply confidence by itself; see statistical significance and confidence intervals. Text feature matrices used in Python topic modeling are often sparse and high-dimensional, so a dense ordinary covariance matrix may be impractical or inappropriate.

Online calculators can be useful for nonsensitive checks, but do not upload confidential data without reviewing processing, retention, and security. Reproducible code with recorded versions and input checks is preferable for consequential analysis.

Originally published June 11, 2025; technically reviewed and substantially updated September 4, 2026.