| Title: | Hotelling's T² Ellipses and Outlier Detection for 'ggplot2' |
|---|---|
| Description: | Calculate Hotelling's T² ellipses and detect multivariate outliers both for base R plots and 'ggplot2' plots. Optionally, uses robust covariance estimation to reduce the influence of outliers on the ellipses. Also included: bagplots, kernel density plots and outlier diagnostic plots. |
| Authors: | January Weiner [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-1438-7819>) |
| Maintainer: | January Weiner <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.1 |
| Built: | 2026-10-12 10:03:02 UTC |
| Source: | https://github.com/january3/gghotelling |
Automatic plotting of PCA objects
## S3 method for class 'prcomp' autoplot(object, dims = c(1, 2), biplot = FALSE, group = NULL, ...) ## S3 method for class 'prcomp' autolayer( object, dims = c(1, 2), group = NULL, labels = NULL, type = c("t2data", "t2mean", "c2data"), outliers = TRUE, level = 0.95, ... )## S3 method for class 'prcomp' autoplot(object, dims = c(1, 2), biplot = FALSE, group = NULL, ...) ## S3 method for class 'prcomp' autolayer( object, dims = c(1, 2), group = NULL, labels = NULL, type = c("t2data", "t2mean", "c2data"), outliers = TRUE, level = 0.95, ... )
object |
An object of prcomp class |
dims |
Dimensions to plot |
biplot |
whether to show the loadings as well as the scores |
group |
Groups of the data to be shown on the plot |
... |
ignored |
labels |
optionally, a vector of labels for showing the outliers. If NULL, the outliers will be identified by row number. |
type |
The type of the coverage / confidence area shown by
|
outliers |
if TRUE, label the outliers. |
level |
Either coverage probability (for type = "t2data" and "c2data") or confidence level (for type = "t2mean"). |
The functions autoplot.prcomp and autolayer.prcomp are for
automatic plotting of prcomp objects, similar to
ggfortify::autoplot.prcomp.
Note, however, that the group parameter is a vector of the same length
as the number of rows in the PCA object, rather than a column name in
the data frame.
A ggplot2 object
pca <- prcomp(iris[,1:4], scale.=TRUE) library(ggplot2) autoplot(pca, group = iris$Species) + autolayer(pca, group = iris$Species) # show the 90% confidence area for the group means autoplot(pca, group = iris$Species) + autolayer(pca, group = iris$Species, type="t2mean", level = 0.90, outliers = FALSE)pca <- prcomp(iris[,1:4], scale.=TRUE) library(ggplot2) autoplot(pca, group = iris$Species) + autolayer(pca, group = iris$Species) # show the 90% confidence area for the group means autoplot(pca, group = iris$Species) + autolayer(pca, group = iris$Species, type="t2mean", level = 0.90, outliers = FALSE)
Draw a convex hull around a group of points
geom_hull( mapping = NULL, data = NULL, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )geom_hull( mapping = NULL, data = NULL, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )
mapping |
Set of aesthetic mappings created by |
data |
The data to be displayed in this layer. There are three options: If A A |
position |
A position adjustment to use on the data for this layer. This
can be used in various ways, including to prevent overplotting and
improving the display. The
|
... |
Additional parameters passed to underlying |
na.rm |
Logical. Should missing values be removed? Default is FALSE. |
show.legend |
logical. Should this layer be included in the legends?
|
inherit.aes |
If |
A ggplot2 geom layer that can be added to a ggplot object.
# nothing library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width, color=Species)) + geom_hull(mapping = aes(fill = Species), alpha=.1) + geom_point()# nothing library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width, color=Species)) + geom_hull(mapping = aes(fill = Species), alpha=.1) + geom_point()
Hotelling ellipses for ggplot2
geom_hotelling( mapping = NULL, data = NULL, position = "identity", ..., level = 0.95, type = "t2data", robust = FALSE, npoints = 100, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )geom_hotelling( mapping = NULL, data = NULL, position = "identity", ..., level = 0.95, type = "t2data", robust = FALSE, npoints = 100, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )
mapping |
Set of aesthetic mappings created by |
data |
The data to be displayed in this layer. There are three options: If A A |
position |
A position adjustment to use on the data for this layer. This
can be used in various ways, including to prevent overplotting and
improving the display. The
|
... |
Additional parameters passed to underlying |
level |
Either coverage probability (for type = "t2data" or "c2data") or confidence level (for type = "t2mean"). |
type |
t2data - Hotelling T2 data ellipse; t2mean - Hotelling confidence interval for the mean; c2data - normal data ellipse (using chi squared distribution). |
robust |
If TRUE, then robust estimates of mean and covariance are used |
npoints |
Number of points to estimate |
na.rm |
Logical. Should missing values be removed? Default is FALSE. |
show.legend |
logical. Should this layer be included in the legends?
|
inherit.aes |
If |
This geom adds data or confidence ellipses to the plot. See
hotelling_ellipse() documentation for more information.
A ggplot2 geom layer that can be added to a ggplot object.
pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) library(ggplot2) ggplot(df, aes(PC1, PC2)) + geom_hotelling(level=.99) + geom_point() ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling() + geom_point() ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling(alpha=0.1, aes(fill = Species)) + geom_point() # compare the robust and regular approaches: ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling() + geom_hotelling(robust = TRUE, linetype = "dashed") + geom_point()pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) library(ggplot2) ggplot(df, aes(PC1, PC2)) + geom_hotelling(level=.99) + geom_point() ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling() + geom_point() ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling(alpha=0.1, aes(fill = Species)) + geom_point() # compare the robust and regular approaches: ggplot(df, aes(PC1, PC2, color=Species)) + geom_hotelling() + geom_hotelling(robust = TRUE, linetype = "dashed") + geom_point()
Calculate Hotelling or data ellipse around some points
hotelling_ellipse( x, level = 0.95, npoints = 100, type = c("t2data", "t2mean", "c2data"), robust = FALSE )hotelling_ellipse( x, level = 0.95, npoints = 100, type = c("t2data", "t2mean", "c2data"), robust = FALSE )
x |
A two-column matrix or data frame like object |
level |
Either coverage probability (for type = "t2data" or "c2data") or confidence level (for type = "t2mean"). |
npoints |
Number of points to estimate |
type |
t2data - Hotelling T2 data ellipse; t2mean - Hotelling confidence interval for the mean; c2data - normal data ellipse (using chi squared distribution). |
robust |
If TRUE, then robust estimates of mean and covariance are used |
Calculate the T2 Hotelling ellipse or a data ellipse for the given coverage probability. There are three types of ellipses which can be plotted:
T2 Hotelling data ellipses, showing data coverage (like the
type="t" version of stat_ellipse)
normal multivariate distribution ellipses (like the type="norm"
version of the stat_ellipse) which use Mahalanobis distance and
chi-squared statistic
T2 Hotelling confidence ellipses of the group means.
The latter (for group means) correspond to the confidence interval for the mean in the univariate world, so the ellipses are very small (depending on the number of points).
The function can also use a robust estimator of location and scatter
using the covMcd function, which uses the
Maximum Covariance Determinant (MCD) estimator. Note that while this
results in ellipses which are more resistent to outliers, the
interpretation slightly changes, as the T2 statistic used is only an
approximation in this case. In other words, use it for visualisation and
QC, but not for statistical testing.
A two-column matrix or data frame with npoints rows, containing the coordinates of the ellipse. The columns are named "x" and "y".
outliers() for calculating per-point based statistic
and geom_hotelling() for plotting of the ellipse with
ggplot
df <- iris[ iris$Species == "setosa", 1:2 ] eli <- hotelling_ellipse(df) plot(df[,1], df[,2]) lines(eli)df <- iris[ iris$Species == "setosa", 1:2 ] eli <- hotelling_ellipse(df) plot(df[,1], df[,2]) lines(eli)
Calculate the T2/D2 statistic or Mahalanobis distance for individual points. For each point, it calculates the squared Mahalanobis distance from the center of the data, and compares it to the critical T2 or chi-squared value for the given coverage probability.
outliers( x, level = 0.95, robust = FALSE, type = c("t2data", "t2mean", "c2data") ) plot_outliers( x, level = 0.95, robust = FALSE, type = c("t2data", "t2mean", "c2data"), labels = NULL )outliers( x, level = 0.95, robust = FALSE, type = c("t2data", "t2mean", "c2data") ) plot_outliers( x, level = 0.95, robust = FALSE, type = c("t2data", "t2mean", "c2data"), labels = NULL )
x |
A matrix or data frame with two columns |
level |
Either coverage probability (for type = "t2data" or "c2data") or confidence level (for type = "t2mean"). |
robust |
If TRUE, then robust estimates of mean and covariance are used |
type |
what type of statistic should be calculated; can be t2data (for data coverage), t2mean (for difference from a mean) or "c2data" (for coverage calculated with the chi squared statistic) |
labels |
Optional labels to use on the plot instead of rownames |
The function can use a robust estimator of location and scatter
using the covMcd function, which uses the
Maximum Covariance Determinant (MCD) estimator. Note that while this
results in ellipses which are more resistant to outliers, the
interpretation slightly changes, as the T2 statistic used is only an
approximation in this case. In other words, use it for visualisation and
QC, but not for statistical testing.
A data frame with one row per point including the columns d2 (squared mahalanobis distance or robust squared distance), t2crit (critical T squared value for the given level), c2crit (critical X squared value for the given level) and is_outlier (logical, whether d2 > t2crit or d2 > c2crit, depending on type).
hotelling_ellipse for more information on the
differences between t2data, t2mean and c2data modes.
library(ggplot2) pca <- prcomp(iris[, 1:4], scale.=TRUE) pca_df <- cbind(iris, pca$x) outlier_stats <- outliers(pca_df[ , c("PC1", "PC2")], level = 0.95) # use plot_outliers() to directly plot them plot_outliers(pca_df[ , c("PC1", "PC2")], level = 0.95)library(ggplot2) pca <- prcomp(iris[, 1:4], scale.=TRUE) pca_df <- cbind(iris, pca$x) outlier_stats <- outliers(pca_df[ , c("PC1", "PC2")], level = 0.95) # use plot_outliers() to directly plot them plot_outliers(pca_df[ , c("PC1", "PC2")], level = 0.95)
Bag plots for ggplot2
geom_bag( mapping = NULL, data = NULL, position = "identity", ..., what = c("bag", "loop"), type = c("hdepth", "projdepth", "sprojdepth", "dprojdepth"), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )geom_bag( mapping = NULL, data = NULL, position = "identity", ..., what = c("bag", "loop"), type = c("hdepth", "projdepth", "sprojdepth", "dprojdepth"), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )
mapping |
Set of aesthetic mappings created by |
data |
The data to be displayed in this layer. There are three options: If A A |
position |
A position adjustment to use on the data for this layer. This
can be used in various ways, including to prevent overplotting and
improving the display. The
|
... |
Additional parameters passed to underlying |
what |
What to plot. Either "bag" (the inner bag region) or "loop" (the outer loop). |
type |
Determines the depth function used to construct the bagplot:
|
na.rm |
Logical. Should missing values be removed? Default is FALSE. |
show.legend |
logical. Should this layer be included in the legends?
|
inherit.aes |
If |
Bag plots are 2-dimensional generalizations of box plots. This geom adds a bag region (which contains 50% of the data points, just like the "box" in "box plots") to the plot, plus the "loop" which can be used to identify potential outliers.
geom_bag() is just a wrapper around the mrfDepth::compBagplot()
function which actually does the calculations, and the arguments
are passed on to that function.
The geom either plots the inner "bag" or the outer "loop", but not both. If you need both, you can add two geom_bag layers to your ggplot object.
A ggplot2 geom layer that can be added to a ggplot object.
library(ggplot2) ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length, color = Species)) + geom_point() + geom_bag(aes(fill = Species), alpha = .1, what = "loop") + geom_bag(aes(fill = Species), alpha = .3)library(ggplot2) ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length, color = Species)) + geom_point() + geom_bag(aes(fill = Species), alpha = .1, what = "loop") + geom_bag(aes(fill = Species), alpha = .3)
Kernel density estimate contours for ggplot2
geom_kde( mapping = NULL, data = NULL, position = "identity", ..., coverage = 0.95, n = 100, show.legend = NA, inherit.aes = TRUE )geom_kde( mapping = NULL, data = NULL, position = "identity", ..., coverage = 0.95, n = 100, show.legend = NA, inherit.aes = TRUE )
mapping |
Set of aesthetic mappings created by |
data |
The data to be displayed in this layer. There are three options: If A A |
position |
A position adjustment to use on the data for this layer. This
can be used in various ways, including to prevent overplotting and
improving the display. The
|
... |
Additional parameters passed to underlying |
coverage |
What fraction of the points should be enclosed by the contour |
n |
Number of grid points in each direction (passed to
|
show.legend |
logical. Should this layer be included in the legends?
|
inherit.aes |
If |
This function uses the kde2d function to estimate density along
a grid, and then plots a contour for a selected coverage.
A ggplot2 geom layer that can be added to a ggplot object.
geom_hull() for simple convex contours and geom_hotelling()
for Hotelling T2 ellipses.
library(ggplot2) # following shows a comparison # between a convex hull and KDE contour df <- data.frame(x=rnorm(500) + 5) df$y <- df$x^5 + rnorm(500)*500 ggplot(df, aes(x=x, y=y)) + geom_point()+ geom_hull(color = "grey") + geom_kde(color="red", linewidth=1) # we can also achieve similar effects to geom_contour pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) # following is similar to geom_contour pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) p <- ggplot(df, aes(x = PC1, y = PC2, color=Species)) for(i in seq(.05, .95, length.out = 10)) { p <- p + geom_kde(aes(fill = Species), alpha =.05, coverage = i, n = 100) } p + geom_point()library(ggplot2) # following shows a comparison # between a convex hull and KDE contour df <- data.frame(x=rnorm(500) + 5) df$y <- df$x^5 + rnorm(500)*500 ggplot(df, aes(x=x, y=y)) + geom_point()+ geom_hull(color = "grey") + geom_kde(color="red", linewidth=1) # we can also achieve similar effects to geom_contour pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) # following is similar to geom_contour pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) p <- ggplot(df, aes(x = PC1, y = PC2, color=Species)) for(i in seq(.05, .95, length.out = 10)) { p <- p + geom_kde(aes(fill = Species), alpha =.05, coverage = i, n = 100) } p + geom_point()
Calculate per-point Hotelling statistic for use in ggplot
stat_outliers( mapping = NULL, data = NULL, geom = "point", position = "identity", ..., type = c("t2data", "c2data"), level = 0.95, outlier_only = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )stat_outliers( mapping = NULL, data = NULL, geom = "point", position = "identity", ..., type = c("t2data", "c2data"), level = 0.95, outlier_only = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE )
mapping |
Set of aesthetic mappings created by |
data |
The data to be displayed in this layer. There are three options: If A A |
geom |
The geometric object to use to display the data for this layer.
When using a
|
position |
A position adjustment to use on the data for this layer. This
can be used in various ways, including to prevent overplotting and
improving the display. The
|
... |
Additional parameters passed to underlying |
type |
t2data - Hotelling T2 data ellipse; t2mean - Hotelling confidence interval for the mean; c2data - normal data ellipse (using chi squared distribution). |
level |
Either coverage probability (for type = "t2data" or "c2data") or confidence level (for type = "t2mean"). |
outlier_only |
Only return the statistic for outliers |
na.rm |
Logical. Should missing values be removed? Default is FALSE. |
show.legend |
logical. Should this layer be included in the legends?
|
inherit.aes |
If |
This calculates the Hotelling statistic for each point in the plot,
group-wise. This allows to use the statistics is_outlier, d2 (the
squared Mahalanobis distance), c2crit (critical Chi-squared value for
the specified level) and t2crit (critical Hotelling T2 value
for the squared Mahalanobis distance) to be used as graphical
parameters, e.g. for coloring the points (see Examples below) using the
ggplot2::after_stat() function.
The is_outlier is simply either d2 > t2crit (if type="t2data" or
type="t2mean") or d2 > c2crit (if type="c2data").
The type argument chooses between the regular Hotelling statistic
(with type="t2data") or the Chi-squared statistic (with
type="c2data"). The Hotelling statistic for the mean is not allowed as
it makes no sense in the context of the outliers.
The function is a wrapper around the outliers() function, which does
the actual calculations, and the parameters passed (level, type, robust)
are passed on to that function. For example, to calculate robust
statistic, use stat_outliers(robust=TRUE).
A stat layer that can be added to a ggplot object.
pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) library(ggplot2) ggplot(df, aes(PC1, PC2, group=Species)) + geom_hotelling(alpha=0.1, aes(fill = Species)) + scale_color_manual(values=c("TRUE"="red", "FALSE"="grey")) + stat_outliers(aes(color = after_stat(is_outlier))) ggplot(df, aes(PC1, PC2, group=Species)) + geom_hotelling(alpha=0.1, level = .75, aes(fill = Species)) + stat_outliers(level = .75, size=2, aes(shape = Species, color = after_stat(d2))) # label the outliers # note that you need to add the label aesthetics for the label geom to # work ggplot(df, aes(PC1, PC2, group=Species, label=rownames(df))) + geom_hotelling(level = 0.75, alpha=0.1, aes(fill = Species)) + geom_point(aes(color = Species)) + stat_outliers(level = .75, geom="label", outlier_only = TRUE)pca <- prcomp(iris[, 1:4], scale.=TRUE) df <- cbind(iris, pca$x) library(ggplot2) ggplot(df, aes(PC1, PC2, group=Species)) + geom_hotelling(alpha=0.1, aes(fill = Species)) + scale_color_manual(values=c("TRUE"="red", "FALSE"="grey")) + stat_outliers(aes(color = after_stat(is_outlier))) ggplot(df, aes(PC1, PC2, group=Species)) + geom_hotelling(alpha=0.1, level = .75, aes(fill = Species)) + stat_outliers(level = .75, size=2, aes(shape = Species, color = after_stat(d2))) # label the outliers # note that you need to add the label aesthetics for the label geom to # work ggplot(df, aes(PC1, PC2, group=Species, label=rownames(df))) + geom_hotelling(level = 0.75, alpha=0.1, aes(fill = Species)) + geom_point(aes(color = Species)) + stat_outliers(level = .75, geom="label", outlier_only = TRUE)