Skip to content

Implement geom_function(). #3982

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

Merged
merged 6 commits into from
May 2, 2020
Merged
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: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Collate:
'geom-errorbar.r'
'geom-errorbarh.r'
'geom-freqpoly.r'
'geom-function.R'
'geom-hex.r'
'geom-histogram.r'
'geom-hline.r'
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export(GeomDensity2dFilled)
export(GeomDotplot)
export(GeomErrorbar)
export(GeomErrorbarh)
export(GeomFunction)
export(GeomHex)
export(GeomHline)
export(GeomLabel)
Expand Down Expand Up @@ -351,6 +352,7 @@ export(geom_dotplot)
export(geom_errorbar)
export(geom_errorbarh)
export(geom_freqpoly)
export(geom_function)
export(geom_hex)
export(geom_histogram)
export(geom_hline)
Expand Down
9 changes: 5 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
native rasters render significantly faster than arrays (@kent37, #3388)

* Default continuous color scales (i.e., the `options()` `ggplot2.continuous.colour` and `ggplot2.continuous.fill`, which inform the `type` argument of `scale_fill_continuous()` and `scale_colour_continuous()`) now accept a function, which allows more control over these default `continuous_scale()`s (@cpsievert, #3827)

* `stat_function()` now works with transformed y axes, e.g. `scale_y_log10()`
(@clauswilke, #3905).


* A newly added `geom_function()` is now the recommended geom to use in
conjunction with `stat_function()`. In addition, `stat_function()` now
works with transformed y axes, e.g. `scale_y_log10()` (@clauswilke, #3611, #3905).

* A bug was fixed in `stat_contour()` when calculating breaks based on
the `bins` argument (@clauswilke, #3879).

Expand Down
85 changes: 85 additions & 0 deletions R/geom-function.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#' Draw a function as a continuous curve
#'
#' Computes and draws a function as a continuous curve. This makes it easy to
#' superimpose a function on top of an existing plot. The function is called
#' with a grid of evenly spaced values along the x axis, and the results are
#' drawn (by default) with a line.
#'
#' @eval rd_aesthetics("geom", "function")
#' @param data Ignored by `stat_function()`, do not use.
#' @inheritParams layer
#' @inheritParams geom_path
#' @examples
#'
#' # geom_function() is useful for overlaying functions
#' set.seed(1492)
#' ggplot(data.frame(x = rnorm(100)), aes(x)) +
#' geom_density() +
#' geom_function(fun = dnorm, colour = "red")
#'
#' # To plot functions without data, specify range of x-axis
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
#' base + geom_function(fun = dnorm)
#' base + geom_function(fun = dnorm, args = list(mean = 2, sd = .5))
#'
#' # The underlying mechanics evaluate the function at discrete points
#' # and connect the points with lines
#' base + stat_function(fun = dnorm, geom = "point")
#' base + stat_function(fun = dnorm, geom = "point", n = 20)
#' base + geom_function(fun = dnorm, n = 20)
#'
#' # Two functions on the same plot
#' base +
#' geom_function(aes(colour = "normal"), fun = dnorm) +
#' geom_function(aes(colour = "t, df = 1"), fun = dt, args = list(df = 1))
#'
#' # Using a custom anonymous function
#' base + geom_function(fun = function(x) 0.5*exp(-abs(x)))
#' base + geom_function(fun = ~ 0.5*exp(-abs(.x)))
#'
#' # Using a custom named function
#' f <- function(x) 0.5*exp(-abs(x))
#' base + geom_function(fun = f)
#' @export
geom_function <- function(mapping = NULL, data = NULL, stat = "function",
position = "identity", ..., na.rm = FALSE,
show.legend = NA, inherit.aes = TRUE) {
# Warn if supplied data is going to be overwritten
if (!is.null(data) && identical(stat, "function")) {
warn("`data` is not used by stat_function()")
}

layer(
data = data,
mapping = mapping,
stat = stat,
geom = GeomFunction,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(
na.rm = na.rm,
...
)
)
}

#' @rdname ggplot2-ggproto
#' @format NULL
#' @usage NULL
#' @export
#' @include geom-path.r
GeomFunction <- ggproto("GeomFunction", GeomPath,
draw_panel = function(self, data, panel_params, coord, arrow = NULL,
lineend = "butt", linejoin = "round", linemitre = 10,
na.rm = FALSE) {
groups <- unique(data$group)
if (length(groups) > 1) {
warn("Multiple drawing groups in `geom_function()`. Did you use the correct `group`, `colour`, or `fill` aesthetics?")
}

ggproto_parent(GeomPath, self)$draw_panel(
data, panel_params, coord, arrow, lineend, linejoin, linemitre, na.rm
)
}
)
57 changes: 7 additions & 50 deletions R/stat-function.r
Original file line number Diff line number Diff line change
@@ -1,61 +1,21 @@
#' Compute function for each x value
#'
#' This stat makes it easy to superimpose a function on top of an existing plot.
#' The function is called with a grid of evenly spaced values along the x axis,
#' and the results are drawn (by default) with a line.
#'
#'
#' @param fun Function to use. Either 1) an anonymous function in the base or
#' rlang formula syntax (see [rlang::as_function()])
#' or 2) a quoted or character name referencing a function; see examples. Must
#' be vectorised.
#' @param n Number of points to interpolate along
#' @param n Number of points to interpolate along the x axis.
#' @param args List of additional arguments passed on to the function defined by `fun`.
#' @param xlim Optionally, restrict the range of the function to this range.
#' @inheritParams layer
#' @inheritParams geom_point
#' @section Computed variables:
#' `stat_function()` computes the following variables:
#' \describe{
#' \item{x}{x's along a grid}
#' \item{y}{value of function evaluated at corresponding x}
#' \item{x}{x values along a grid}
#' \item{y}{value of the function evaluated at corresponding x}
#' }
#' @seealso [rlang::as_function()]
#' @export
#' @examples
#'
#' # stat_function is useful for overlaying functions
#' set.seed(1492)
#' ggplot(data.frame(x = rnorm(100)), aes(x)) +
#' geom_density() +
#' stat_function(fun = dnorm, colour = "red")
#'
#' # To plot functions without data, specify range of x-axis
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
#' base + stat_function(fun = dnorm)
#' base + stat_function(fun = dnorm, args = list(mean = 2, sd = .5))
#'
#' # The underlying mechanics evaluate the function at discrete points
#' # and connect the points with lines
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
#' base + stat_function(fun = dnorm, geom = "point")
#' base + stat_function(fun = dnorm, geom = "point", n = 20)
#' base + stat_function(fun = dnorm, n = 20)
#'
#' # Two functions on the same plot
#' base +
#' stat_function(fun = dnorm, colour = "red") +
#' stat_function(fun = dt, colour = "blue", args = list(df = 1))
#'
#' # Using a custom anonymous function
#' base + stat_function(fun = function(.x) .5*exp(-abs(.x)))
#' base + stat_function(fun = ~ .5*exp(-abs(.x)))
#'
#' # Using a custom named function
#' f <- function(.x) .5*exp(-abs(.x))
#' base + stat_function(fun = f)
#'
#' @rdname geom_function
stat_function <- function(mapping = NULL, data = NULL,
geom = "path", position = "identity",
geom = "function", position = "identity",
...,
fun,
xlim = NULL,
Expand All @@ -65,10 +25,7 @@ stat_function <- function(mapping = NULL, data = NULL,
show.legend = NA,
inherit.aes = TRUE) {

# Warn if supplied mapping and/or data is going to be overwritten
if (!is.null(mapping)) {
warn("`mapping` is not used by stat_function()")
}
# Warn if supplied data is going to be overwritten
if (!is.null(data)) {
warn("`data` is not used by stat_function()")
}
Expand Down
113 changes: 65 additions & 48 deletions man/stat_function.Rd → man/geom_function.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading