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