Подтвердить что ты не робот

Ggplot2: гистограмма с нормальной кривой

Я пытаюсь наложить нормальную кривую на мою гистограмму с помощью ggplot 2.

Моя формула:

data <- read.csv (path...)

ggplot(data, aes(V2)) + 
  geom_histogram(alpha=0.3, fill='white', colour='black', binwidth=.04)

Я пробовал несколько вещей:

+ stat_function(fun=dnorm)  

.... ничего не изменил

+ stat_density(geom = "line", colour = "red")

... дал мне прямую красную линию по оси x.

+ geom_density()  

не работает для меня, потому что я хочу сохранить свои значения частоты по оси y и не хочу значений плотности.

Любые предложения?

Заранее благодарим за любые советы!

Решение найдено!

+geom_density(aes(y=0.045*..count..), colour="black", adjust=4)

4b9b3361

Ответ 1

Думаю, я понял:

set.seed(1)
df <- data.frame(PF = 10*rnorm(1000))
ggplot(df, aes(x = PF)) + 
    geom_histogram(aes(y =..density..),
                   breaks = seq(-50, 50, by = 10), 
                   colour = "black", 
                   fill = "white") +
stat_function(fun = dnorm, args = list(mean = mean(df$PF), sd = sd(df$PF)))

enter image description here

Ответ 2

На этот вопрос ответили здесь и частично здесь.

Площадь под кривой плотности равна 1, а площадь под гистограммой равна ширине столбцов, умноженной на их высоту, т.е. ширина полосы умножается на общее количество не пропущенных наблюдений. Чтобы разместить оба элемента на одном и том же графике, необходимо изменить масштаб одного или другого, чтобы их области совпадали.

Если вы хотите, чтобы ось Y имела частоту отсчетов, есть несколько вариантов:

Сначала смоделируйте некоторые данные.

library(ggplot2)

set.seed(1)
dat_hist <- data.frame(
  group = c(rep("A", 200), rep("B",150)),
  value = c(rnorm(200, 20, 5), rnorm(150,25,10)))

# Set desired binwidth and number of non-missing obs
bw = 2
n_obs = sum(!is.na(dat_hist$value))

Вариант 1. Отобразите как гистограмму, так и кривую плотности как плотность, а затем измените масштаб оси y.

Это, пожалуй, самый простой подход для одной гистограммы. Используя подход, предложенный Карлосом, постройте гистограмму и кривую плотности как плотность

g <- ggplot(dat_hist, aes(value))  + 
geom_histogram(aes(y = ..density..), binwidth = bw, colour = "black") + 
stat_function(fun = dnorm, args = list(mean = mean(dat_hist$value), sd = sd(dat_hist$value)))

А затем измените масштаб оси Y.

ybreaks = seq(0,50,5) 
## On primary axis
g + scale_y_continuous("Counts", breaks = round(ybreaks / (bw * n_obs),3), labels = ybreaks)

## Or on secondary axis
g + scale_y_continuous("Density", sec.axis = sec_axis(
  trans = ~ . * bw * n_obs, name = "Counts", breaks = ybreaks))

Single histogram with normal curve

Вариант 2. Масштабируйте кривую плотности с помощью функции stat_function

Код приведен в соответствие с ответом PatrickT.

ggplot(dat_hist, aes(value))  + 
  geom_histogram(colour = "black", binwidth = bw) + 
  stat_function(fun = function(x) 
    dnorm(x, mean = mean(dat_hist$value), sd = sd(dat_hist$value)) * bw * n_obs)

Вариант 3. Создание внешнего набора данных и графика с использованием geom_line.

В отличие от вышеуказанных опций, этот работает с фасетами. (ИЗМЕНЕНО для предоставления решения dplyr, а не plyr). Обратите внимание, что суммарный набор данных используется в качестве основного, а необработанные данные передаются только для гистограммы.

library(tidyverse)

dat_hist %>% 
  group_by(group) %>% 
  nest(value) %>% 
  mutate(y = map(data, ~ dnorm(
    .$value, mean = mean(.$value), sd = sd(.$value)
    ) * bw * sum(!is.na(.$value)))) %>% 
  unnest(data,y) %>% 

  ggplot(aes(x = value)) +
  geom_histogram(data = dat_hist, binwidth = bw, colour = "black") +
  geom_line(aes(y = y)) + 
  facet_wrap(~ group)

Histogram with normal curve and facets

Вариант 4. Создание внешних функций для редактирования данных на лету

Может быть, немного сверх того, но может быть кому-то полезно?

## Function to create scaled dnorm data along full x axis range
dnorm_scaled <- function(data, x = NULL, binwidth = 1, xlim = NULL) {
  .x <- na.omit(data[,x])
  if(is.null(xlim))
    xlim = c(min(.x), max(.x))
  x_range = seq(xlim[1], xlim[2], length.out = 101)
  setNames(
    data.frame(
    x = x_range,
    y = dnorm(x_range, mean = mean(.x), sd = sd(.x)) * length(.x) * binwidth),
    c(x, "y"))
}

## Function to apply over groups
dnorm_scaled_group <- function(data, x = NULL, group = NULL, binwidth = NULL, xlim = NULL) {
  dat_hists <- lapply(
    split(data, data[, group]), dnorm_scaled,
      x = x, binwidth = binwidth, xlim = xlim)
  for(g in names(dat_hists))
    dat_hists[[g]][, "group"] <- g
  setNames(do.call(rbind, dat_hists), c(x, "y", group))
}

## Single histogram
ggplot(dat_hist, aes(value)) + 
  geom_histogram(binwidth = bw, colour = "black") + 
  geom_line(data = ~ dnorm_scaled(., "value", binwidth = bw), 
            aes(y = y)) 

## With a single faceting variable
ggplot(dat_hist, aes(value))  + 
  geom_histogram(binwidth = 2, colour = "black") + 
  geom_line(data = ~ dnorm_scaled_group(
    ., x = "value", group = "group", binwidth = 2, xlim = c(0,50)), 
    aes(y = y)) +
  facet_wrap(~ group)

Ответ 3

Это расширенный комментарий к ответу Дж. Виллимана. Я нашел ответ J очень полезным. Во время игры я обнаружил способ упростить код. Я не говорю, что это лучший способ, но я думал, что упомяну это.

Обратите внимание, что ответ JWilliman предоставляет счет на оси Y и "хак" для масштабирования соответствующего приближения нормальной плотности (которое в противном случае охватило бы общую площадь 1 и, следовательно, имело бы намного более низкий пик).

Основной смысл этого комментария: упрощенный синтаксис внутри stat_function, путем передачи необходимых параметров в функцию эстетики, например,

aes(x = x, mean = 0, sd = 1, binwidth = 0.3, n = 1000)

Это позволяет избежать необходимости передавать args = в stat_function и, следовательно, более удобно для пользователя. Ладно, это не сильно отличается, но, надеюсь, кто-то найдет это интересным.

# parameters that will be passed to ''stat_function''
n = 1000
mean = 0
sd = 1
binwidth = 0.3 # passed to geom_histogram and stat_function
set.seed(1)
df <- data.frame(x = rnorm(n, mean, sd))

ggplot(df, aes(x = x, mean = mean, sd = sd, binwidth = binwidth, n = n)) +
    theme_bw() +
    geom_histogram(binwidth = binwidth, 
        colour = "white", fill = "cornflowerblue", size = 0.1) +
stat_function(fun = function(x) dnorm(x, mean = mean, sd = sd) * n * binwidth,
    color = "darkred", size = 1)

enter image description here

Ответ 4

Этот код должен сделать это:

set.seed(1)
z <- rnorm(1000)

qplot(z, geom = "blank") + 
geom_histogram(aes(y = ..density..)) + 
stat_density(geom = "line", aes(colour = "bla")) + 
stat_function(fun = dnorm, aes(x = z, colour = "blabla")) + 
scale_colour_manual(name = "", values = c("red", "green"), 
                               breaks = c("bla", "blabla"), 
                               labels = c("kernel_est", "norm_curv")) + 
theme(legend.position = "bottom", legend.direction = "horizontal")

введите описание изображения здесь

Примечание. Я использовал qplot, но вы можете использовать более универсальный ggplot.