R ggplot2直方图叠加,每个直方图都有归一化值

3

我希望创建一个直方图,比较三组数据。但是,我想通过每组内的总计数来标准化每个直方图,而不是通过总计数来标准化。下面是我的代码。

library(ggplot2)
library(reshape2)
# Creates dataset
set.seed(9)
df<- data.frame(values = c(runif(400,20,50),runif(300,40,80),runif(600,0,30)),labels = c(rep("med",400),rep("high",300),rep("low",600)))

levs <- c("low", "med", "high")
df$labels <- factor(df$labels, levels = levs)

ggplot(df, aes(x=values, fill=labels)) + 
    geom_histogram(aes(y=..density..), 
                   breaks= seq(0, 80, by = 2),
                   alpha=0.2, 
                   position="identity")

生成一个直方图,看起来是按密度归一化的。 enter image description here

然而,我决定用手动验证密度的方法交叉检查这个密度图。为了做到这一点,我使用了以下代码:

# Separates the low medium and high groups
df1 <- df[df$labels == "low",]
df2 <- df[df$labels == "med",]
df3 <- df[df$labels == "high",]

# creates histogram for each group that is normalized by the total number of counts
hist_temp <- hist(df1$values, breaks=seq(0,80, by=2))
    tdf <- data.frame(hist_temp$breaks[2:length(hist_temp$breaks)],hist_temp$counts)
    colnames(tdf) <- c("bins","counts")
    tdf$norm <- tdf$counts/(sum(tdf$counts))
        low1 <- tdf

hist_temp <- hist(df2$values, breaks=seq(0,80, by=2))
    tdf <- data.frame(hist_temp$breaks[2:length(hist_temp$breaks)],hist_temp$counts)
    colnames(tdf) <- c("bins","counts")
    tdf$norm <- tdf$counts/(sum(tdf$counts))
        med1 <- tdf

hist_temp <- hist(df3$values, breaks=seq(0,80, by=2))
    tdf <- data.frame(hist_temp$breaks[2:length(hist_temp$breaks)],hist_temp$counts)
    colnames(tdf) <- c("bins","counts")
    tdf$norm <- tdf$counts/(sum(tdf$counts))
        high1 <- tdf

# Combines normalized histograms for each data frame and melts them into a single vector for plotting
Tdata <- data.frame(low1$bins,low1$norm,med1$norm,high1$norm)
    colnames(Tdata) <- c("bin","low", "med", "high")
    Tdata<- melt(Tdata,id = "bin")

levs <- c("low", "med", "high")
Tdata$variable <- factor(Tdata$variable, levels = levs)

# Plot the data
ggplot(Tdata, aes(group=variable, colour= variable)) + 
    geom_line(aes(x = bin, y = value))

生成的图像如下所示:

enter image description here

可以看到,这两个图像非常不同,我无法弄清楚为什么。 Y轴应该是相同的,但事实并非如此。因此,假设我没有犯任何愚蠢的数学错误,我认为希望直方图看起来像线图,但我无法找到一种方法实现这一点。任何帮助都将不胜感激,谢谢。


编辑以添加进一步的不起作用的示例:

我还尝试使用代码中的..count../(sum(..count..))方法:

# Histogram where each histogram is divided by the total count of all groups    
    ggplot(df, aes(x=values, fill=labels, group=labels)) + 
        geom_histogram(aes(y=(..count../sum(..count..))), 
                       breaks= seq(0, 80, by = 2),
                       alpha=0.2, 
                       position="identity")

根据这些结果: enter image description here 这只是将所有直方图的总计数归一化。这也不能反映出我在线图中看到的情况。此外,我尝试在分子、分母和分子和分母中替换..count..为..ncount..,但也无法重新创建线图中显示的结果。
此外,我尝试使用“position=stack”而不是使用下面的标识代码:
    ggplot(df, aes(x=values, fill=labels, group=labels)) + 
        geom_histogram(aes(y=..density..), 
                       breaks= seq(0, 80, by = 2),
                       alpha=0.2, 
                       position="stack")

我得到了以下结果: enter image description here 但是这并不反映折线图中显示的值。
取得了进展!使用Joran在这篇帖子中概述的方法,我现在可以生成与折线图相同的直方图。以下是代码:
# Plot where each histogram is normalized by its own counts.  
ggplot(df,aes(x=values, fill=labels, group=labels)) + 
    geom_histogram(data=subset(df, labels == 'high'),
                   aes(y=(..count../sum(..count..))), 
                   breaks= seq(0, 80, by = 2),
                   alpha = 0.2) + 
    geom_histogram(data=subset(df, labels == 'med'),
                   aes(y=(..count../sum(..count..))), 
                   breaks= seq(0, 80, by = 2),
                   alpha = 0.2) +
    geom_histogram(data=subset(df, labels == 'low'),
                   aes(y=(..count../sum(..count..))), 
                   breaks= seq(0, 80, by = 2),
                   alpha = 0.2) +
    scale_fill_manual(values = c("blue","red","green"))

这将生成以下图表: enter image description here 然而,我仍然无法重新排列数据,以便图例按照“低”,“中”,“高”的顺序显示,而不是按字母顺序显示。我已经设置了因子的级别。(请参见第一个代码块)。 有什么想法吗?

在第一个块中,您使用了 y = ..density..,我猜这是考虑到概率密度。尝试添加 group = labels 并改用 y = ..count../sum(..count..)。 - Vitor Bianchi Lanzetta
谢谢您的回复。不幸的是,这似乎并不是我正在寻找的线图。我已经添加了上面的行后面的结果以显示它产生的内容。基本上,..count../sum(..count..) 只在您拥有一个单一的直方图时有效。当您拥有多个直方图时,sum(..count..) 会除以所有直方图的总和,并给出过低的分数。 - Nathan
如果是这种情况,我有一个解决方案,虽然不够优雅但可以解决问题。按标签过滤您的数据框,并使用3层而不是1层。 - Vitor Bianchi Lanzetta
@VitorBianchiLanzetta 谢谢您的评论。看起来确实可以用。如果可能的话,我仍然希望有更优雅的解决方案。这似乎不是太奇怪的情节。也许我会花时间编辑 ggplot2 的源代码并将其提交给开发人员以供他们包含。 - Nathan
我有一种感觉,也许 foreach 是正确的方法,但是现在我没有时间去处理它。你应该试试看:D - Vitor Bianchi Lanzetta
我花了很长时间才解决这个问题,终于有人理解并停止简单地建议使用“..density..”!作为来自Python世界的程序员,这真的不应该那么难。感谢您提供的解决方案! - Q-man
1个回答

0

要为每个类别使用计数,也许可以使用position="stack"

ggplot(df, aes(x=values, fill=labels)) + 
  geom_histogram(aes(y=..density..), 
                 breaks= seq(0, 80, by = 2),
                 alpha=0.4, 
                 position="stack") +
  geom_density(alpha=.2, position="stack")

它给了我这个distribution,但似乎仍然与你的第二个图不同。


谢谢回复。我已经将代码添加到我的帖子中,作为另一种似乎不起作用的方法。睡了一晚上后,再次查看线图代码,我仍然看不出我可能做错了什么。这似乎也很疯狂,我可以产生半打不同的直方图,所有这些直方图都以不同的方式进行归一化,但没有一个是对我来说最有意义的方式。 - Nathan

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