“for”循环只添加最后的ggplot图层

简介:当我使用“for”循环将图层添加到小提琴绘图(在ggplot中)时,唯一添加的图层是由最终循环迭代创build的图层。 然而,在模仿循环产生的代码的显式代码中,所有的图层都被添加了。

详细信息:我正在尝试创build具有重叠图层的小提琴图表,以显示估计分布与多个调查问题响应是否重叠的程度,按地点分层。 我希望能够包含任意数量的位置,所以我在每个位置都有一个数据框的列,并试图使用“for”循环为每个位置生成一个ggplot图层。 但循环仅添加循环的最终迭代中的图层。

这个代码说明了这个问题,一些build议的方法失败了:

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 (ie, 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 

这是因为ggplot的“懒惰评估”。 当ggplot以这种方式使用时,这是一个常见的问题(使图层单独在一个循环中,而不是像在@ hrbrmstr的解决scheme中那样为你使用ggplot)。

ggplot将参数作为expression式存储到aes(...)中,并且只在渲染图时对其进行评估。 所以,在你的循环中,像

 aes(y = df[,p], colour = place[p-1]) 

按原样存储,并在循环完成后渲染绘图时进行评估。 在这一点上,P = 3所以所有的情节都呈现与P = 3。

因此,“正确”的方法是在reshape2包中使用melt(...) ,将数据从宽格式转换为长格式,并让ggplot为您pipe理图层。 我把“正确的”置于引号中,因为在这个特定的情况下,有一个微妙之处。 当使用融化的dataframe计算小提琴的分布时, ggplot使用总计(芝加哥和迈阿密)作为比例。 如果你想要基于频率缩放单独的小提琴,你需要使用循环(悲伤)。

解决懒惰评估问题的方法是在data=...定义中引用循环索引。 这不是作为expression式存储的,实际的数据存储在绘图定义中。 所以你可以这样做:

 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的原始代码不一样。 海事组织这不是一个好的数据可视化的方法,因为它表明芝加哥的数据更多。

 ggplot(gg) +geom_violin(aes(x=topic,y=value,color=variable), alpha=0.3,position="identity",scale="width") 

你可以做W / OA循环:

 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 

在这里输入图像描述

那么就避免使用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 ,然后它只生成一个只有芝加哥数据的图表。 无论如何,原则应该仍然有效(尽pipemelt可能是更好的select):

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