27

我正在尝试用 ggplot2 绘制格型数据,然后在样本数据上叠加一个正态分布,以说明基础数据的正常程度。我希望顶部的普通 dist 具有与面板相同的均值和标准差。

这是一个例子:

library(ggplot2)

#make some example data
dd<-data.frame(matrix(rnorm(144, mean=2, sd=2),72,2),c(rep("A",24),rep("B",24),rep("C",24)))
colnames(dd) <- c("x_value", "Predicted_value",  "State_CD")

#This works
pg <- ggplot(dd) + geom_density(aes(x=Predicted_value)) +  facet_wrap(~State_CD)
print(pg)

这一切都很好,并产生了一个很好的数据三面板图。如何在顶部添加正常的 dist?看来我会使用 stat_function,但这失败了:

#this fails
pg <- ggplot(dd) + geom_density(aes(x=Predicted_value)) + stat_function(fun=dnorm) +  facet_wrap(~State_CD)
print(pg)

stat_function 似乎与 facet_wrap 功能不兼容。我怎样才能让这两个玩得很好?

- - - - - - 编辑 - - - - -

我试图整合以下两个答案的想法,但我仍然不在那里:

结合使用这两个答案,我可以将其破解:

library(ggplot)
library(plyr)

#make some example data
dd<-data.frame(matrix(rnorm(108, mean=2, sd=2),36,2),c(rep("A",24),rep("B",24),rep("C",24)))
colnames(dd) <- c("x_value", "Predicted_value",  "State_CD")

DevMeanSt <- ddply(dd, c("State_CD"), function(df)mean(df$Predicted_value)) 
colnames(DevMeanSt) <- c("State_CD", "mean")
DevSdSt <- ddply(dd, c("State_CD"), function(df)sd(df$Predicted_value) )
colnames(DevSdSt) <- c("State_CD", "sd")
DevStatsSt <- merge(DevMeanSt, DevSdSt)

pg <- ggplot(dd, aes(x=Predicted_value))
pg <- pg + geom_density()
pg <- pg + stat_function(fun=dnorm, colour='red', args=list(mean=DevStatsSt$mean, sd=DevStatsSt$sd))
pg <- pg + facet_wrap(~State_CD)
print(pg)

这真的很接近......除了正常的 dist 绘图有问题:

在此处输入图像描述

我在这里做错了什么?

4

6 回答 6

41

stat_function旨在在每个面板中覆盖相同的功能。(没有明显的方法可以将函数的参数与不同的面板相匹配)。

正如 Ian 所建议的,最好的方法是自己生成法线曲线,并将它们绘制为一个单独的数据集(这是你之前出错的地方 - 合并对于这个例子没有意义,如果你仔细看你会看到这就是为什么你会得到奇怪的锯齿图案)。

这是我解决问题的方法:

dd <- data.frame(
  predicted = rnorm(72, mean = 2, sd = 2),
  state = rep(c("A", "B", "C"), each = 24)
) 

grid <- with(dd, seq(min(predicted), max(predicted), length = 100))
normaldens <- ddply(dd, "state", function(df) {
  data.frame( 
    predicted = grid,
    density = dnorm(grid, mean(df$predicted), sd(df$predicted))
  )
})

ggplot(dd, aes(predicted))  + 
  geom_density() + 
  geom_line(aes(y = density), data = normaldens, colour = "red") +
  facet_wrap(~ state) 

在此处输入图像描述

于 2009-09-04T12:56:59.330 回答
4

最初作为这个问题的答案发布,我也被鼓励在这里分享我的解决方案。

我也对在经验数据上叠加理论密度感到沮丧,所以我写了一个函数来自动化这个过程。自 2009 年首次提出此问题以来,ggplot2 极大地扩展了可扩展性,因此我将其放在 github 上的扩展包中(编辑:您现在可以在 CRAN 上找到它)。

library(ggplot2)
library(ggh4x)

set.seed(0)

# Make the example data
dd <- data.frame(matrix(rnorm(144, mean=2, sd=2),72,2),
                 c(rep("A",24),rep("B",24),rep("C",24)))
colnames(dd) <- c("x_value", "Predicted_value",  "State_CD")

ggplot(dd, aes(Predicted_value)) +
  geom_density() +
  stat_theodensity(colour = "red") +
  facet_wrap(~ State_CD)

reprex 包(v0.3.0)于 2021 年 1 月 28 日创建

于 2021-01-28T12:06:19.917 回答
3

我认为您需要提供更多信息。这似乎有效:

 pg <- ggplot(dd, aes(Predicted_value)) ## need aesthetics in the ggplot
 pg <- pg + geom_density() 
 ## gotta provide the arguments of the dnorm
 pg <- pg + stat_function(fun=dnorm, colour='red',            
            args=list(mean=mean(dd$Predicted_value), sd=sd(dd$Predicted_value)))
 ## wrap it!
 pg <- pg + facet_wrap(~State_CD)
 pg

我们为每个面板提供相同的均值和 sd 参数。获取特定面板的均值和标准差作为练习留给读者* ;)

'*' 换句话说,不知道怎么做...

于 2009-09-04T03:04:08.707 回答
3

如果你愿意使用 ggformula,那么这很容易。(也可以混合和匹配并使用 ggformula 仅用于分布覆盖,但我将说明完整的 ggformula 方法。)

library(ggformula)
theme_set(theme_bw())

gf_dens( ~ Sepal.Length | Species, data = iris) %>%
  gf_fitdistr(color = "red") %>% 
  gf_fitdistr(dist = "gamma", color = "blue")

reprex 包(v0.2.1)于 2019 年 1 月 15 日创建

于 2019-01-16T02:41:42.957 回答
2

如果您不想“手动”生成正态分布折线图,仍然使用 stat_function,并并排显示图形——那么您可以考虑使用“Cookbook for R”上发布的“multiplot”函数作为 facet_wrap 的替代方案。您可以从此处将多图代码复制到您的项目中。

复制代码后,执行以下操作:

# Some fake data (copied from hadley's answer)
dd <- data.frame(
  predicted = rnorm(72, mean = 2, sd = 2),
  state = rep(c("A", "B", "C"), each = 24)
) 

# Split the data by state, apply a function on each member that converts it into a 
# plot object, and return the result as a vector.
plots <- lapply(split(dd,dd$state),FUN=function(state_slice){ 
  # The code here is the plot code generation. You can do anything you would 
  # normally do for a single plot, such as calling stat_function, and you do this 
  # one slice at a time.
  ggplot(state_slice, aes(predicted)) + 
    geom_density() + 
    stat_function(fun=dnorm, 
                  args=list(mean=mean(state_slice$predicted), 
                            sd=sd(state_slice$predicted)),
                  color="red")
})

# Finally, present the plots on 3 columns.
multiplot(plotlist = plots, cols=3)

在此处输入图像描述

于 2014-12-11T17:53:03.403 回答
1

我认为你最好的选择是用 geom_line 手动画线。

dd<-data.frame(matrix(rnorm(144, mean=2, sd=2),72,2),c(rep("A",24),rep("B",24),rep("C",24)))
colnames(dd) <- c("x_value", "Predicted_value",  "State_CD")
dd$Predicted_value<-dd$Predicted_value*as.numeric(dd$State_CD) #make different by state

##Calculate means and standard deviations by level
means<-as.numeric(by(dd[,2],dd$State_CD,mean))
sds<-as.numeric(by(dd[,2],dd$State_CD,sd))

##Create evenly spaced evaluation points +/- 3 standard deviations away from the mean
dd$vals<-0
for(i in 1:length(levels(dd$State_CD))){
    dd$vals[dd$State_CD==levels(dd$State_CD)[i]]<-seq(from=means[i]-3*sds[i], 
                            to=means[i]+3*sds[i],
                            length.out=sum(dd$State_CD==levels(dd$State_CD)[i]))
}
##Create normal density points
dd$norm<-with(dd,dnorm(vals,means[as.numeric(State_CD)],
                        sds[as.numeric(State_CD)]))


pg <- ggplot(dd, aes(Predicted_value)) 
pg <- pg + geom_density() 
pg <- pg + geom_line(aes(x=vals,y=norm),colour="red") #Add in normal distribution
pg <- pg + facet_wrap(~State_CD,scales="free")
pg
于 2009-09-04T04:41:14.100 回答