I think the crux here is that you should combine your data so that each geom only needs to refer to one table, with the characteristic of the source table (e.g. min vs. max vs. mean) made explicit as a variable in that combined table.
Here's a quick function to make some fake data and save it to three tables:
make_fake <- function(a, b, label) {
df <- data.frame(name = "apple", number = 1:5, value = a - b*sqrt(1:5), level = 2)
df$lower = df$value - 0.5; df$upper = df$value + 0.5; df$label = label
df
}
fake_min <- make_fake(3,.1, "min")
fake_max <- make_fake(7,1.5, "max")
fake_mean <- make_fake(5,0.8, "mean")
To plot them together, it will be simpler if they are combined such that each geom only needs to refer to one table. I've done this by adding a label
variable in the fake data above.
If we use base::rbind
, we can append the tables to each other and plot them, distinguishing series by having the color
aesthetic connect to the label
variable:
ggplot(data = rbind(fake_min, fake_max, fake_mean),
aes(x=number, y=value, group=label))+
geom_line(aes(color=label))+
geom_ribbon(aes(ymin=lower, ymax=upper, fill=label, group=label), alpha=0.3)

Maybe you want a combined ribbon showing the highest upper and lowest lower. Then you could precalc those, here using dplyr:
library(dplyr)
rbind(fake_min, fake_max, fake_mean) %>%
group_by(number) %>%
summarize(upper = max(upper),
lower = min(lower)) -> fake_ribbon
rbind(fake_min, fake_max, fake_mean) %>%
ggplot(aes(x=number)) +
geom_line(aes(color=label, y=value))+
geom_ribbon(data = fake_ribbon, aes(ymin=lower, ymax=upper), alpha=0.2)
