Skip to content

Split geom_hline into geom and annotation (fixes #426) #482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ Collate:
'stat-sum.r'
'stat-summary.r'
'stat-unique.r'
'stat-vline.r'
'stat-ydensity.r'
'summary.r'
'templates.r'
Expand Down
3 changes: 0 additions & 3 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ export(scale_y_log10)
export(scale_y_reverse)
export(scale_y_sqrt)
export(should_stop)
export(stat_abline)
export(stat_bin)
export(stat_bin2d)
export(stat_bindot)
Expand All @@ -181,7 +180,6 @@ export(stat_contour)
export(stat_density)
export(stat_density2d)
export(stat_function)
export(stat_hline)
export(stat_identity)
export(stat_qq)
export(stat_quantile)
Expand All @@ -192,7 +190,6 @@ export(stat_summary_hex)
export(stat_summary)
export(stat_summary2d)
export(stat_unique)
export(stat_vline)
export(stat_ydensity)
export(theme_blank)
export(theme_bw)
Expand Down
9 changes: 6 additions & 3 deletions R/annotation.r
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#' @param ymin ymin position
#' @param xmax xmax position
#' @param ymax ymax position
#' @param xintercept xintercept position
#' @param yintercept yintercept position
#' @param ... other arguments passed to geom as parameters
#' @export
#' @examples
Expand All @@ -27,11 +29,12 @@
#' p + annotate("segment", x = 2.5, xend = 4, y = 15, yend = 25, colour = "blue")
#' p + annotate("pointrange", x = 3.5, y = 20, ymin = 12, ymax = 28,
#' colour = "red", size = 1.5)
annotate <- function(geom, x = NULL, y = NULL, xmin = NULL, xmax = NULL, ymin = NULL, ymax = NULL, ...) {
annotate <- function(geom, x = NULL, y = NULL, xmin = NULL, xmax = NULL,
xintercept = NULL, ymin = NULL, ymax = NULL, yintercept = NULL, ...) {

layer_data <- compact(list(
x = x, xmin = xmin, xmax = xmax,
y = y, ymin = ymin, ymax = ymax
x = x, xmin = xmin, xmax = xmax, xintercept = xintercept,
y = y, ymin = ymin, ymax = ymax, yintercept = yintercept
))

layer(
Expand Down
77 changes: 13 additions & 64 deletions R/geom-abline.r
Original file line number Diff line number Diff line change
@@ -1,81 +1,30 @@
#' Line specified by slope and intercept.
#'
#' The abline geom adds a line with specified slope and intercept to the
#' plot.
#'
#' With its siblings \code{geom_hline} and \code{geom_vline}, it's useful for
#' annotating plots. You can supply the parameters for geom_abline,
#' intercept and slope, in two ways: either explicitly as fixed values, or
#' in a data frame. If you specify the fixed values
#' (\code{geom_abline(intercept=0, slope=1)}) then the line will be the same
#' in all panels. If the intercept and slope are stored in the data, then
#' they can vary from panel to panel. See the examples for more ideas.
#'
#' @seealso \code{\link{stat_smooth}} to add lines derived from the data,
#' \code{\link{geom_hline}} for horizontal lines,
#' \code{\link{geom_vline}} for vertical lines
#' \code{\link{geom_segment}}
#' @param show_guide should a legend be drawn? (defaults to \code{FALSE})
#' @inheritParams geom_point
#' @rdname geom_hline
#' @export
#' @examples
#' p <- qplot(wt, mpg, data = mtcars)
#'
#' # Fixed slopes and intercepts
#' p + geom_abline() # Can't see it - outside the range of the data
#' p + geom_abline(intercept = 20)
#'
#' # Calculate slope and intercept of line of best fit
#' coef(lm(mpg ~ wt, data = mtcars))
#' p + geom_abline(intercept = 37, slope = -5)
#' p + geom_abline(intercept = 10, colour = "red", size = 2)
#'
#' # See ?stat_smooth for fitting smooth models to data
#' p + stat_smooth(method="lm", se=FALSE)
#'
#' # Slopes and intercepts as data
#' p <- ggplot(mtcars, aes(x = wt, y=mpg), . ~ cyl) + geom_point()
#' df <- data.frame(a=rnorm(10, 25), b=rnorm(10, 0))
#' p + geom_abline(aes(intercept=a, slope=b), data=df)
#'
#' # Slopes and intercepts from linear model
#' library(plyr)
#' coefs <- ddply(mtcars, .(cyl), function(df) {
#' m <- lm(mpg ~ wt, data=df)
#' data.frame(a = coef(m)[1], b = coef(m)[2])
#' })
#' str(coefs)
#' p + geom_abline(data=coefs, aes(intercept=a, slope=b))
#'
#' # It's actually a bit easier to do this with stat_smooth
#' p + geom_smooth(aes(group=cyl), method="lm")
#' p + geom_smooth(aes(group=cyl), method="lm", fullrange=TRUE)
#'
#' # With coordinate transforms
#' p + geom_abline(intercept = 37, slope = -5) + coord_flip()
#' p + geom_abline(intercept = 37, slope = -5) + coord_polar()
geom_abline <- function (mapping = NULL, data = NULL, stat = "abline", position = "identity", show_guide = FALSE, ...) {
geom_abline <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity", show_guide = FALSE, ...) {
GeomAbline$new(mapping = mapping, data = data, stat = stat, position = position, show_guide = show_guide, ...)
}

GeomAbline <- proto(Geom, {
objname <- "abline"

new <- function(., mapping = NULL, ...) {
mapping <- compact(defaults(mapping, aes(group = 1)))
class(mapping) <- "uneval"
.super$new(., ..., mapping = mapping, inherit.aes = FALSE)
}

draw <- function(., data, scales, coordinates, ...) {
draw <- function(., data, scales, coordinates, intercept = NULL, slope = NULL, ...) {
ranges <- coord_range(coordinates, scales)

if (!is.null(intercept))
data$intercept <- intercept
if (!is.null(slope))
data$slope <- slope

data$x <- ranges$x[1]
data$xend <- ranges$x[2]
data$y <- ranges$x[1] * data$slope + data$intercept
data$yend <- ranges$x[2] * data$slope + data$intercept

GeomSegment$draw(unique(data), scales, coordinates)
if(nrow(data) > 1 && nrow(unique(data)) == 1)
message("There are ", nrow(data), " identical ablines. If you want just one line, use annontate(\"abline\") instead of geom_abline().")


GeomSegment$draw(data, scales, coordinates)
}

icon <- function(.) linesGrob(c(0, 1), c(0.2, 0.8))
Expand Down
85 changes: 50 additions & 35 deletions R/geom-hline.r
Original file line number Diff line number Diff line change
@@ -1,63 +1,78 @@
#' Horizontal line.
#' Horizontal, vertical, and sloped lines.
#'
#' This geom allows you to annotate the plot with horizontal lines (see
#' \code{\link{geom_vline}} and \code{\link{geom_abline}} for other types of
#' lines).
#' You can either add lines at specified positions with
#' \code{annotate(geom="hline")} (or \code{vline} or \code{abline},
#' or you can use variables from a data frame to specify the positions,
#' using \code{geom_hline}.
#'
#' There are two ways to use it. You can either specify the intercept of
#' the line in the call to the geom, in which case the line will be in the
#' same position in every panel. Alternatively, you can supply a different
#' intercept for each panel using a data.frame. See the examples for the
#' differences
#' The \code{annotate} form is useful for adding individual lines to a plot,
#' while the \code{geom} form is useful for drawing lines directly from the
#' data
#'
#' For \code{geom_hline}, specify the y-intercept with \code{yintercept}.
#'
#' For \code{geom_vline}, specify the x-intercept with \code{xintercept}.
#'
#' For \code{geom_abline}, specify the y-intercept with \code{intercept}
#' and the slope with \code{slope}.
#'
#' @seealso \code{\link{geom_vline}} for vertical lines,
#' \code{\link{geom_abline}} for lines defined by a slope and intercept,
#' \code{\link{geom_segment}} for a more general approach
#' @export
#' @inheritParams geom_point
#' @param show_guide should a legend be drawn? (defaults to \code{FALSE})
#' @inheritParams geom_point
#' @seealso
#' \code{\link{annotate}} for adding annotations.
#' @export
#' @examples
#' p <- ggplot(mtcars, aes(x = wt, y=mpg)) + geom_point()
#' p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
#'
#' p + geom_hline(aes(yintercept=mpg))
#' p + geom_hline(yintercept=20)
#' p + geom_hline(yintercept=seq(10, 30, by=5))
#' # Individual lines
#' p + annotate("hline", yintercept = 20)
#' p + annotate("vline", xintercept = 5, colour = "blue")
#' p + annotate("abline", intercept = 20, slope = 1)
#'
#' # Using vectors to specify lines
#' p + annotate("hline", yintercept = seq(10, 30, by = 5))
#' p + annotate("vline", xintercept = 1:5, colour="darkgreen", linetype = "longdash")
#' p + annotate("abline", intercept = c(17, 22), slope = c(0.5, 1))
#'
#' # Map a variable to line properties
#' p + geom_hline(aes(yintercept = mpg))
#' p + geom_vline(aes(xintercept = wt), colour = "blue")
#' p + geom_abline(aes(intercept = mpg, slope = wt))
#'
#' # Calculate slope and intercept of line of best fit
#' coef(lm(mpg ~ wt, data = mtcars))
#' p + annotate_abline(intercept = 37, slope = -5)
#'
#' # With coordinate transforms
#' p + geom_hline(aes(yintercept=mpg)) + coord_equal()
#' p + geom_hline(aes(yintercept=mpg)) + coord_flip()
#' p + geom_hline(aes(yintercept=mpg)) + coord_polar()
#' p + geom_hline(aes(yintercept = mpg)) + coord_equal()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we eliminate some of these given that we now have the visual tests?

#' p + geom_hline(aes(yintercept = mpg)) + coord_flip()
#' p + geom_hline(aes(yintercept = mpg)) + coord_polar()
#'
#' # To display different lines in different facets, you need to
#' # create a data frame.
#' p <- qplot(mpg, wt, data=mtcars, facets = vs ~ am)
#'
#' hline.data <- data.frame(z = 1:4, vs = c(0,0,1,1), am = c(0,1,0,1))
#' p + geom_hline(aes(yintercept = z), hline.data)
geom_hline <- function (mapping = NULL, data = NULL, stat = "hline", position = "identity", show_guide = FALSE, ...) {
#' p + geom_hline(aes(yintercept = z), hline.data, inherit.aes = FALSE)
geom_hline <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity", show_guide = FALSE, ...) {
GeomHline$new(mapping = mapping, data = data, stat = stat, position = position, show_guide = show_guide, ...)
}

GeomHline <- proto(Geom, {
objname <- "hline"

new <- function(., data = NULL, mapping = NULL, yintercept = NULL, ...) {
if (is.numeric(yintercept)) {
data <- data.frame(yintercept = yintercept)
yintercept <- NULL
mapping <- aes_all(names(data))
}
.super$new(., data = data, mapping = mapping, inherit.aes = FALSE,
yintercept = yintercept, ...)
}

draw <- function(., data, scales, coordinates, ...) {
draw <- function(., data, scales, coordinates, yintercept = NULL, ...) {
ranges <- coord_range(coordinates, scales)

data$y <- yintercept %||% data$yintercept
data$yend <- data$y
data$x <- ranges$x[1]
data$xend <- ranges$x[2]

GeomSegment$draw(unique(data), scales, coordinates)
if(nrow(data) > 1 && nrow(unique(data)) == 1)
message(nrow(data), " identical hlines were drawn. If you want just one line, use annontate(\"hline\") instead of geom_hline().")

GeomSegment$draw(data, scales, coordinates)
}

icon <- function(.) linesGrob(c(0, 1), c(0.5, 0.5))
Expand Down
61 changes: 9 additions & 52 deletions R/geom-vline.r
Original file line number Diff line number Diff line change
@@ -1,67 +1,24 @@
#' Line, vertical.
#'
#' This geom allows you to annotate the plot with vertical lines (see
#' \code{\link{geom_hline}} and \code{\link{geom_abline}} for other types of
#' lines.
#'
#' There are two ways to use it. You can either specify the intercept of the
#' line in the call to the geom, in which case the line will be in the same
#' position in every panel. Alternatively, you can supply a different
#' intercept for each panel using a data.frame. See the examples for the
#' differences.
#'
#' @param show_guide should a legend be drawn? (defaults to \code{FALSE})
#' @inheritParams geom_point
#' @seealso
#' \code{\link{geom_hline}} for horizontal lines,
#' \code{\link{geom_abline}} for lines defined by a slope and intercept,
#' \code{\link{geom_segment}} for a more general approach"
#' @rdname geom_hline
#' @export
#' @examples
#' # Fixed lines
#' p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
#' p + geom_vline(xintercept = 5)
#' p + geom_vline(xintercept = 1:5)
#' p + geom_vline(xintercept = 1:5, colour="green", linetype = "longdash")
#' p + geom_vline(aes(xintercept = wt))
#'
#' # With coordinate transforms
#' p + geom_vline(aes(xintercept = wt)) + coord_equal()
#' p + geom_vline(aes(xintercept = wt)) + coord_flip()
#' p + geom_vline(aes(xintercept = wt)) + coord_polar()
#'
#' p2 <- p + aes(colour = factor(cyl))
#' p2 + geom_vline(xintercept = 15)
#'
#' # To display different lines in different facets, you need to
#' # create a data frame.
#' p <- qplot(mpg, wt, data=mtcars, facets = vs ~ am)
#' vline.data <- data.frame(z = c(15, 20, 25, 30), vs = c(0, 0, 1, 1), am = c(0, 1, 0, 1))
#' p + geom_vline(aes(xintercept = z), vline.data)
geom_vline <- function (mapping = NULL, data = NULL, stat = "vline", position = "identity", show_guide = FALSE, ...) {
geom_vline <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity", show_guide = FALSE, ...) {
GeomVline$new(mapping = mapping, data = data, stat = stat, position = position, show_guide = show_guide, ...)
}

GeomVline <- proto(Geom, {
objname <- "vline"

new <- function(., data = NULL, mapping = NULL, xintercept = NULL, ...) {
if (is.numeric(xintercept)) {
data <- data.frame(xintercept = xintercept)
xintercept <- NULL
mapping <- aes_all(names(data))
}
.super$new(., data = data, mapping = mapping, inherit.aes = FALSE,
xintercept = xintercept, ...)
}

draw <- function(., data, scales, coordinates, ...) {
draw <- function(., data, scales, coordinates, xintercept = NULL, ...) {
ranges <- coord_range(coordinates, scales)

data$x <- xintercept %||% data$xintercept
data$xend <- data$x
data$y <- ranges$y[1]
data$yend <- ranges$y[2]

GeomSegment$draw(unique(data), scales, coordinates)
if(nrow(data) > 1 && nrow(unique(data)) == 1)
message(nrow(data), " identical vlines were drawn. If you want just one line, use annontate(\"vline\") instead of geom_vline().")

GeomSegment$draw(data, scales, coordinates)
}


Expand Down
Loading