使用geom_boxplot绘制分位数箱线图

3

问题

我想使用ggplot的geom_boxplot,并使用我自己的数据列作为分位数段,而不是由stat_boxplot返回的数据列。

经过一些转换后,数据看起来像这样:

> allquartile                                                      
      T method       s.0%      s.25%      s.50%      s.75%     s.100%                                                                                                    
1     2    LDA -196.76273 -190.38842 -184.01411 -177.63979 -171.26548                                                                                                    
2     3    LDA -171.53987 -166.16923 -160.79859 -115.28652  -69.77446                                                                                                    
3     4    LDA -161.17590 -157.61372 -149.71026 -124.68926  -69.77446                                                                                                    
4     5    LDA -194.10553 -179.83165 -175.14337 -168.46104 -159.07206 

经过大量搜索和挖掘,我发现我的绘图命令应该是这样的:

p <- ggplot(allquartile,aes(x=T, ymin=`s.0%`, lower=`s.25%`,
                            middle=`s.50%`, upper=`s.75%`,
                            ymax=`s.100%`, color=method)) + 
     geom_boxplot(stat="identity")

这里应该使用s.0%作为最小值,s.25%作为较低值等等。但是当我尝试显示p时,出现以下错误:

Error in eval(expr, envir, enclos) : object 's.0%' not found                                                                                                             
Calls: print ... lapply -> is.vector -> lapply -> FUN -> eval -> eval

我还尝试使用aes_string代替aes,但是我得到了以下错误:

Error in aes_string(x = T, ymin = `s.0%`, lower = `s.25%`, middle = `s.50%`,  :                                                                                            
object 's.0%' not found 

我对R和ggplot2都比较新,不太确定如何解释这个问题,但我猜测是由于s.0%中的.所致。

如果您有任何建议,请不吝赐教。

编辑:我查找了更多资料,发现这是由于我对分位数方法的误解造成的。我通过以下命令创建了allquartile

allquartile <-aggregate(list(s=topicquality$score), list(T=topicquality$T,method=topicquality$method),FUN=quantile,probs=seq(0, 1, .25)) 

我意识到没有名为score.0%score.25%等的列,只有包含5个值的score列。所以问题是:如何访问score中的这5个值?

解决方案

我已经发现了数据集中的问题。正如我在编辑中提到的,基于我生成数据框架的方式,score.0%score.25%等列并不存在。例如,运行colnames(allquartile)返回:

[1] "T"      "method" "score"

原来,score列是一个值的向量。运行allquartile$score会得到以下结果:
            0%       25%       50%       75%       100%
[1,] -196.7627 -190.3884 -184.0141 -177.6398 -171.26548
[2,] -171.5399 -166.1692 -160.7986 -115.2865  -69.77446
[3,] -161.1759 -157.6137 -149.7103 -124.6893  -69.77446
[4,] -194.1055 -179.8316 -175.1434 -168.4610 -159.07206
[5,] -200.1544 -174.2835 -167.7209 -145.3432 -129.54586

我可以通过执行以下操作访问每个单独分位数的值。
> allquartile$score[,1]
[1] -196.7627 -171.5399 -161.1759 -194.1055 -200.1544

我对R不太熟悉,不确定这是什么数据结构,但我会称之为矩阵。因此,像任何好的矩阵对象一样,m [,列]返回列的值,而m [行,]返回行的值,m [行,列]获取单元格的值。

有了这个想法,我意识到正确的绘图命令应该是

p <- ggplot(allquartile,
            aes(x=T,
                ymin=score[,1],
                lower=score[,2],
                middle=score[,3],
                upper=score[,4], 
                ymax=score[,5], 
                color=method)) + 
     geom_boxplot(stat="identity") 

这将完美地呈现所有东西。

感谢大家提供的好建议,虽然它们没有解决问题,但对于搞清楚事情有很大帮助。


我原以为那很好,但是我发现这段示例代码可以正确地绘制别人的数据。p <- ggplot(data,aes(x=.id, ymin=\5%`, lower=`25%`, middle=`50%`, upper=`75%`, ymax=`95%`)) + geom_boxplot(stat="identity")` - fozziethebeat
好的,我只是想引导您朝着尝试简单的调试方向迈出第一步:将“allquartile”中的列名设置为绝对安全的内容(没有符号)。 - joran
@Henry:哎呀,那肯定是个打字错误,但问题仍然存在。另外,s.x%名称周围的所有内容都应该用反引号括起来。 - fozziethebeat
@joran:我喜欢那个建议,但是我完全不懂R(实际上我继承了这段代码,并试图通过箱线图来扩展它)。如何重置“allquartile”中的列名? - fozziethebeat
使用 colnames(allquartile) <- c(...) - joran
显示剩余2条评论
2个回答

0

实际上,根据您的编辑,我认为您真正的问题在于不应该使用 aggregate。如果您应用的函数返回多个值(例如 quantile),aggregate 将默认以略微不便的格式返回结果。

发生的情况是这样的。数据框实际上有点令人困惑,它实际上是一个列表,其中每一列都是列表的一个元素。唯一的要求是每个“列”具有相同数量的行。因此,您将得到一个带有三个“列”的数据框:第三个“列”只是一个矩阵!

使用 aggregate 可以做到这一点,但是还有更方便的工具。(例如,您可以调用 cbind(allquartile[,1:2],allquartile[,3]) 来创建一个“正确”维度的数据框。)

例如,一个非常受欢迎的工具是 plyr 包中的 ddply。下面是一个使用一些虚构数据的示例,但遵循您数据的一般结构:

topicquality <- data.frame(score = runif(20),
                            T = rep(letters[1:2],each = 10),
                            method = rep(letters[3:4],length.out = 20))

ddply(topicquality,.(T,method),FUN = function(x,...){quantile(x$score,...)},probs = seq(0,1,0.25))

你会注意到这将返回一个你期望的数据框,但你仍然需要处理不方便的列名。最好在你应用于每个部分的函数中处理它们:

myQuantile <- function(x,...){
    tmp <- quantile(x,...)
    names(tmp) <- NULL #Or something else convenient
    tmp
}
ddply(topicquality,.(T,method),FUN = myQuantile,probs = seq(0,1,0.25))

这很有道理。我只是使用聚合函数,因为其他人已经编写了它来绘制像平均值、中位数和最大值这样的简单图形。我已经通过聚合和矩阵操作使事情按照上述所述工作,但我会尝试给这个一个机会。我发现我的第三个“列”是一个矩阵,这让我感到相当奇怪。 - fozziethebeat

0

以下是解决方法。问题出在您的列名上。如果您键入names(allquartile),您会发现您的列名为s.0.s.25.等。我的建议是避免在列名中使用所有标点符号,除了_.

require(stringr)
names(allquartile) = str_replace_all(names(allquartile), "\\.", '')
p <- ggplot(allquartile2, aes_string(x = "T", ymin = "s0", lower = "s25", 
      middle = "s50", upper = "s75", ymax = "s100", color = "method")) + 
     geom_boxplot(stat = "identity")

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