I can plot a function in ggplot2, like so:
library(ggplot2)
ggplot(data.frame(x=0), aes(x)) + geom_function(fun = sin) + xlim(c(-5,5))
Can I use ggplot2's facetting to make the plot for multiple functions, one in each facet? (for example sin and cos)
Looks like you in fact can facet by the function if you feed each layer its own data with the faceting variable specified:
library(ggplot2) # using ggplot2 3.3.5
ggplot(data.frame(x=0), aes(x)) +
geom_function(fun = sin, data = data.frame(x = -5:5, fun_name = "sin")) +
geom_function(fun = cos, data = data.frame(x = -5:5, fun_name = "cos")) +
facet_wrap(~fun_name)

... fun is not an aesthetic ... you can make it one :)
It's a bit of an overkill, but just a quick demonstration what is possible by modifying the Geoms and Stats. The below is a quick hack and I've referenced with quite a lot of ugly :::, which you wouldn't do if you would properly package this. Also this is clearly not properly tested on many use cases. Few more comments in the code.
This was quite ... fun :)
df <- data.frame(x = 0, fun = c("sin", "cos", "tan", "mean"))
ggplot(df, aes(x)) +
stat_function2(aes(fun = fun)) +
xlim(c(-5,5)) +
facet_wrap(~fun, scales = "free_y")

Modifying Geom and Stat - StatFunction2
StatFunction2 <- ggproto(NULL, StatFunction)
## removing fun from the arguments
StatFunction2$compute_group <- function (data, scales, xlim = NULL, n = 101, args = list())
{
if (is.null(scales$x)) {
## need to change that here a bit
range <- rlang::`%||%`(xlim, c(0, 1))
xseq <- seq(range[1], range[2], length.out = n)
x_trans <- xseq
}
else {
## same same
range <- rlang::`%||%`(xlim, scales$x$dimension())
xseq <- seq(range[1], range[2], length.out = n)
if (scales$x$is_discrete()) {
x_trans <- xseq
}
else {
x_trans <- scales$x$trans$inverse(xseq)
}
}
## get the function, this is the trick :)
fun <- unique(data$fun)
if (plyr::is.formula(fun))
fun <- as_function(fun)
y_out <- do.call(fun, c(list(quote(x_trans)), args))
if (!is.null(scales$y) && !scales$y$is_discrete()) {
y_out <- scales$y$trans$transform(y_out)
}
ggplot2:::new_data_frame(list(x = xseq, y = y_out))
}
## update stat_function - remove fun argument and reference new geom_function2
stat_function2 <- function (mapping = NULL, data = NULL, geom = "function2", position = "identity",
..., fun, xlim = NULL, n = 101, args = list(), na.rm = FALSE,
show.legend = NA, inherit.aes = TRUE)
{
if (is.null(data)) {
### those ::: are just for to make it work here
data <- ggplot2:::ensure_nonempty_data
}
layer(data = data, mapping = mapping, stat = StatFunction2,
geom = geom, position = position, show.legend = show.legend,
## fun needs to be removed here too.
inherit.aes = inherit.aes, params = list(n = n,
args = args, na.rm = na.rm, xlim = xlim, ...))
}
## This is the correct way to create copies (children) of ggproto objects
## see https://stackoverflow.com/a/70637511/7941188
GeomFunction2 <- ggproto(NULL, GeomFunction)
## change the required aesthetics - this removes the warning that aesthetics are not known
GeomFunction2$required_aes <- c("x", "y", "fun")
## update the corresponding geom (two locations in this function definition)
geom_function2 <- function (mapping = NULL, data = NULL, stat = "function2", position = "identity",
..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
{
if (is.null(data)) {
data <- ensure_nonempty_data
}
layer(data = data, mapping = mapping, stat = stat, geom = GeomFunction2,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, ...))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With