Home > other >  How to add multiple geoms to ggplot using a list (or for loop)?
How to add multiple geoms to ggplot using a list (or for loop)?

Time:10-04

My question is, is it possible to dynamically create a list of geoms, which I can add to a ggplot, enabling me to plot several, seperate series of data all at once?

Reproducible Example

The following code demonstrates my question:

library(ggplot2)

# Function to generate fake data
generate_fake_results = function(){
  results = list()
  
  for(i in c(1:10)){
    
    x = c((1 10*i):(10 10*i))
    
    results = append(results, list(data.frame(
      x = as.Date("2000-01-01")   x,
      y = sin(x),
      ylower1 = sin(x) - 0.25,
      ylower2 = sin(x) - 0.5,
      yupper1 = sin(x)   0.25,
      yupper2 = sin(x)   0.50
    )
    )
    )
  }
  
  return(results)
}

fake_data = generate_fake_results()


# Function to plot the mean, upper and lower bounds of a model
# The dataset contains two upper and lower bounds; the 80% and 95% confidence interval
predict_margin_func = function(r, color='blue', alpha=0.1){
  return(
    list(
      geom_ribbon(aes(x=as.Date(r$x,"%Y-%m-%d"),
                      ymin=r$ylower1,
                      ymax=r$yupper1), fill=color, alpha=alpha),
      geom_ribbon(aes(x=as.Date(r$x,"%Y-%m-%d"),
                      ymin=r$ylower2,
                      ymax=r$yupper2), fill=color, alpha=alpha),
      geom_line(aes(x=as.Date(r$x,"%Y-%m-%d"), y=r$y), size=1.25, color=color)
    )
  )
}



# This plots the graph that I want, but... I have to manually add each forecast 
# from my fake_data list "manually"
ggplot()  
  predict_margin_func(fake_data[[1]])   
  predict_margin_func(fake_data[[2]])   
  predict_margin_func(fake_data[[3]])  
  predict_margin_func(fake_data[[4]])  
  predict_margin_func(fake_data[[5]])


# I'd rather use a for loop to do this dynamically, but I can't get it to work.
# If I do this, it doesn't work:
plot_list = list()
for(i in c(1:length(fake_data))){
  plot_list = append(plot_list, predict_margin_func(fake_data[[i]]))
}

ggplot()  
  plot_list

While solution 1 "works", I'd much rather use something like solution 2, where I don't have to manually add each series I want to plot, as this is more easily extendible if the number of forecasts in the results list changes.

The results in plot_list seem to be 10 copies of the last result/the highest i from the for loop. I'm quessing R is doing some clever trick, which I don't want in this specific case, where the results in the list are instances/references of a thing, where I want "the thing that is being referenced too".

Does anyone have an idea what I could do here? I could maybe also reshape my data, but I wondered if it were possible to do using a list.

CodePudding user response:

Up front: I can fix your for loop (see below), but I think a better solution is to do:

ggplot()   lapply(fake_data, predict_margin_func)

enter image description here


As to why your for loop is failing ...

ggplot tends to operate lazily, so the [[i]] is being realized at the time it is rendered, not when the geom is created. This is why you're only seeing the last of them. While R ggplot2 for loop plots same data appears to be a similar question, the proposed answers don't really apply. Why? Because you're hard-coding values into your geoms instead of assigning data= within them and using non-standard evaluation within the geom itself.

Here, I add data=r to each geom and remove their references to r$.

predict_margin_func2 = function(r, color='blue', alpha=0.1){
  return(
    list(
      geom_ribbon(data = r, aes(x=as.Date(x,"%Y-%m-%d"),
                      ymin=ylower1,
                      ymax=yupper1), fill=color, alpha=alpha),
      geom_ribbon(data = r, aes(x=as.Date(x,"%Y-%m-%d"),
                      ymin=ylower2,
                      ymax=yupper2), fill=color, alpha=alpha),
      geom_line(data = r, aes(x=as.Date(x,"%Y-%m-%d"), y=y), size=1.25, color=color)
    )
  )
}
plot_list = list()
for(i in c(1:length(fake_data))){
  plot_list = append(plot_list, predict_margin_func2(fake_data[[i]]))
}
ggplot()   plot_list

(produces the same plot as above).

  • Related