Reputation: 41
I simply want to draw multiple arrows on a scatterplot using ggplot2. In this (dummy) example, an arrow is drawn but it moves as i is incremented and only one arrow is drawn. Why does that happen?
library(ggplot2)
a <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
b <- data.frame(x1=c(2,3),y1=c(10,10),x2=c(3,4),y2=c(15,15))
for (i in 1:nrow(b)) {
a <- a + geom_segment(arrow=arrow(),
mapping = aes(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))
plot(a)
}
Thanks.
Upvotes: 4
Views: 423
Reputation: 206167
This isn't strange behavior, this is exactly how aes()
is supposed to work. It delays evaluation of the parameters until the plotting actually runs. This is problematic if you include expressions to variable outside your data.frame (like i
) and functions (like [,]
). These are only evaulated when you actually "draw" the plot.
If you want to force evaulation of your parameters, you can use aes_
. This will work for you
for (i in 1:nrow(b)) {
a <- a + geom_segment(arrow=arrow(),
mapping = aes_(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))
}
plot(a)
Now within the loop the parameters for x=
and y=
, etc are evaluated in the environment and their value are "frozen" in the layer.
Of course, it would be better not to build layers in loops and just procide a proper data object as pointed out in @eipi10's answer.
Upvotes: 3
Reputation: 93761
As @Roland explains in the comment thread to this answer, only one arrow is plotted, because geom_segment(arrow=arrow(), mapping = aes(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))
is evaluated only when a
is plotted. But i
only has one value each time a
is plotted. During the first time through the loop, i=1
and during the second time i=2
. After the loop i
also still equals 2. Thus, only one arrow is plotted each time. If, after the loop, you run i=1:2
then you'll get both arrows. On the other hand, if you change i
to anything other than 1 and/or 2, you won't get any arrows plotted.
In any case, you can get both arrows without a loop as follows:
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_segment(data=b, arrow=arrow(), aes(x=x1,y=y1,xend=x2,yend=y2))
Question regarding @Roland's first comment: Shouldn't the object a
be updated each time through the loop by adding the new geom_segment? For example, if I start with the OP's original a
, then after one iteration of the loop,
a = a + geom_segment(arrow=arrow(), aes(x=b[1,1],y=b[1,2],xend=b[1,3],yend=b[1,4]))
Then, after two iterations of the loop,
a = a + geom_segment(arrow=arrow(), aes(x=b[1,1],y=b[1,2],xend=b[1,3],yend=b[1,4])) +
geom_segment(arrow=arrow(), aes(x=b[2,1],y=b[2,2],xend=b[2,3],yend=b[2,4]))
where in each case a
means the value of a
before the start of the loop. Shouldn't those underlying changes to the object a
occur regardless of the when or if a
is evaluated?
Upvotes: 2