在R中使用facet_wrap规范化ggplot2密度图

4

我正在使用geom_density从数据框绘制一系列密度图,并使用facet_wrap按条件展示,如下所示:

ggplot(iris) + geom_density(aes(x=Sepal.Width, colour=Species, y=..count../sum(..count..))) + facet_wrap(~Species)

当我这样做时,y轴标尺似乎不代表每个面板中Species的百分比,而是跨越所有物种的所有数据点的百分比。
我的问题是:如何使geom_density中的..count..变量引用每个面板中每个Species集合中的项目计数,以便virginica的面板具有与“virginica数据点的分数”相对应的y轴?
此外,是否有一种方法可以让ggplot2输出它用于..count..sum(..count..)的值,以便我可以验证它使用的数字?
编辑:我误解了geom_density,即使对于单个Species..count../sum(..count..)也不是百分比。
ggplot(iris[iris$Species == 'virginica',]) + geom_density(aes(x=Sepal.Width, colour=Species, y=..count../sum(..count..))) + facet_wrap(~Species)

因此,我修改后的问题:如何使密度图成为每个条形桶中数据的比例?我必须使用 stat_density 还是 geom_histogram?我只想要 y 轴是数据点的百分比/比例。


@Ricardo:怎么做?新建一个数据框吗? - user248237
@joran:你说得对,ggplot(iris[iris$Species == 'virginica',]) + geom_density(aes(x=Sepal.Width, colour=Species, y=..count../sum(..count..))) + facet_wrap(~Species)也没有给出百分比。所以我会编辑我的问题来澄清,谢谢。 - user248237
@user248237dfsf,我是指先计算您想绘制的值,然后将其用作ggplot的数据参数。 - Ricardo Saporta
1
也许你对密度的概念感到困惑?它下面的面积应该总和为一,但这并不意味着每个点都代表着“总值的比例”。实际上,密度函数经常会超过1。 - joran
@baptise:是的!谢谢!如果你把它作为答案给出,我会接受它。 - user248237
显示剩余5条评论
3个回答

5

很遗憾,您要求ggplot2为每个facet定义单独的y,但在我所知道的范围内,它在语法上无法实现。

因此,针对您在评论线程中提到的“基本上只想要一个直方图”,我建议改用geom_histogram,或者如果您偏爱线条而不是条形图,则使用geom_freqpoly

ggplot(iris, aes(Sepal.Width, ..count..)) + 
  geom_histogram(aes(colour=Species, fill=Species), binwidth=.2) +
  geom_freqpoly(colour="black", binwidth=.2) +
  facet_wrap(~Species)

enter image description here

注意:在上面的示例中,geom_freqpoly与geom_histogram一样有效。我只是将两者都添加到一个图中以提高效率。

希望这可以帮助你。

编辑:好吧,我设法想出了一种快速而简单的方法来获得你想要的结果。它需要安装和加载plyr。提前道歉;从RAM使用方面来看,这可能不是最有效的方法,但它确实起作用。

首先,让我们打开iris(我使用RStudio,所以习惯于在窗口中查看所有对象):

d <- iris

现在,我们可以使用ddply来计算每个唯一测量值所属的个体数量,这将成为您的x轴(这里我使用Sepal.Length而不是Sepal.Width,以便在绘制时能够看到更大的差异,从而获得更多范围)。
new <- ddply(d, c("Species", "Sepal.Length"), summarize, count=length(Sepal.Length))

请注意,ddply会根据引用的变量自动对输出数据框进行排序。
然后我们可以将数据框分成每个独特的条件--在iris数据集中,每个三个物种(如果您处理的是大量数据,则不建议继续创建相同数据框的子集,因为可能会耗尽内存)。
set <- new[which(new$Species%in%"setosa"),]
ver <- new[which(new$Species%in%"versicolor"),]
vgn <- new[which(new$Species%in%"virginica"),]

使用ddply再次计算每个测量值下属于每个物种的个体比例,但是需要分别进行计算。

prop <- rbind(ddply(set, c("Species"), summarize, prop=set$count/sum(set$count)),
              ddply(ver, c("Species"), summarize, prop=ver$count/sum(ver$count)),
              ddply(vgn, c("Species"), summarize, prop=vgn$count/sum(vgn$count)))

然后,我们只需将所有需要的内容放入一个数据集中,并从我们的工作区中删除所有垃圾。
new$prop <- prop$prop
rm(list=ls()[which(!ls()%in%c("new", "d"))])

我们可以按照每个面的比例在y轴上制作图形。请注意,我现在使用geom_line,因为ddply已经自动对您的数据框进行了排序。

ggplot(new, aes(Sepal.Length, prop)) + 
  geom_line(aes(colour=new$Species)) +
  facet_wrap(~Species)

facet_wrap with facet-specific proportions

# let's check our work. each should equal 50
sum(new$count[which(new$Species%in%"setosa")]) 
sum(new$count[which(new$Species%in%"versicolor")]) 
sum(new$count[which(new$Species%in%"versicolor")])

#... and each of these should equal 1
sum(new$prop[which(new$Species%in%"setosa")]) 
sum(new$prop[which(new$Species%in%"versicolor")]) 
sum(new$prop[which(new$Species%in%"versicolor")])

我认为你是对的,它是freqpoly...我以为..密度..做到了,但那仍然是密度而不是分数。 - user248237
是的,虽然freqpoly仍然只提供计数。但是就像我说的,如果你想要一个分数,你实际上正在要求使用每个facet的不同分母计算您的分数y值,我认为在ggplot2中这是不可能的。 - sc_evans
啊,我明白了。虽然这正是我想要的,但每个方面需要不同的分母。我必须手动计算吗?那该怎么做呢? - user248237
我认为是这样,但这可能需要一些创造性地使用 plyr 或 reshape2 包。您需要调整数据框,直到您有一个列的 bin 和另一个列的每个条件成员所属的比例。然后,您可以使用 x=bin、y=proportions 和 group=condition(在鸢尾花数据中为 =="Species")进行绘图。但我时间不多,暂时无法测试... - sc_evans
1
在这个例子中,因为我只是记录了我的因变量的唯一值,所以binwidth是测量精度的直接函数。如果你想使用这种方法获得更宽的箱子,你需要手动指定一个范围并计算落在这个范围内的观测数量。 - sc_evans
显示剩余3条评论

0
也许你可以尝试使用table()和barplot()函数来达到你的目的。但我还不确定这是否符合你的要求...
barplot(table(iris[iris$Species == 'virginica',1]))

使用ggplot2

tb <- table(iris[iris$Species == 'virginica',1])
tb <- as.data.frame(tb)
ggplot(tb, aes(x=Var1, y=Freq)) + geom_bar()

0
将参数scales='free_y'传递给facet_wrap()函数即可解决问题。

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