geom_violin с использованием веса aestheti c неожиданно падение уровня - PullRequest
2 голосов
/ 05 марта 2020
library(tidyverse)
set.seed(12345)

dat <- data.frame(year = c(rep(1990, 100), rep(1991, 100), rep(1992, 100)),
                  fish_length = sample(x = seq(from = 10, 131, by = 0.1), 300, replace = F),
                  nb_caught = sample(x = seq(from = 1, 200, by = 0.1), 300, replace = T), 
                  stringsAsFactors = F) %>% 
       mutate(age = ifelse(fish_length < 20, 1,
                           ifelse(fish_length >= 20 & fish_length < 100, 2, 
                                  ifelse(fish_length >= 100 & fish_length < 130, 3, 4)))) %>% 
       arrange(year, fish_length) 

head(dat)
  year fish_length nb_caught age
1 1990        10.1      45.2   1
2 1990        10.7     170.0   1
3 1990        10.9      62.0   1
4 1990        12.1     136.0   1
5 1990        14.1      80.8   1
6 1990        15.0     188.9   1

dat %>% group_by(year) %>% summarise(ages = n_distinct(age)) # Only 1992 has age 4 fish
# A tibble: 3 x 2
   year  ages
  <dbl> <int>
1  1990     3
2  1991     3
3  1992     4

dat %>% filter(age == 4) # only 1 row for age 4
  year fish_length nb_caught age
1 1992       130.8      89.2   4

Здесь:

  • год = год выборки
  • fish_length = длина фи sh в см
  • nb_caught = число чисел sh, пойманных после использования ключа длины по возрасту, что объясняет наличие десятичных знаков
  • возраст = возраст фи sh

graph1: geom_violin без использования веса эстет c.

Здесь я должен скопировать каждая строка dat в соответствии со значением, найденным в nb_caught .

dim(dat) # 300 rows
dat_graph1 <- dat[rep(1:nrow(dat), floor(dat$nb_caught)), ]
dim(dat_graph1) # 30932 rows
dat_graph1$nb_caught <- NULL # useless now
sum(dat$nb_caught) - nrow(dat_graph1) # 128.2 rows lost here

Поскольку у меня есть десятичные значения nb_caught , я взял целочисленное значение для создания dat_graph1. Я потерял 128,2 "строк" в процессе.

Теперь для графика:

dat_tile <- data.frame(year = sort(unique(dat$year))[sort(unique(dat$year)) %% 2 == 0]) 
# for the figure's background

graph1 <- ggplot(data = dat_graph1, 
                 aes(x = as.factor(year), y = fish_length, fill = as.factor(age), 
                     color = as.factor(age), .drop = F)) +
          geom_tile(data = dat_tile, aes(x = factor(year), y = 1, height = Inf, width = 1),
                    fill = "grey80", inherit.aes = F) +
          geom_violin(draw_quantiles = c(0.05, 0.5, 0.95), color = "black",
                      scale = "width", position = "dodge") + 
          scale_x_discrete(expand = c(0,0)) +
          labs(x = "Year", y = "Fish length", fill = "Age", color = "Age", title = "graph1") +
          scale_fill_brewer(palette = "Paired", drop = F) + # drop = F for not losing levels
          scale_color_brewer(palette = "Paired", drop = F) + # drop = F for not losing levels
          scale_y_continuous(expand = expand_scale(mult = 0.01)) +
          theme_bw()

graph1

graph1

Обратите внимание, что у меня есть квартира бар для возраст 4 в год 1992.

dat_graph1 %>% filter(year == 1992, age == 4) %>% pull(fish_length) %>% unique
[1] 130.8

Это потому, что у меня есть только одна длина для этой конкретной год-возраста .

graph2: geom_violin с использованием веса aestheti c.

Теперь вместо копирования каждой строки dat значением number_caught , давайте используем вес aestheti c.

Давайте вычислим вес wt , который будет иметь каждая строка dat при расчете кривой плотности каждого год-возраст комбинаций.

dat_graph2 <- dat %>%
              group_by(year, age) %>%
              mutate(wt = nb_caught / sum(nb_caught)) %>%
              as.data.frame()
head(dat_graph2)
  year fish_length nb_caught age         wt
1 1990        10.1      45.2   1 0.03573123
2 1990        10.7     170.0   1 0.13438735
3 1990        10.9      62.0   1 0.04901186
4 1990        12.1     136.0   1 0.10750988
5 1990        14.1      80.8   1 0.06387352
6 1990        15.0     188.9   1 0.14932806

graph2 <- ggplot(data = dat_graph2, 
                 aes(x = as.factor(year), y = fish_length, fill = as.factor(age), 
                     color = as.factor(age), .drop = F)) +
          geom_tile(data = dat_tile, aes(x = factor(year), y = 1, height = Inf, width = 1),
                    fill = "grey80", inherit.aes = F) +
          geom_violin(aes(weight = wt), draw_quantiles = c(0.05, 0.5, 0.95), color = "black",
                      scale = "width", position = "dodge") + 
          scale_x_discrete(expand = c(0,0)) +
          labs(x = "Year", y = "Fish length", fill = "Age", color = "Age", title = "graph2") +
          scale_fill_brewer(palette = "Paired", drop = F) + # drop = F for not losing levels
          scale_color_brewer(palette = "Paired", drop = F) + # drop = F for not losing levels
          scale_y_continuous(expand = expand_scale(mult = 0.01)) + 
          theme_bw()

graph2

dat_graph2 %>% filter(year == 1992, age == 4)
  year fish_length nb_caught age wt
1 1992       130.8      89.2   4  1

graph2

Обратите внимание, что полоса для age 4 in year 1992, показанный на графике 1, здесь опущен, даже если в dat_graph2 есть строка.

Мои вопросы

  1. Почему возраст 4 в 1992 году упал при использовании вэй ght aestheti c? Как я могу преодолеть это?
  2. Почему два графика визуально не похожи, даже если они использовали одни и те же данные?

Заранее спасибо за помощь!

1 Ответ

0 голосов
/ 07 марта 2020

1.

Проблема 1 не связана с использованием веса aestheti c. Вы можете проверить это, сбросив вес aestheti c в коде для вашего второго графика. Проблема в том, что алгоритм вычисления плотности не работает, когда слишком мало наблюдений.

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

К сожалению, geom_violin не выдает предупреждение в вашем конкретном случае c. Однако, если вы отфильтруете dat_graph2 для age == 4 geom_violin, вы получите предупреждение

Warning message:
Computation failed in `stat_ydensity()`:
replacement has 1 row, data has 0 

geom_density гораздо яснее в этом вопросе, предупреждая, что группы с менее чем двумя наблюдателями имеют был отброшен.

К сожалению, у меня нет решения для преодоления этого, кроме работы с расширенным набором данных.

2.

Относительно задачи 2 У меня нет убедительного ответа, кроме того, что Я предполагаю, что это связано с деталями оценки плотности ядра, используемой geom_violin, geom_density, ... и, возможно, также как-то связано с количеством точек данных.

...