"for" 循环只添加最后的 ggplot 层
Posted
技术标签:
【中文标题】"for" 循环只添加最后的 ggplot 层【英文标题】:"for" loop only adds the final ggplot layer 【发布时间】:2014-12-01 20:51:55 【问题描述】:总结:当我使用“for”循环将层添加到小提琴图(在 ggplot 中)时,唯一添加的层是由最终循环迭代创建的层。然而,在模仿循环产生的代码的显式代码中,所有的层都被添加了。
详细信息:我正在尝试创建具有重叠层的小提琴图,以显示估计分布在多个调查问题响应中重叠或不重叠的程度,按地点分层。我希望能够包含任意数量的地方,所以我在每个地方的数据框中有一列,并尝试使用“for”循环为每个地方生成一个 ggplot 层。但循环只添加循环最后一次迭代中的层。
这段代码说明了问题,以及一些失败的建议方法:
library(ggplot2)
# Create a dataframe with 500 random normal values for responses to 3 survey questions from two cities
topic <- c("Poverty %","Mean Age","% Smokers")
place <- c("Chicago","Miami")
n <- 500
mean <- c(35, 40,58, 50, 25,20)
var <- c( 7, 1.5, 3, .25, .5, 1)
df <- data.frame( topic=rep(topic,rep(n,length(topic)))
,c(rnorm(n,mean[1],var[1]),rnorm(n,mean[3],var[3]),rnorm(n,mean[5],var[5]))
,c(rnorm(n,mean[2],var[2]),rnorm(n,mean[4],var[4]),rnorm(n,mean[6],var[6]))
)
names(df)[2:dim(df)[2]] <- place # Name those last two columns with the corresponding place name.
head(df)
# This "for" loop seems to only execute the final loop (i.e., where p=3)
g <- ggplot(df, aes(factor(topic), df[,2]))
for (p in 2:dim(df)[2])
g <- g + geom_violin(aes(y = df[,p], colour = place[p-1]), alpha = 0.3)
g
# But mimicing what the for loop does in explicit code works fine, resulting in both "place"s being displayed in the graph.
g <- ggplot(df, aes(factor(topic), df[,2]))
g <- g + geom_violin(aes(y = df[,2], colour = place[2-1]), alpha = 0.3)
g <- g + geom_violin(aes(y = df[,3], colour = place[3-1]), alpha = 0.3)
g
## per http://***.com/questions/18444620/set-layers-in-ggplot2-via-loop , I tried
g <- ggplot(df, aes(factor(topic), df[,2]))
for (p in 2:dim(df)[2])
df1 <- df[,c(1,p)]
g <- g + geom_violin(aes(y = df1[,2], colour = place[p-1]), alpha = 0.3)
g
# but got the same undesired result
# per http://***.com/questions/15987367/how-to-add-layers-in-ggplot-using-a-for-loop , I tried
g <- ggplot(df, aes(factor(topic), df[,2]))
for (p in names(df)[-1])
cat(p,"\n")
g <- g + geom_violin(aes_string(y = p, colour = p), alpha = 0.3) # produced this error: Error in unit(tic_pos.c, "mm") : 'x' and 'units' must have length > 0
# g <- g + geom_violin(aes_string(y = p ), alpha = 0.3) # produced this error: Error: stat_ydensity requires the following missing aesthetics: y
g
# but that failed to produce any graphic, per the errors noted in the "for" loop above
【问题讨论】:
你为什么不把数据帧melt
长格式?
【参考方案1】:
虽然一般来说,重塑数据始终是首选,使用较新版本的 ggplot2 (>3.0.0),您可以使用 !!
将值注入 aes()
例如您可以这样做
g <- ggplot(df, aes(factor(topic), df[,2]))
for (p in 2:dim(df)[2])
g <- g + geom_violin(aes(y = df[,!!p], colour = place[!!p-1]), alpha = 0.3)
g
为了得到想要的结果。 !!
将强制进行评估,而不是像默认设置那样保持惰性。
【讨论】:
【参考方案2】:您可以使用 aes_() 而不是 aes(),这似乎可以停止惰性求值。在链接到此处 (Update a ggplot using a for loop (R)) 的已关闭问题上找到答案,但认为它应该在这里,因为其他问题已关闭。
【讨论】:
我遇到了类似的问题,这是一个非常简单的解决方案。【参考方案3】:发生这种情况的原因是ggplot
的“懒惰评估”。这是以这种方式使用ggplot
时的常见问题(在循环中单独制作层,而不是像@hrbrmstr 的解决方案那样为您提供ggplot
)。
ggplot
将aes(...)
的参数存储为表达式,并且仅在绘制绘图时评估它们。所以,在你的循环中,像
aes(y = df[,p], colour = place[p-1])
按原样存储,并在循环完成后渲染绘图时进行评估。此时,p=3,所以所有绘图都以 p=3 呈现。
因此,“正确”的做法是在 reshape2
包中使用 melt(...)
,以便将数据从宽格式转换为长格式,并让 ggplot
为您管理图层。我将“正确”放在引号中,因为在这种特殊情况下存在微妙之处。当使用融化的数据框计算小提琴的分布时,ggplot
使用总计(芝加哥和迈阿密)作为比例。如果您想要基于频率单独缩放的小提琴,您需要使用循环(可悲)。
解决惰性求值问题的方法是将任何对循环索引的引用放在data=...
定义中。这不存储为表达式,实际数据存储在绘图定义中。所以你可以这样做:
g <- ggplot(df,aes(x=topic))
for (p in 2:length(df))
gg.data <- data.frame(topic=df$topic,value=df[,p],city=names(df)[p])
g <- g + geom_violin(data=gg.data,aes(y=value, color=city))
g
这给出了与你相同的结果。请注意,索引p
不会出现在aes(...)
中。
更新:关于scale="width"
的注释(在评论中提到)。这会导致所有小提琴具有相同的宽度(见下文),这与 OP 的原始代码中的缩放比例不同。 IMO 这不是可视化数据的好方法,因为它表明芝加哥组中的数据要多得多。
ggplot(gg) +geom_violin(aes(x=topic,y=value,color=variable),
alpha=0.3,position="identity",scale="width")
【讨论】:
谢谢。我很欣赏循环和ggplot如何发生这种奇怪的解释。现在我明白了。我想它可能是这样的——我试图找到一个命令,将绘图作为每个循环的最后一步(就像“g”),但我没有尝试过。你的循环代码正是我所需要的。 这是一个很好的解释。指出另一种解决方法 - 一个更新的答案(链接到此处的已关闭问题)提到您可以使用 aes_() 而不是 aes() 来覆盖惰性评估(***.com/questions/44317502/…)【参考方案4】:你可以在没有循环的情况下做到这一点:
df.2 <- melt(df)
gg <- ggplot(df.2, aes(x=topic, y=value))
gg <- gg + geom_violin(position="identity", aes(color=variable), alpha=0.3)
gg
【讨论】:
这不会产生与 OP 的“成功”努力相同的情节,因为当您分别创建两个图层时,小提琴的缩放比例与您按variable
分组时不同。另外,可能应该提到OP需要为此加载reshape2
。
非常优雅。使用这种方法,如果我使用 'scale="width"' aes 选项,jlhoward 提到的分组而不是单个缩放的微妙之处将无关紧要。以上是关于"for" 循环只添加最后的 ggplot 层的主要内容,如果未能解决你的问题,请参考以下文章
新手提问 python for循环问题 print (y) #这里为啥只输出一行?
如何在javascript的for循环中增加相同标签添加onclick事件