I need to plot a family of functions variying according to a set of parameters, say, a family of normal distribution curves that depend on the mean and standard deviation. I found here, a code snipet that almost do the task:
p9 <- ggplot(data.frame(x = c(0, 1)), aes(x = x)) +
stat_function(fun = dnorm, args = list(0.2, 0.1),
aes(colour = "Group 1")) +
stat_function(fun = dnorm, args = list(0.7, 0.05),
aes(colour = "Group 2")) +
scale_x_continuous(name = "Probability",
breaks = seq(0, 1, 0.2),
limits=c(0, 1)) +
scale_y_continuous(name = "Frequency") +
ggtitle("Normal function curves of probabilities") +
scale_colour_brewer(palette="Accent") +
labs(colour = "Groups")
p9
In this case, the code plots exactly two curves, as shown below:
My problem is that the number of curves in the family could be any, thus I tried to adapt the code as follows:
aa <- list(list(0.2, 0.1), list(0.7, 0.05), list(0.45, 0.2))
p9 <- ggplot(data.frame(x = c(0, 1)), aes(x = x))
for (i in 1:3) {
p9 <- p9 + stat_function(fun = dnorm, args = aa[[i]],
aes(colour = paste("Group", i))
}
p9 <- p9 +
scale_x_continuous(name = "Probability",
breaks = seq(0, 1, 0.2),
limits=c(0, 1)) +
scale_y_continuous(name = "Frequency") +
ggtitle("Normal function curves of probabilities") +
scale_colour_brewer(palette="Accent") +
labs(colour = "Groups")
p9
The result is almost successful in that it depicts the three curves, except that it does not distinguish them by colors nor in the legend,as it is shown below:
I guess the problem arises from the manner the function aes() manages its arguments. Do you have any idea on how to rewrite my code?
Plotting a function is very easy with curve function but we can do it with ggplot2 as well. Since ggplot2 provides a better-looking plot, it is common to use it for plotting instead of other plotting functions. To plot a function, we should specify the function under stat_function in ggplot.
The function geom_point() adds a layer of points to your plot, which creates a scatterplot. ggplot2 comes with many geom functions that each add a different type of layer to a plot.
stat_function() computes the following variables: x. x values along a grid. y.
The diamonds data set [in ggplot2] we'll be used to plot the discrete variable color (for diamond colors) by the discrete variable cut (for diamond cut types). The plot is created using the function geom_jitter().
ggplot
A quick edit: I just learned an idiom I just learned from @BrodieG that's very applicable here: you can add a list of geoms or stats directly to a ggplot
call, which allows you to avoid the convolutions of Reduce
for lapply
or even Map
, which lets you pass as many variables in parallel as you like. Combined with @JulioSergio's aes_
approach, you get a decent plot with readable code that's easily customizable:
ggplot(data.frame(x = 0:1), aes(x)) +
Map(function(params, name){stat_function(mapping = aes_(color = name),
fun = dnorm, args = params)},
params = aa,
name = paste('Group', seq_along(aa)))
Reduce
The structure lends itself reasonably well to Reduce
with init
set to the initial ggplot
call. Colors can be added by indexing a palette function by the number of layers in the object at that point:
Reduce(function(x, y){
x + stat_function(fun = dnorm, args = y,
colour = scales::brewer_pal('qual', 'Set1')(length(aa))[length(x$layers) + 1])},
aa,
init = ggplot(data.frame(x = c(0, 1)), aes(x = x)))
The disadvantage of this approach is that it does not make a nice legend, as it's hardcoding colors.
One way around this is to simply do the calculations before plotting, which makes the plotting itself very simple:
library(tidyverse)
aa <- list(list(0.2, 0.1), list(0.7, 0.05), list(0.45, 0.2))
aa %>% set_names(paste('Group', 1:3)) %>%
map_df(~dnorm(seq(0, 1, length = 100), .x[[1]], .x[[2]])) %>%
mutate(x = seq(0, 1, length = 100)) %>%
gather(Group, y, -x) %>%
ggplot(aes(x, y, color = Group)) +
geom_line()
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