Skip to content

Commit 49aca36

Browse files
authored
Implement geom_function(). (#3982)
* Implement geom_function(). Closes #3611. * update unit test * rename test file, check for empty data * improve documentation * consolidate news items
1 parent 101c68d commit 49aca36

File tree

8 files changed

+187
-119
lines changed

8 files changed

+187
-119
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ Collate:
131131
'geom-errorbar.r'
132132
'geom-errorbarh.r'
133133
'geom-freqpoly.r'
134+
'geom-function.R'
134135
'geom-hex.r'
135136
'geom-histogram.r'
136137
'geom-hline.r'

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export(GeomDensity2dFilled)
175175
export(GeomDotplot)
176176
export(GeomErrorbar)
177177
export(GeomErrorbarh)
178+
export(GeomFunction)
178179
export(GeomHex)
179180
export(GeomHline)
180181
export(GeomLabel)
@@ -351,6 +352,7 @@ export(geom_dotplot)
351352
export(geom_errorbar)
352353
export(geom_errorbarh)
353354
export(geom_freqpoly)
355+
export(geom_function)
354356
export(geom_hex)
355357
export(geom_histogram)
356358
export(geom_hline)

NEWS.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
native rasters render significantly faster than arrays (@kent37, #3388)
88

99
* 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)
10-
11-
* `stat_function()` now works with transformed y axes, e.g. `scale_y_log10()`
12-
(@clauswilke, #3905).
13-
10+
11+
* A newly added `geom_function()` is now the recommended geom to use in
12+
conjunction with `stat_function()`. In addition, `stat_function()` now
13+
works with transformed y axes, e.g. `scale_y_log10()` (@clauswilke, #3611, #3905).
14+
1415
* A bug was fixed in `stat_contour()` when calculating breaks based on
1516
the `bins` argument (@clauswilke, #3879).
1617

R/geom-function.R

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#' Draw a function as a continuous curve
2+
#'
3+
#' Computes and draws a function as a continuous curve. This makes it easy to
4+
#' superimpose a function on top of an existing plot. The function is called
5+
#' with a grid of evenly spaced values along the x axis, and the results are
6+
#' drawn (by default) with a line.
7+
#'
8+
#' @eval rd_aesthetics("geom", "function")
9+
#' @param data Ignored by `stat_function()`, do not use.
10+
#' @inheritParams layer
11+
#' @inheritParams geom_path
12+
#' @examples
13+
#'
14+
#' # geom_function() is useful for overlaying functions
15+
#' set.seed(1492)
16+
#' ggplot(data.frame(x = rnorm(100)), aes(x)) +
17+
#' geom_density() +
18+
#' geom_function(fun = dnorm, colour = "red")
19+
#'
20+
#' # To plot functions without data, specify range of x-axis
21+
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
22+
#' base + geom_function(fun = dnorm)
23+
#' base + geom_function(fun = dnorm, args = list(mean = 2, sd = .5))
24+
#'
25+
#' # The underlying mechanics evaluate the function at discrete points
26+
#' # and connect the points with lines
27+
#' base + stat_function(fun = dnorm, geom = "point")
28+
#' base + stat_function(fun = dnorm, geom = "point", n = 20)
29+
#' base + geom_function(fun = dnorm, n = 20)
30+
#'
31+
#' # Two functions on the same plot
32+
#' base +
33+
#' geom_function(aes(colour = "normal"), fun = dnorm) +
34+
#' geom_function(aes(colour = "t, df = 1"), fun = dt, args = list(df = 1))
35+
#'
36+
#' # Using a custom anonymous function
37+
#' base + geom_function(fun = function(x) 0.5*exp(-abs(x)))
38+
#' base + geom_function(fun = ~ 0.5*exp(-abs(.x)))
39+
#'
40+
#' # Using a custom named function
41+
#' f <- function(x) 0.5*exp(-abs(x))
42+
#' base + geom_function(fun = f)
43+
#' @export
44+
geom_function <- function(mapping = NULL, data = NULL, stat = "function",
45+
position = "identity", ..., na.rm = FALSE,
46+
show.legend = NA, inherit.aes = TRUE) {
47+
# Warn if supplied data is going to be overwritten
48+
if (!is.null(data) && identical(stat, "function")) {
49+
warn("`data` is not used by stat_function()")
50+
}
51+
52+
layer(
53+
data = data,
54+
mapping = mapping,
55+
stat = stat,
56+
geom = GeomFunction,
57+
position = position,
58+
show.legend = show.legend,
59+
inherit.aes = inherit.aes,
60+
params = list(
61+
na.rm = na.rm,
62+
...
63+
)
64+
)
65+
}
66+
67+
#' @rdname ggplot2-ggproto
68+
#' @format NULL
69+
#' @usage NULL
70+
#' @export
71+
#' @include geom-path.r
72+
GeomFunction <- ggproto("GeomFunction", GeomPath,
73+
draw_panel = function(self, data, panel_params, coord, arrow = NULL,
74+
lineend = "butt", linejoin = "round", linemitre = 10,
75+
na.rm = FALSE) {
76+
groups <- unique(data$group)
77+
if (length(groups) > 1) {
78+
warn("Multiple drawing groups in `geom_function()`. Did you use the correct `group`, `colour`, or `fill` aesthetics?")
79+
}
80+
81+
ggproto_parent(GeomPath, self)$draw_panel(
82+
data, panel_params, coord, arrow, lineend, linejoin, linemitre, na.rm
83+
)
84+
}
85+
)

R/stat-function.r

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,21 @@
1-
#' Compute function for each x value
2-
#'
3-
#' This stat makes it easy to superimpose a function on top of an existing plot.
4-
#' The function is called with a grid of evenly spaced values along the x axis,
5-
#' and the results are drawn (by default) with a line.
6-
#'
7-
#'
81
#' @param fun Function to use. Either 1) an anonymous function in the base or
92
#' rlang formula syntax (see [rlang::as_function()])
103
#' or 2) a quoted or character name referencing a function; see examples. Must
114
#' be vectorised.
12-
#' @param n Number of points to interpolate along
5+
#' @param n Number of points to interpolate along the x axis.
136
#' @param args List of additional arguments passed on to the function defined by `fun`.
147
#' @param xlim Optionally, restrict the range of the function to this range.
15-
#' @inheritParams layer
16-
#' @inheritParams geom_point
178
#' @section Computed variables:
9+
#' `stat_function()` computes the following variables:
1810
#' \describe{
19-
#' \item{x}{x's along a grid}
20-
#' \item{y}{value of function evaluated at corresponding x}
11+
#' \item{x}{x values along a grid}
12+
#' \item{y}{value of the function evaluated at corresponding x}
2113
#' }
2214
#' @seealso [rlang::as_function()]
2315
#' @export
24-
#' @examples
25-
#'
26-
#' # stat_function is useful for overlaying functions
27-
#' set.seed(1492)
28-
#' ggplot(data.frame(x = rnorm(100)), aes(x)) +
29-
#' geom_density() +
30-
#' stat_function(fun = dnorm, colour = "red")
31-
#'
32-
#' # To plot functions without data, specify range of x-axis
33-
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
34-
#' base + stat_function(fun = dnorm)
35-
#' base + stat_function(fun = dnorm, args = list(mean = 2, sd = .5))
36-
#'
37-
#' # The underlying mechanics evaluate the function at discrete points
38-
#' # and connect the points with lines
39-
#' base <- ggplot(data.frame(x = c(-5, 5)), aes(x))
40-
#' base + stat_function(fun = dnorm, geom = "point")
41-
#' base + stat_function(fun = dnorm, geom = "point", n = 20)
42-
#' base + stat_function(fun = dnorm, n = 20)
43-
#'
44-
#' # Two functions on the same plot
45-
#' base +
46-
#' stat_function(fun = dnorm, colour = "red") +
47-
#' stat_function(fun = dt, colour = "blue", args = list(df = 1))
48-
#'
49-
#' # Using a custom anonymous function
50-
#' base + stat_function(fun = function(.x) .5*exp(-abs(.x)))
51-
#' base + stat_function(fun = ~ .5*exp(-abs(.x)))
52-
#'
53-
#' # Using a custom named function
54-
#' f <- function(.x) .5*exp(-abs(.x))
55-
#' base + stat_function(fun = f)
56-
#'
16+
#' @rdname geom_function
5717
stat_function <- function(mapping = NULL, data = NULL,
58-
geom = "path", position = "identity",
18+
geom = "function", position = "identity",
5919
...,
6020
fun,
6121
xlim = NULL,
@@ -65,10 +25,7 @@ stat_function <- function(mapping = NULL, data = NULL,
6525
show.legend = NA,
6626
inherit.aes = TRUE) {
6727

68-
# Warn if supplied mapping and/or data is going to be overwritten
69-
if (!is.null(mapping)) {
70-
warn("`mapping` is not used by stat_function()")
71-
}
28+
# Warn if supplied data is going to be overwritten
7229
if (!is.null(data)) {
7330
warn("`data` is not used by stat_function()")
7431
}

man/stat_function.Rd renamed to man/geom_function.Rd

Lines changed: 65 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)