使用 `facet_wrap` 绘制 ggplot 图时添加正态分布。

4

我想绘制以下直方图:

library(palmerpenguins)
library(tidyverse)

penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram() + 
  facet_wrap(~species)

enter image description here

对于每个直方图,我想为每个物种的平均值和标准差添加一个正态分布。
当然,我知道在开始ggplot命令之前可以计算特定组的平均值和SD,但我想知道是否有更聪明/更快的方法来做到这一点。
我已经尝试过:
penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram() + 
  facet_wrap(~species) + 
  stat_function(fun = dnorm)

但这只会在底部给我一条细线:

enter image description here

任何想法吗? 谢谢!
编辑 我想要重新创建的是来自Stata的这个简单命令: hist bill_length_mm, by(species) normal
它给了我这个: enter image description here 我知道这里有一些建议:在R中使用stat_function和facet_wrap 但我特别寻求一个不需要我创建单独函数的简短答案。

你需要计算这个。尝试手动计算 dnorm(penguins$bill_length_mm) - 你会注意到非常小的数字(大约是-300次方!)。我猜你需要先将它们分组,以便理解那个dnorm调用。四舍五入没有帮助,所以我认为这不仅仅是一个浮点问题。 - tjebo
1
谢谢 - 我会尝试的。 我添加了促使我尝试这个的Stata中的图形。当然,在那里数据被转换为密度。 - Moritz Schwarz
2个回答

6
前段时间我写了一个函数,把理论密度图的绘制自动化了。这个函数现在已经被收录在我编写的ggh4x软件包中,你可能会觉得很方便。你只需要确保直方图和理论密度图具有相同的比例尺(例如每个x轴单位对应的计数)。
library(palmerpenguins)
library(tidyverse)
library(ggh4x)

penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram(binwidth = 1) + 
  stat_theodensity(aes(y = after_stat(count))) +
  facet_wrap(~species)
#> Warning: Removed 2 rows containing non-finite values (stat_bin).

您可以更改直方图的箱子大小,但必须同时调整理论密度数量。通常会乘以箱宽。

penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram(binwidth = 2) + 
  stat_theodensity(aes(y = after_stat(count)*2)) +
  facet_wrap(~species)
#> Warning: Removed 2 rows containing non-finite values (stat_bin).

这是由reprex 包(v0.3.0)在2021-01-27 创建的。

如果这太麻烦,你总可以将直方图转换为密度而不是将密度转换为计数。

penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram(aes(y = after_stat(density))) + 
  stat_theodensity() +
  facet_wrap(~species)

我有点料到你的ggh4x也有这方面的统计数据 :) - tjebo
2
整个软件包的主要动力是“让我感到沮丧的事情应该更容易些” :) - teunbrand
1
您太好了。我可能应该在某个时候做一下! - teunbrand
绝对同意,非常有用的包。 - Ian Campbell
很抱歉,该问题因与此问题相关而被关闭:https://dev59.com/THM_5IYBdhLWcg3wcSt_ 也许你可以在那里分享你的解决方案! :) - Moritz Schwarz
显示剩余2条评论

4

虽然在这种情况下使用ggh4x包是正确的选择,但更具有普适性的方法是使用tapply函数,并且要利用当分面被应用时会添加到数据中的PANEL变量。

penguins %>% 
  ggplot(aes(x=bill_length_mm, fill = species)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30) + 
  facet_wrap(~species) + 
  geom_line(aes(y = dnorm(bill_length_mm,
                          mean = tapply(bill_length_mm, species, mean, na.rm = TRUE)[PANEL],
                          sd = tapply(bill_length_mm, species, sd, na.rm = TRUE)[PANEL])))

enter image description here


非常感谢您提供这个非常有用的补充。我并不知道PANEL变量,我需要去了解一下!恐怕这个问题被关闭,因为它与这个问题有关:https://dev59.com/THM_5IYBdhLWcg3wcSt_ 也许您也可以在那里分享您的解决方案! :) - Moritz Schwarz
为什么不使用species而使用PANEL呢?PANEL只是没有标签的species - jtr13
这非常有帮助,我学到了关于PANEL和获取面板数据的新方法。请注意,这仅在原始数据中存在观察点时才会放置一个点。对于稀缺数据,曲线将不会平滑。如果您用geom_smooth替换geom_line,则它将不知道分布并且会有负数的尾巴。这很有效,但对于我的应用程序,我还选择了ggh4x。 - Steven Ouellette

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