Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to plot a function family in ggplot2

Tags:

plot

r

ggplot2

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:

two curves

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:

any number of curves

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?

like image 686
JulioSergio Avatar asked Apr 18 '17 02:04

JulioSergio


People also ask

How do I add a function in ggplot2?

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.

What does Geom_point () do in R?

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.

What does stat_function do in R?

stat_function() computes the following variables: x. x values along a grid. y.

Which function can you use to create a different plot for each type of cut of diamond?

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().


1 Answers

Add a list to 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.


Precalculate

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()

like image 141
alistaire Avatar answered Nov 08 '22 23:11

alistaire