Skip to content

Commit ccf58b9

Browse files
committed
fixed up indentation, included reference to car package.
1 parent 3a58a17 commit ccf58b9

File tree

1 file changed

+68
-73
lines changed

1 file changed

+68
-73
lines changed

R/stat-ellipse.R

Lines changed: 68 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,99 @@
1-
#' Calculate Data Ellipses
2-
#'
1+
#' Plot data ellipses.
2+
#'
33
#' @param level The confidence level at which to draw an ellipse (default is 0.95),
4-
#' or, if \code{type="euclid"}, the radius of the circle to be drawn.
5-
#' @param type The type of ellipse.
6-
#' The default \code{"t"} assumes a multivariate t-distribution, and
7-
#' \code{"norm"} assumes a multivariate normal distribution.
8-
#' \code{"euclid"} draws a circle with the radius equal to \code{level},
9-
#' representing the euclidian distance from the center.
10-
#' This ellipse probably won't appear circular unless \code{coord_fixed()} is applied.
11-
#' @param segments The number of segments to be used in drawing the ellipse.
4+
#' or, if \code{type="euclid"}, the radius of the circle to be drawn.
5+
#' @param type The type of ellipse.
6+
#' The default \code{"t"} assumes a multivariate t-distribution, and
7+
#' \code{"norm"} assumes a multivariate normal distribution.
8+
#' \code{"euclid"} draws a circle with the radius equal to \code{level},
9+
#' representing the euclidian distance from the center.
10+
#' This ellipse probably won't appear circular unless \code{coord_fixed()} is applied.
11+
#' @param segments The number of segments to be used in drawing the ellipse.
1212
#' @param na.rm If \code{FALSE} (the default), removes missing values with
13-
#' a warning. If \code{TRUE} silently removes missing values.
13+
#' a warning. If \code{TRUE} silently removes missing values.
1414
#' @inheritParams stat_identity
15-
#'
16-
#' @details The code for calculating the ellipse is largely borrowed from car::ellipse.
17-
#'
15+
#'
16+
#' @details The method for calculating the ellipses has been modified from car::ellipse (Fox and Weisberg, 2011)
17+
#'
18+
#' @references
19+
#' John Fox and Sanford Weisberg (2011). An {R} Companion to Applied Regression, Second Edition. Thousand Oaks CA: Sage. URL: http://socserv.socsci.mcmaster.ca/jfox/Books/Companion
20+
#'
1821
#' @export
1922
#' @importFrom MASS cov.trob
20-
#'
23+
#'
2124
#' @examples
22-
#' \donttest{
2325
#' ggplot(faithful, aes(waiting, eruptions))+
2426
#' geom_point()+
2527
#' stat_ellipse()
26-
#'
28+
#'
2729
#' ggplot(faithful, aes(waiting, eruptions, color = eruptions > 3))+
2830
#' geom_point()+
2931
#' stat_ellipse()
30-
#'
32+
#'
3133
#' ggplot(faithful, aes(waiting, eruptions, color = eruptions > 3))+
3234
#' geom_point()+
3335
#' stat_ellipse(type = "norm", linetype = 2)+
3436
#' stat_ellipse(type = "t")
35-
#'
37+
#'
3638
#' ggplot(faithful, aes(waiting, eruptions, color = eruptions > 3))+
3739
#' geom_point()+
3840
#' stat_ellipse(type = "norm", linetype = 2)+
3941
#' stat_ellipse(type = "euclid", level = 3)+
4042
#' coord_fixed()
41-
#'
43+
#'
4244
#' ggplot(faithful, aes(waiting, eruptions, color = eruptions > 3))+
4345
#' stat_ellipse(geom = "polygon")
44-
#' }
4546

46-
stat_ellipse <- function(mapping = NULL, data = NULL, geom = "path", position = "identity",
47-
type = "t", level = 0.95, segments = 51, na.rm = FALSE, ...) {
48-
StatEllipse$new(mapping = mapping, data = data, geom = geom, position = position,
49-
type = type, level = level, segments = segments, na.rm = na.rm, ...)
47+
stat_ellipse <- function(mapping = NULL, data = NULL, geom = "path", position = "identity", type = "t", level = 0.95, segments = 51, na.rm = FALSE, ...) {
48+
StatEllipse$new(mapping = mapping, data = data, geom = geom, position = position, type = type, level = level, segments = segments, na.rm = na.rm, ...)
5049
}
5150

5251

53-
StatEllipse <- proto(Stat,
54-
{
55-
objname <- "ellipse"
56-
57-
required_aes <- c("x", "y")
58-
default_geom <- function(.) GeomPath
52+
StatEllipse <- proto(Stat, {
53+
objname <- "ellipse"
5954

60-
calculate_groups <- function(., data, scales, ...){
61-
.super$calculate_groups(., data, scales,...)
62-
}
63-
calculate <- function(., data, scales, type = "t", level = 0.95, segments = 51, na.rm = FALSE, ...){
64-
data <- remove_missing(data, na.rm, vars = c("x","y"),
65-
name = "stat_ellipse", finite = TRUE)
66-
67-
dfn <- 2
68-
dfd <- length(data$x) - 1
69-
70-
if (!type %in% c("t", "norm", "euclid")){
71-
message("Unrecognized ellipse type")
72-
ellipse <- rbind(as.numeric(c(NA, NA)))
73-
} else if (dfd < 3){
74-
message("Too few points to calculate an ellipse")
75-
ellipse <- rbind(as.numeric(c(NA, NA)))
55+
required_aes <- c("x", "y")
56+
default_geom <- function(.) GeomPath
57+
58+
calculate_groups <- function(., data, scales, ...){
59+
.super$calculate_groups(., data, scales,...)
60+
}
61+
calculate <- function(., data, scales, type = "t", level = 0.95, segments = 51, na.rm = FALSE, ...){
62+
data <- remove_missing(data, na.rm, vars = c("x","y"), name = "stat_ellipse", finite = TRUE)
63+
64+
dfn <- 2
65+
dfd <- length(data$x) - 1
66+
67+
if (!type %in% c("t", "norm", "euclid")){
68+
message("Unrecognized ellipse type")
69+
ellipse <- rbind(as.numeric(c(NA, NA)))
70+
} else if (dfd < 3){
71+
message("Too few points to calculate an ellipse")
72+
ellipse <- rbind(as.numeric(c(NA, NA)))
73+
} else {
74+
if (type == "t"){
75+
v <- cov.trob(cbind(data$x, data$y))
76+
} else if (type == "norm"){
77+
v <- cov.wt(cbind(data$x, data$y))
78+
} else if (type == "euclid"){
79+
v <- cov.wt(cbind(data$x, data$y))
80+
v$cov <- diag(rep(min(diag(v$cov)), 2))
81+
}
82+
shape <- v$cov
83+
center <- v$center
84+
chol_decomp <- chol(shape)
85+
if (type == "euclid"){
86+
radius <- level/max(chol_decomp)
7687
} else {
77-
if (type == "t"){
78-
v <- cov.trob(cbind(data$x, data$y))
79-
} else if (type == "norm"){
80-
v <- cov.wt(cbind(data$x, data$y))
81-
} else if (type == "euclid"){
82-
v <- cov.wt(cbind(data$x, data$y))
83-
v$cov <- diag(rep(min(diag(v$cov)), 2))
84-
}
85-
shape <- v$cov
86-
center <- v$center
87-
chol_decomp <- chol(shape)
88-
if (type == "euclid"){
89-
radius <- level/max(chol_decomp)
90-
} else {
91-
radius <- sqrt(dfn * qf(level, dfn, dfd))
92-
}
93-
angles <- (0:segments) * 2 * pi/segments
94-
unit.circle <- cbind(cos(angles), sin(angles))
95-
ellipse <- t(center + radius * t(unit.circle %*% chol_decomp))
88+
radius <- sqrt(dfn * qf(level, dfn, dfd))
9689
}
97-
98-
ellipse <- as.data.frame(ellipse)
99-
colnames(ellipse) <- c("x", "y")
100-
return(ellipse)
101-
}
102-
}
103-
)
90+
angles <- (0:segments) * 2 * pi/segments
91+
unit.circle <- cbind(cos(angles), sin(angles))
92+
ellipse <- t(center + radius * t(unit.circle %*% chol_decomp))
93+
}
10494

95+
ellipse <- as.data.frame(ellipse)
96+
colnames(ellipse) <- c("x", "y")
97+
return(ellipse)
98+
}
99+
})

0 commit comments

Comments
 (0)