confidence_ellipse

confidence_ellipse(
    data: pd.DataFrame,
    x: str,
    y: str,
    z: Optional[str] = None,
    group_by: Optional[str] = None,
    conf_level: float = 0.95,
    robust: bool = False,
    distribution: Literal['normal', 'hotelling'] = 'normal',
)

Coordinates of confidence ellipses or ellipsoids of raw data, optionally by group.

Computes points on the boundary of the confidence region of two (ellipse) or three (ellipsoid) variables at a given confidence level, from the classical or a robust estimate of their mean and covariance matrix, and a normal or Hotelling’s T-squared quantile.

Parameters

data : pandas.DataFrame

Data containing the variables.

x : str

Column of the x-axis variable.

y : str

Column of the y-axis variable.

z : str = None

Column of the z-axis variable. When given, an ellipsoid is computed instead of an ellipse.

group_by : str = None

Categorical column defining groups. When given, one ellipse (ellipsoid) is computed for each group.

conf_level : float = 0.95

Confidence level, strictly between 0 and 1.

robust : bool = False

When True, the mean and covariance matrix are estimated robustly with scikit-learn’s EllipticEnvelope (Minimum Covariance Determinant). If the robust fit fails, the classical estimates are used with a warning.

distribution : str = 'normal'

Distribution of the quantile scaling the region: the chi-square distribution ('normal', for large samples) or Hotelling’s T-squared distribution ('hotelling', for small samples). See Notes.

Returns

: pandas.DataFrame

Coordinates of the points, in columns 'x' and 'y', plus 'z' for an ellipsoid: 361 points around each ellipse, or a 50 by 50 grid of points on each ellipsoid. With group_by, the group of each point is in a column named after group_by.

Raises

: TypeError

If data is not a DataFrame or conf_level is not a number.

: ValueError

If a column is missing, an argument is invalid, a group has fewer than three observations (four for an ellipsoid), or the covariance matrix contains missing values.

Notes

With \(\hat{\boldsymbol{\mu}}\) and \(\hat{\boldsymbol{\Sigma}} = \mathbf{V} \boldsymbol{\Lambda} \mathbf{V}^\top\) the estimated mean and covariance matrix of the \(p\) variables (\(p = 2\) or 3) and \(c\) the quantile, the points are \(\hat{\boldsymbol{\mu}} + \mathbf{V} (c\boldsymbol{\Lambda})^{1/2} \mathbf{u}\), where \(\mathbf{u}\) runs over the unit circle (sphere). The quantile is \(c = \chi^2_{1-\alpha}(p)\) with distribution='normal', and \(c = \frac{p(n-1)}{n-p} F_{1-\alpha}(p, n-p)\) with distribution='hotelling', where \(n\) is the number of observations (of each group). Derivations are given on the Theory page of the documentation, https://christiangoueguel.com/pyEllipse/theory.html.

At least \(p + 1\) observations are required, so that the covariance matrix can be nonsingular and \(n - p > 0\). A singular covariance matrix, e.g. of collinear variables, gives a flat region: a line segment, or a flat ellipsoid.

The robust estimates come from EllipticEnvelope(support_fraction=0.9, random_state=42), i.e. the reweighted Minimum Covariance Determinant estimator computed with the FAST-MCD algorithm (Rousseeuw and Van Driessen, 1999) on 90% of the observations. The reweighted covariance matrix is made consistent at the normal distribution (Croux and Haesbroeck, 1999), as by scikit-learn >= 1.8, so that the robust and classical regions agree for normal data without outliers. Older versions of scikit-learn do not apply this factor, so pyEllipse applies it.

References

Croux, C. and Haesbroeck, G. (1999). Influence function and efficiency of the minimum covariance determinant scatter matrix estimator. Journal of Multivariate Analysis, 71(2), 161-190.

Rousseeuw, P. J. and Van Driessen, K. (1999). A fast algorithm for the minimum covariance determinant estimator. Technometrics, 41(3), 212-223.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> from pyEllipse import confidence_ellipse
>>> rng = np.random.default_rng(0)
>>> df = pd.DataFrame(rng.standard_normal((60, 2)), columns=['u', 'v'])
>>> df['group'] = np.repeat(['A', 'B', 'C'], 20)
>>> ellipses = confidence_ellipse(df, x='u', y='v', group_by='group', distribution='hotelling')
>>> ellipses.shape
(1083, 3)
>>> ellipses['group'].unique().tolist()
['A', 'B', 'C']