ggplot2的奇怪行为

4

我想使用ggplot2在散点图上绘制多个箭头。在这个(虚拟的)例子中,虽然画出了一支箭,但随着i的增加,它会移动,并且只有一支箭被画出。为什么会这样呢?

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

感谢您的选择。
2个回答

3
这并不是奇怪的行为,这正是aes()的工作方式。它延迟参数的评估,直到绘图实际运行。如果您在数据框外部包含变量(如i)和函数(如[,])的表达式,则会出现问题。只有在您实际“绘制”图形时才进行评估。
如果您想强制评估参数,可以使用aes_。 这将对您有所帮助。
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)

现在,在循环内部,x=y= 等参数在当前环境中被确定,并且它们的值在该层中被“冻结”。

当然,最好不要在循环中构建层,而是像 @eipi10 的答案所指出的那样提供适当的数据对象。


我想我以前听说过函数 aes_,但当时无法理解。现在我有了一个入门指南,会更加深入地了解它。谢谢。 - whitetiger2016

2
如@Roland在评论中解释的那样,只绘制了一个箭头,因为当绘制a时,仅评估geom_segment(arrow=arrow(), mapping = aes(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))。但是每次绘制a时,i只有一个值。在循环的第一次通过期间,i=1,在第二次通过期间,i=2。循环结束后,i仍然等于2。因此,每次仅绘制一个箭头。如果在循环后运行i=1:2,则会得到两个箭头。另一方面,如果将i更改为除1和/或2以外的任何内容,则不会绘制任何箭头。
无论如何,您可以按以下方式在没有循环的情况下获得两个箭头:
ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  geom_segment(data=b, arrow=arrow(), aes(x=x1,y=y1,xend=x2,yend=y2))

@Roland第一条评论的问题:在每次循环中,是否应该通过添加新的geom_segment来更新对象a?例如,如果我从OP的原始a开始,那么经过一次循环后,

a = a + geom_segment(arrow=arrow(), aes(x=b[1,1],y=b[1,2],xend=b[1,3],yend=b[1,4])) 

然后,在循环的两次迭代之后,

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

在每种情况下,a 指的是循环开始前的值。那么,无论何时或是否评估 a,对象 a 的底层更改不应该发生吗?

因为评估发生在绘图期间和循环之后 i == 2。 - Roland
@Roland,我有一个关于你的评论的问题,但我已经将它添加到我的答案中,这样代码会更容易阅读。 - eipi10
aes的参数仅在绘图期间进行评估。只有当发生i的值时才很重要。 - Roland
1
有趣。所以如果在循环之后,你将i更改为除1或2之外的任何值,然后评估a,你就不会得到任何箭头。另一方面,如果在循环之后运行i = 1:2,然后评估a,你会得到两个箭头。好吧,我学到了新东西。谢谢! - eipi10
感谢eipi10和Roland。我可以在我的脚本中绘制多个箭头。我尝试了不使用循环,但似乎忘记提供"data=b"。无论如何,我也学到了一些新东西,谢谢。 - whitetiger2016
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接