在一个分面的ggplot条形图中,如何在y轴上显示百分比?

35
在ggplot中进行facets时,我通常希望使用百分比而不是计数。
例如:
test1 <- sample(letters[1:2], 100, replace=T)
test2 <- sample(letters[3:8], 100, replace=T)
test <- data.frame(cbind(test1,test2))
ggplot(test, aes(test2))+geom_bar()+facet_grid(~test1)

这很容易,但如果在A面和B面中N不同,最好是比较百分比,以使每个面总和为100%。

您如何实现这一点?

希望我的问题有意义。

真诚地。

6个回答

55

这是一个使用ggplot方法内部的示例,使用..count....PANEL..

ggplot(test, aes(test2)) + 
    geom_bar(aes(y = (..count..)/tapply(..count..,..PANEL..,sum)[..PANEL..])) + 
    facet_grid(~test1)

由于这是动态计算的,因此它应该对绘图参数的更改具有鲁棒性。


这是一个很好的方法。您认为在每个面板中添加总和为100%的百分比标签对于每个条形图是否可行? - marbel
@MartínBel 看起来 geom_text 无法与计算变量配合使用。您可能想要将其作为一个单独的问题发布。 - James
2
当然。这是我留下的问题,以备将来参考。 - marbel
非常有帮助。非常感谢。 - hyunwoo jeong
如果使用stat='identity',那么 ..y../tapply(..y.., ..PANEL.., sum)[..PANEL..] 就可以工作。 - krassowski

21

试一下这个:

# first make a dataframe with frequencies
df <- as.data.frame(with(test, table(test1,test2)))
# or with count() from plyr package as Hadley suggested
df <- count(test, vars=c('test1', 'test2'))
# next: compute percentages per group
df <- ddply(df, .(test1), transform, p = Freq/sum(Freq))
# and plot
ggplot(df, aes(test2, p))+geom_bar()+facet_grid(~test1)

alt text

对于ggplot2版本0.8.9,您还可以添加+ scale_y_continuous(formatter = "percent")到图形中,或者对于版本0.9.0,您可以使用+ scale_y_continuous(labels = percent_format())


这是一个更好的解决方案。+1 - Chase
@Chase和@Andreas:谢谢你们!我刚刚发布了一个更简单(我认为更好)的方法,基于这个问题:https://dev59.com/pXA65IYBdhLWcg3wrQh4 - daroczig
2
尝试使用count而不是as.data.frame(table(...)) - 它更快,而且不会将所有制表变量转换为因子。 - hadley
@hadley:感谢您指出这个有用的函数。我已经记下来了,以备将来之需! - daroczig
1
对于ggplot2 v.1.0.1,最后一部分应该是:+ scale_y_continuous(labels = percent) - Owen

8
一个非常简单的方法:
ggplot(test, aes(test2)) + 
    geom_bar(aes(y = (..count..)/sum(..count..))) + 
    facet_grid(~test1)

所以我只改变了geom_bar的参数为aes(y = (..count..)/sum(..count..))。 将ylab设置为NULL并指定格式化程序,您可以获得:

ggplot(test, aes(test2)) +
    geom_bar(aes(y = (..count..)/sum(..count..))) + 
    facet_grid(~test1) +
    scale_y_continuous('', formatter="percent")

更新请注意,虽然formatter = "percent"适用于ggplot2版本0.8.9,在0.9.0中,您需要使用scale_y_continuous(labels = percent_format())

alt text

4
实际上是使用scales包中的scale_y_continuous(labels = percent)函数进行y轴标签的设置。 - dickoa
有人能帮我理解上述语句中 (..count..)/sum(..count..) 的含义吗? - Abhi
1
@Abhi - 这是一个内部的ggplot2函数:“要在美学映射中使用这些变量,您需要将它们用..括起来,例如aes(x = ..output..)。这告诉ggplot该变量不是原始数据集,而是由统计数据创建的。”请参见:http://had.co.nz/ggplot2/stat_sum.html - Andreas
6
在ggplot2的0.9.3版本中,这种方法行不通。它不是将每个小分面相加到100%,而是将所有小分面相加到100%。 - Sim
2
我的先前评论是不正确的。我发现了一个非常奇怪的行为:从一个新的R会话开始,这个可以工作。但是如果在加载了许多库的项目中启动,则无法正常工作。它不是将每个方面相加到100%,而是将所有方面相加到100%。看起来像是ggplot2中的一个错误——某些东西被搞混了。 - Sim
显示剩余2条评论

1

这里有一个解决方案,应该可以让你朝着正确的方向前进。我很好奇是否有更有效的方法来完成这个任务,因为这似乎有点繁琐和复杂。我们可以使用内置的..density..参数来处理y aesthetic,但是因子在那里不起作用。所以我们还需要使用scale_x_discrete来适当地标记轴,一旦我们将test2转换为数字对象。

ggplot(data = test, aes(x = as.numeric(test2)))+ 
geom_bar(aes(y = ..density..), binwidth = .5)+ 
scale_x_discrete(limits = sort(unique(test$test2))) + 
facet_grid(~test1) + xlab("Test 2") + ylab("Density") 

不过试试这个,然后告诉我你的想法。

另外,你可以像这样简化你的测试数据创建,避免在环境中添加额外的对象和将它们绑定在一起:

test <- data.frame(
    test1 = sample(letters[1:2], 100, replace = TRUE), 
    test2 = sample(letters[3:8], 100, replace = TRUE)
)

1
是的,有点复杂,但还是谢谢,比我之前的好多了 :-) 我不知道这是否应该成为ggplot的一个功能。我可以想象很多情况下它会比绘制计数更好。另一方面,最好将数据处理和图形分开处理 :-) - Andreas
密度与百分比不同。 - russellpierce

1
感谢您在ggplot方法中分享PANEL“提示”。
信息:您可以使用ggplot方法中的countgroup来在同一条形图上生成百分比。y lab
ggplot(test, aes(test2,fill=test1))
   + geom_bar(aes(y = (..count..)/tapply(..count..,..group..,sum)[..group..]), position="dodge")
   + scale_y_continuous(labels = percent)

虽然其他人可能认为这更适合作为评论而不是答案,但我给了这个答案一个+1,因为它帮助我解决了一个问题,并想感谢Lilly发布这个答案。 - paleo13

0

我经常处理类似的情况,但采用了与 Hadley 的其他两个包(即 reshape 和 plyr)完全不同的方法。主要是因为我更喜欢将事物呈现为100%堆叠条形图(当它们总计为100%时)。

test <- data.frame(sample(letters[1:2], 100, replace=T), sample(letters[3:8], 100, replace=T))
colnames(test) <- c("variable","value")
test <- cast(test, variable + value ~ .) 
colnames(test)[3] <- "frequ"

test <- ddply(test,"variable", function(x) {
    x <- x[order(x$value),]
    x$cfreq <- cumsum(x$frequ)/sum(x$frequ)
    x$pos <- (c(0,x$cfreq[-nrow(x)])+x$cfreq)/2
    x$freq <- (x$frequ)/sum(x$frequ)
    x
})

plot.tmp <- ggplot(test, aes(variable,frequ, fill=value)) + geom_bar(stat="identity", position="fill") + coord_flip() + scale_y_continuous("", formatter="percent")

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