ggplot2箱线图:中位数的水平线?

4
我想通过在中位数处添加一个粗条来使ggplot2箱线图更有意义(这样,如果中位数等于下四分位数或上四分位数中的任何一个,就可以检测到它等于哪一个)。我看到了Kohske最近的一篇文章: Can I get boxplot notches in ggplot2? 但我不知道如何给“交叉条”添加“高度”。然后我试着用一个矩形,但也没有成功。这是一个最简示例:
require(ggplot2) 
require(reshape2) 
require(plyr) 
set.seed(1) 
## parameters 
p1 <- c(5, 20, 100) 
p2 <- c("f1", "f2", "f3", "f4", "f5") 
p3 <- c("g1","g2","g3","g4","g5") 
N <- 1000 
## lengths 
l1 <- length(p1) 
l2 <- length(p2) 
l3 <- length(p3) 
## build result array containing the measurements 
arr <- array(rep(NA, l1*l2*l3*N), dim=c(l1, l2, l3, N), 
         dimnames=list( 
         p1=p1, 
         p2=p2, 
         p3=p3, 
         N=1:N)) 
for(i in 1:l1){ 
    for(j in 1:l2){ 
        for(k in 1:l3){ 
            arr[i,j,k,] <- i+j+k+runif(N, min=-4, max=4) 
        } 
    } 
} 

arr <- arr + rexp(3*5*5*N) 
## create molten data 
mdf <- melt(arr, formula = . ~ p1 + p2 + p3 + N) # create molten data frame 
## confidence interval calculated by `boxplot.stats` 
f <- function(x){ 
    ans <- boxplot.stats(x) 
    data.frame(x=x, y=ans$stats[3], ymin=ans$conf[1], ymax=ans$conf[2]) 
} 

## (my poor) trial 
ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) + 
stat_summary(fun.data=f, geom="rectangle", colour=NA, fill="black", 
xmin=x-0.36, xmax=x+0.36, ymin=max(y-0.2, ymin), ymax=min(y+0.2, 
ymax)) + facet_grid(p2 ~ p1, scales = "free_y") 


**SOLUTION** (after the discussion with Kohske below):
f <- function(x, height){
    ans <- median(x)
    data.frame(y=ans, ymin=ans-height/2, ymax=ans+height/2)
}
p <- ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) +
stat_summary(fun.data=f, geom="crossbar", height=0.5, colour=NA,
         fill="black", width=0.78) +
facet_grid(p2 ~ p1, scales = "free_y")
pdf()
print(p)
dev.off()

**UPDATE** Hmmm... it's not that trivial. The following example shows that the "height" of the crossbar should be adapted to the y-axis scale, otherwise it might be overseen.

require(ggplot2)
require(reshape2)
require(plyr)
set.seed(1)
## parameters
p1 <- c(5, 20, 100)
p2 <- c("f1", "f2", "f3", "f4", "f5")
p3 <- c("g1","g2","g3","g4","g5")
N <- 1000
## lengths
l1 <- length(p1)
l2 <- length(p2)
l3 <- length(p3)
## build result array containing the measurements
arr <- array(rep(NA, l1*l2*l3*N), dim=c(l1, l2, l3, N),
     dimnames=list(
     p1=p1,
     p2=p2,
     p3=p3,
     N=1:N))
for(i in 1:l1){
    for(j in 1:l2){
        for(k in 1:l3){
            arr[i,j,k,] <- i+j^4+k+runif(N, min=-4, max=4)
        }
    } 
}
arr <- arr + rexp(3*5*5*N)
arr[1,2,5,] <- arr[1,2,5,]+30
arr[1,5,3,] <- arr[1,5,3,]+100

## create molten data
mdf <- melt(arr, formula = . ~ p1 + p2 + p3 + N) # create molten data frame

f <- function(x, height){
    ans <- median(x)
    data.frame(y=ans, ymin=ans-height/2, ymax=ans+height/2)
}

## plot
p <- ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) +
stat_summary(fun.data=f, geom="crossbar", height=0.7, colour=NA,
         fill="black", width=0.78) +
facet_grid(p2 ~ p1, scales = "free_y")
pdf()
print(p)
dev.off()

不是那么简单,更重要的是,它无法再现。请尝试在干净的会话中运行此代码 - x 的值找不到。 - Andrie
它是可以再现的。我应该这样说:它没有工作(正是由于这个原因)。如果它能工作,我就不会问了 :-) - Marius Hofert
我特别不明白为什么在函数f中,我明确在data.frame中返回x,但它却找不到。 - Marius Hofert
而且,您在箱线图中看到的水平线只是中位数。 - kohske
以上的基础箱线图相当困难(这就是为什么我使用ggplot2 :-)),但您可以通过查看boxplot(count ~ spray, data = InsectSprays)(这是?boxplot中的第一个示例)来完全理解我的意思。您可以清楚地看到,中位数由比下四分位数和上四分位数更粗的水平线表示(或者整个箱子,如果您愿意)。如果您查看组“D”,则会发现在这种情况下,中位数等于上四分位数。如果中位数没有更粗的线,则无法将其与中位数等于下四分位数的情况区分开来。 - Marius Hofert
显示剩余6条评论
1个回答

7

以下是一个例子:

f <- function(x, height) {
 ans <- median(x)
 data.frame(ymin = ans-height/2, ymax = ans+height/2, y = ans)
}

df <- data.frame(x=gl(2,6), y=c(1,1,1,1,3,3, 1,1,3,3,3,3))
ggplot(df, aes(x, y)) + geom_boxplot() + 
 stat_summary(fun.data = f, geom = "crossbar", height = 0.1,
  colour = NA, fill = "skyblue", width = 0.8, alpha = 0.5)

输入图像描述

如果你只想改变外观,那么这是一个快速的hack,但我不建议这样做。

df <- data.frame(x=gl(2,6), y=c(c(1,1,1,1,3,3), c(1,1,3,3,3,3)*10))
ggplot(df, aes(x, y)) + geom_boxplot() + facet_grid(x~.)

gs <- grid.gget("geom_boxplot", grep = T)
if (inherits(gs, "grob")) gs <- list(gs)
gss <- llply(gs, function(g) g$children[[length(g$children)]])

l_ply(gss, function(g) grid.edit(g$name, grep=T, just = c("left", "center"), height = unit(0.05, "native"), gp = gpar(fill = "skyblue", alpha = 0.5, col = NA)))

enter image description here


“height”被传递给了“f”,而不是横杆。我修改了“f”,使其使用指定的“height”绘制中位数。 - kohske
我应该如何获取更新版本呢?我总是在从Github仓库安装时遇到麻烦...是简单地使用install_github("ggplot2")命令,还是需要指定一个分支呢...? - Marius Hofert
关于箱线图的最后一点评论:我一直在想为什么ggplot2默认不给出whiskers。中位数和四分位数用水平线显示。如果也用水平线标示whiskers的端点,那么眼睛就更容易检测到它们了,对吧?这是有原因的吗?从排版的角度来看,“传统”的方式更可取。 - Marius Hofert
亲爱的Kohske,注意上面的“UPDATE”。正如您所看到的,指定固定(即与行列无关)高度可能不合适。在第2行和第5行,条形图不够粗,而在第一行则有点太粗了...您知道解决方法吗? - Marius Hofert
@MariusHofert 答案已更新。我不建议使用这种方式。不能确定在未来的版本中是否有效。此外,我建议您提供最小数据集的小型示例。 - kohske
显示剩余2条评论

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