Использовать gganimate для построения наблюдения гистограммы путем наблюдения? Нужно работать для больших наборов данных (~ n = 5000)

12

Я хотел бы выбрать точки из нормального распределения, а затем построить точечный график один за другим, используя gganimateпакет, пока последний кадр не покажет полный точечный график.

Решение, которое работает для больших наборов данных ~ 5000 - 20000 точек, имеет важное значение.

Вот код, который у меня есть:

library(gganimate)
library(tidyverse)

# Generate 100 normal data points, along an index for each sample 
samples <- rnorm(100)
index <- seq(1:length(samples))

# Put data into a data frame
df <- tibble(value=samples, index=index)

DF выглядит так:

> head(df)
# A tibble: 6 x 2
    value index
    <dbl> <int>
1  0.0818     1
2 -0.311      2
3 -0.966      3
4 -0.615      4
5  0.388      5
6 -1.66       6

Статический график показывает правильный точечный график:

# Create static version
plot <- ggplot(data=df, mapping=aes(x=value))+
          geom_dotplot()

Тем не менее, gganimateверсия не (см. Ниже). Он только ставит точки на оси X и не складывает их.

plot+
  transition_reveal(along=index)

Статический сюжет

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

Нечто подобное было бы идеально: Кредит: https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1 введите описание изображения здесь

Максимум
источник
Хэя. Могу ли я предложить другой заголовок для лучшей возможности поиска? Мне действительно начала нравиться эта анимированная гистограмма, и я думаю, что это отличная визуализация ... Что-то вроде "Гистограмма анимированных точек, построенное наблюдение по наблюдению" может быть более уместным?
Tjebo

Ответы:

11

Другой вариант - нарисовать точки другим геом. сначала вам нужно будет сделать некоторые подсчеты для ваших данных (и для биннинга), но это не требует увеличения объема ваших данных.

Например, вы можете использовать geom_point, но задача будет в том, чтобы правильно определить размеры ваших точек, чтобы они касались / не касались. Это зависит от размера устройства / файла.

Но вы также можете просто использовать, ggforce::geom_ellipseчтобы нарисовать свои точки :)

geom_point ( метод проб и ошибок с размерами устройства)

library(tidyverse)
library(gganimate)

set.seed(42)
samples <- rnorm(100)
index <- seq(1:length(samples))
df <- tibble(value = samples, index = index)

bin_width <- 0.25

count_data <- # some minor data transformation
  df %>%
  mutate(x = plyr::round_any(value, bin_width)) %>%
  group_by(x) %>%
  mutate(y = seq_along(x))

plot <-
  ggplot(count_data, aes(group = index, x, y)) + # group by index is important
  geom_point(size = 5)

p_anim <- 
  plot +
  transition_reveal(index)

animate(p_anim, width = 550, height = 230, res = 96)

geom_ellipse (полный контроль размера точки)

library(ggforce)
plot2 <- 
  ggplot(count_data) +
  geom_ellipse(aes(group = index, x0 = x, y0 = y, a = bin_width/2, b = 0.5, angle = 0), fill = 'black') +
  coord_equal(bin_width) # to make the dots look nice and round

p_anim2 <- 
  plot2 +
  transition_reveal(index) 

animate(p_anim2) 

Обновив ссылку, которую вы предоставляете, на удивительный пример Томаса, вы можете увидеть, что он использует аналогичный подход - он использует geom_circle вместо geom_ellipse, который я выбрал из-за лучшего контроля как вертикального, так и горизонтального радиуса.

Чтобы получить эффект «падающих капель», вам понадобится transition_statesи большая длительность, и множество кадров в секунду.

p_anim2 <- 
  plot2 +
  transition_states(states = index, transition_length = 100, state_length = 1) +
  shadow_mark() +
  enter_fly(y_loc = 12) 

animate(p_anim2, fps = 40, duration = 20) 

Создано 2020-04-29 пакетом представлением (v0.3.0)

вдохновение от: ggplot dotplot: Как правильно использовать geom_dotplot?

Tjebo
источник
Я ищу точки, приходящие по одной, а не по строкам в соответствии со значением Y.
максимум
2
@max см. обновление - просто замените y индексом.
Тьебо
3

Попробуй это. Основная идея состоит в том, чтобы сгруппировать объекты obs в кадры, т.е. разделить по индексу, а затем накапливать выборки в кадры, т.е. в кадре 1 отображаются только первые объекты obs, в кадрах 2 obs 1 и 2, ..... Возможно, есть это более элегантный способ добиться этого, но он работает:

library(ggplot2)
library(gganimate)
library(dplyr)
library(purrr)

set.seed(42)

# example data
samples <- rnorm(100)
index <- seq(1:length(samples))

# Put data into a data frame
df <- tibble(value=samples, index=index)

# inflated df. Group obs together into frames
df_ani <- df %>% 
  split(.$index) %>% 
  accumulate(~ bind_rows(.x, .y)) %>% 
  bind_rows(.id = "frame") %>% 
  mutate(frame = as.integer(frame))
head(df_ani)
#> # A tibble: 6 x 3
#>   frame  value index
#>   <int>  <dbl> <int>
#> 1     1  1.37      1
#> 2     2  1.37      1
#> 3     2 -0.565     2
#> 4     3  1.37      1
#> 5     3 -0.565     2
#> 6     3  0.363     3

p_gg <- ggplot(data=df, mapping=aes(x=value))+
  geom_dotplot()
p_gg
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.

p_anim <- ggplot(data=df_ani, mapping=aes(x=value))+
  geom_dotplot()

anim <- p_anim + 
  transition_manual(frame) +
  ease_aes("linear") +
  enter_fade() +
  exit_fade()
anim
#> `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.

Создано 2020-04-27 по пакету представительства (v0.3.0)

Штефана
источник
это работает, но быстро становится невозможным для больших наборов данных, так как таблица содержит много строк дублированных данных.
максимум
например, чтобы построить 5000 точек, фрейм данных имеет 12 миллионов строк :(
максимум
Извините за столь поздний ответ. Немного занят в данный момент. Да. Я понимаю вашу точку зрения. Я совершенно уверен, что должно быть лучшее и более прямолинейное решение этой проблемы. Тем не менее, я все еще новичок в gganimate и до сих пор у меня не было времени, чтобы проверить все его возможности и возможности. Так что, боюсь, я не могу найти лучшего решения на данный момент.
Стефан
3

Я думаю, что ключевой момент здесь состоит в том, чтобы представить, как вы бы создали эту анимацию вручную, то есть вы бы добавляли точки по одному наблюдению за раз в результирующий точечный график. Имея это в виду, подход, который я использовал здесь, состоял в том, чтобы создать ggplotобъект, который состоял из слоев графика = количество наблюдений, а затем пошагово проходил слой за слоем transition_layer.

# create the ggplot object
df <- data.frame(id=1:100, y=rnorm(100))

p <- ggplot(df, aes(y))

for (i in df$id) {
  p <- p + geom_dotplot(data=df[1:i,])
}

# animation
anim <- p + transition_layers(keep_layers = FALSE) +
    labs(title='Number of dots: {frame}')
animate(anim, end_pause = 20, nframes=120, fps=20)

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

Обратите внимание, что я установил, keep_layers=FALSEчтобы избежать переполнения. Если вы начертите начальный ggplotобъект, вы поймете, что я имею в виду, поскольку первое наблюдение строится 100 раз, второе - 99 раз ... и т. Д.

Как насчет масштабирования для больших наборов данных?

Так как количество кадров = количество наблюдений, вам необходимо настроить масштабируемость. Здесь просто оставьте # frames постоянным, то есть вы должны позволить коду сгруппировать кадры в сегменты, что я делаю с помощью seq()функции, определяющей length.out=100. Обратите внимание также, что в новом примере набор данных содержит n=5000. Чтобы держать точечную диаграмму в рамке, вам нужно сделать размеры точек очень маленькими. Я, вероятно, сделал точки слишком маленькими, но вы поняли идею. Теперь # frames = количество групп наблюдений.

df <- data.frame(id=1:5000, y=rnorm(5000))

p <- ggplot(df, aes(y))

for (i in seq(0,length(df$id), length.out=100)) {
  p <- p + geom_dotplot(data=df[1:i,], dotsize=0.08)
}

anim <- p + transition_layers(keep_layers=FALSE) +
  labs(title='Frame: {frame}')

animate(anim, end_pause=20, nframes=120, fps=20)

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

chemdork123
источник
Это хорошо работает для небольших наборов данных, но плохо масштабируется даже до умеренно больших данных (n = 5000).
максимум
Вот ошибка в отчетах для n = 5000: Ошибка: использование стека C 7969904 слишком близко к пределу
максимум
Да, здесь пример имеет фрейм = количество наблюдений. Я отредактировал ответ для масштабируемости, где вы держите # кадров постоянным на 100, а затем масштабируете так, чтобы фреймы = количество групп
chemdork123