I want to add multiple curves to one ggplot
. With the normal plot
and curve(add=TRUE)
i can loop the curve function and can add multiple curves/functions.
Since i want to rewrite my code for ggplot
, i'm trying to figure out how achieve this with ggplot.
I'm using the stat_function
and a for
loop, but ggplot
is not adding the curve
, just saving the last one.
Minimum reproducible example with a single line:
library(ggplot2)
p1<-ggplot()
for (i in 1:10){
p1<- p1 + stat_function(aes(x=1:200),fun = function(x) {x+i*3}, col=i)
print(p1)
}
What i think the code should do:
I loop multiple stat_function
and adding it to my plot p1
What i want to do:
I want alle lines to be added in one plot(p1
). The code as it is, just shows the last line of the loop, as if it is overwriting the plot always, but as far as i understand ggplot
this should just add a line. Also an explanation why this does not work would be nice, maybe i just don't understand ggplot
here
The issue is that all the stat_function
s you are refer to the same i
variable. And as a consequence, each function you add will overlay perfectly with the others.
The solution is to reassign the variable in a local scope, to make a local copy for each iteration:
p1 = ggplot(data.frame(x = 1 : 200)) + aes(x)
for (i in 1:10){
p1 = local({
j = i
p1 + stat_function(fun = function(x) x + j * 3, col = j)
})
}
To make matters more confusing, you don’t actually have to give the local variable a new name; you could just as easily have written i = i
, and continued to use i
. The reason is that this assignment will generate a new local variable i
that masks the non-local variable i
. I hope we can agree that writing such code is confusing, and a bad idea.
I’ve also taken the liberty of simplifying your code slightly by moving the data x
out of the stat_function
, and into the ggplot
object directly.
However, it is altogether cleaner not to use a loop and reassignment here. Instead you can use lapply
or map
(from the purrr package):
p1 = ggplot(data.frame(x = 1 : 200)) +
aes(x) +
map(
1 : 10,
~ stat_function(fun = function (x) x + .x * 3, color = .x)
)
This is shorter, more readable (it focuses on the “what” rather than the “how”, i.e. the mechanics of the loop), and it uses a single assignment.
Try:
library(ggplot2)
add_curve <- function(plot, i) {
return(plot + stat_function(aes(x=1:200),fun = function(x) {x+i*3}, col=i))
}
p1 <- ggplot()
for (i in 1:10){
p1<- add_curve(p1, i)
}
p1
Output is:
Or alternatively you can also define your function inside the for-loop:
for (i in 1:10){
add_curve <- function(plot, i) {
return(plot + stat_function(aes(x=1:200),fun = function(x) {x+i*3}, col=i))
}
p1<- add_curve(p1, i)
}
Or as a (somewhat obscure) Reduce
+ lapply
one-liner (thx to @KonradRudolph):
eval(
Reduce(function(a, b) call('+', a, b),
lapply(1:10, function(i) {
bquote(stat_function(aes(x=1:200),fun = function(x) {x+.(i)*3
}, col=.(i)))}), init = quote(ggplot())))
The idea is to build the whole ggplot()
call at once and then evaluate it.