Использовать 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?


Я ищу точки, приходящие по одной, а не по строкам в соответствии со значением 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)

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


Это хорошо работает для небольших наборов данных, но плохо масштабируется даже до умеренно больших данных (n = 5000).
— максимум

Вот ошибка в отчетах для n = 5000: Ошибка: использование стека C 7969904 слишком близко к пределу
— максимум

Да, здесь пример имеет фрейм = количество наблюдений. Я отредактировал ответ для масштабируемости, где вы держите # кадров постоянным на 100, а затем масштабируете так, чтобы фреймы = количество групп
— chemdork123
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.