总结:当我使用“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://stackoverflow.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://stackoverflow.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
答案 0 :(得分:9)
发生这种情况的原因是ggplot
的“懒惰评估”。当ggplot
以这种方式使用时,这是一个常见的问题(将这些图层分别放在循环中,而不是像{hrbrmstr的解决方案中那样为ggplot
创建)。
ggplot
将aes(...)
的参数存储为表达式,并仅在渲染绘图时对其进行求值。所以,在你的循环中,像
aes(y = df[,p], colour = place[p-1])
按原样存储,并在循环完成后渲染绘图时进行评估。此时,p = 3,因此所有图都以p = 3呈现。
所以“正确”的方法是在melt(...)
包中使用reshape2
,以便将数据从宽格式转换为长格式,让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")
答案 1 :(得分:3)
然后避免使用for
循环。如何改为lapply
:
g <- g + lapply(2:ncol(df), function(p) {
geom_violin(aes(y = df[,p], colour = place[p-1]), alpha = 0.3)
})
编辑:这真的不起作用。在运行它之前,我在我的工作区中有p <- 2
,然后它生成了一个仅包含芝加哥数据的图表。无论如何,该原则仍然有效(尽管melt
可能是更好的选择):
g <- ggplot(df, aes(x=factor(topic)))
g + lapply(place, function(p) {
geom_violin(aes_string(y = p), alpha = 0.3, color = which(p==place))
})
答案 2 :(得分:2)
你可以没有循环:
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
答案 3 :(得分:1)
您可以使用 aes_() 而不是 aes(),这似乎可以停止惰性求值。在链接到此处 (Update a ggplot using a for loop (R)) 的封闭问题上找到答案,但认为它应该在此处,因为另一个问题已关闭。