Source code for plotnine_extra.stats.stat_conf_ellipse
from __future__ import annotations
import numpy as np
import pandas as pd
from plotnine.doctools import document
from plotnine.stats.stat import stat
from scipy.stats import chi2
[docs]
@document
class stat_conf_ellipse(stat):
"""
Compute confidence ellipses around group barycenters
Draws a confidence ellipse based on the covariance
structure of the bivariate data within each group.
{usage}
Parameters
----------
{common_parameters}
level : float, default=0.95
Confidence level for the ellipse.
npoint : int, default=100
Number of points used to draw the ellipse.
bary : bool, default=True
If ``True``, compute the ellipse around the
barycenter (mean). If ``False``, compute the
ellipse around the data cloud.
See Also
--------
plotnine.geom_path : The default `geom` for this `stat`.
plotnine.geom_polygon : Alternative `geom` for filled
ellipses.
"""
_aesthetics_doc = """
{aesthetics_table}
"""
REQUIRED_AES = {"x", "y"}
DEFAULT_PARAMS = {
"geom": "path",
"position": "identity",
"na_rm": False,
"level": 0.95,
"npoint": 100,
"bary": True,
}
[docs]
def compute_group(self, data, scales) -> pd.DataFrame:
x = data["x"].to_numpy(dtype=float)
y = data["y"].to_numpy(dtype=float)
if len(x) < 3:
return pd.DataFrame({"x": [], "y": []})
level = self.params["level"]
npoint = self.params["npoint"]
bary = self.params["bary"]
center_x = np.mean(x)
center_y = np.mean(y)
# Covariance matrix
cov_mat = np.cov(x, y)
if bary:
cov_mat = cov_mat / len(x)
# Eigendecomposition
eigenvalues, eigenvectors = np.linalg.eigh(cov_mat)
# Scaling factor from chi-squared distribution
scale = np.sqrt(chi2.ppf(level, 2))
# Generate ellipse points
theta = np.linspace(0, 2 * np.pi, npoint)
unit_circle = np.column_stack([np.cos(theta), np.sin(theta)])
# Transform unit circle to ellipse
transform = eigenvectors @ np.diag(np.sqrt(eigenvalues))
ellipse = unit_circle @ transform.T * scale
ellipse_x = ellipse[:, 0] + center_x
ellipse_y = ellipse[:, 1] + center_y
return pd.DataFrame(
{
"x": ellipse_x,
"y": ellipse_y,
}
)