¿Usar gganimate para construir una observación de histograma por observación? Necesita trabajar para conjuntos de datos más grandes (~ n = 5000)


12

Me gustaría muestrear puntos de una distribución normal, y luego construir un diagrama de puntos uno por uno usando el gganimatepaquete hasta que el cuadro final muestre el diagrama de puntos completo.

Una solución que funcione para conjuntos de datos más grandes ~ 5,000 - 20,000 puntos es esencial.

Aquí está el código que tengo hasta ahora:

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)

El df se ve así:

> 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

El diagrama estático muestra el diagrama de puntos correcto:

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

Sin embargo, la gganimateversión no lo hace (ver más abajo). Solo coloca los puntos en el eje xy no los apila.

plot+
  transition_reveal(along=index)

Trama estática

ingrese la descripción de la imagen aquí

Algo similar a esto sería ideal: Crédito: https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1 ingrese la descripción de la imagen aquí


Heya ¿Puedo sugerir un título diferente para una mejor capacidad de búsqueda? Realmente me empezó a gustar este histograma animado, y creo que es una gran visualización ... Algo así como "Histograma animado de puntos, observación construida por observación" ¿quizás sería más pertinente?
Tjebo

Respuestas:


11

Otra opción es dibujar los puntos con otra geom. primero tendrá que hacer algunos recuentos de sus datos (y binning), pero no requiere que sus datos sean más largos.

Por ejemplo, puede usar geom_point, pero el desafío será obtener las dimensiones correctas de sus puntos, para que se toquen / no se toquen. Esto depende del dispositivo / tamaño del archivo.

Pero también puedes usarlo ggforce::geom_ellipsepara dibujar tus puntos :)

geom_point (prueba y error con las dimensiones del dispositivo)

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 (Control total del tamaño del punto)

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) 

actualización en el enlace que proporcione a Thomas ejemplo increíble, se puede ver que utiliza un enfoque similar - se utiliza en lugar de geom_circle geom_ellipse, que he elegido debido a un mejor control tanto para el radio vertical y horizontal.

Para obtener el efecto "gotas que caen", necesitará transition_statesuna larga duración y muchos cuadros por segundo.

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) 

Creado en 2020-04-29 por el paquete reprex (v0.3.0)

alguna inspiración de: ggplot dotplot: ¿Cuál es el uso adecuado de geom_dotplot?


Estoy buscando los puntos que vienen uno por uno, no en filas según el valor Y.
máximo

2
@max ver actualización: simplemente reemplace y con índice.
Tjebo

3

Prueba esto. La idea básica es agrupar los obs en cuadros, es decir, dividirlos por índice y luego acumular las muestras en cuadros, es decir, en el cuadro 1 solo se muestra el primer obs, en los cuadros 2 obs 1 y 2, ..... Quizás haya es una forma más elegante de lograr esto, pero funciona:

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`.

Creado el 27-04-2020 por el paquete reprex (v0.3.0)


esto funciona, pero rápidamente se vuelve inviable para conjuntos de datos más grandes ya que la tabla contiene muchas filas de datos duplicados.
máximo

por ejemplo, para trazar 5000 puntos, el marco de datos tiene 12 millones de filas :(
máximo

Discúlpeme por la respuesta tarde. Poco ocupado en este momento. Si. Entiendo tu argumento. Estoy bastante seguro de que debe haber una solución mejor y más directa para este tipo de problema. Sin embargo, todavía soy un novato en la materia y hasta ahora no tenía tiempo para ver todas sus posibilidades y características. Entonces, me temo que no puedo encontrar una mejor solución por el momento.
stefan

3

Creo que la clave aquí es imaginar cómo crearía esta animación manualmente, es decir, agregaría puntos una observación a la vez al diagrama de puntos resultante. Con esto en mente, el enfoque que utilicé aquí fue crear un ggplotobjeto que consistiera en capas de trama = número de observaciones, luego paso a paso capa por capa 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)

ingrese la descripción de la imagen aquí

Tenga en cuenta que configuré keep_layers=FALSEpara evitar la sobreplotación. Si traza el ggplotobjeto inicial , verá lo que quiero decir, ya que la primera observación se traza 100 veces, la segunda 99 veces ... etc.

¿Qué pasa con el escalado para conjuntos de datos más grandes?

Como número de cuadros = número de observaciones, debe ajustar la escalabilidad. Aquí, simplemente mantenga los # cuadros constantes, lo que significa que debe dejar que el código agrupe los cuadros en segmentos, lo que estoy haciendo a través de la seq()función, especificando length.out=100. Tenga en cuenta también en el nuevo ejemplo, el conjunto de datos contiene n=5000. Para mantener el diagrama de puntos en el marco, debe hacer que los tamaños de los puntos sean realmente pequeños. Probablemente hice los puntos un poco demasiado pequeños aquí, pero se te ocurre la idea. Ahora los # cuadros = número de grupos de observaciones.

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)

ingrese la descripción de la imagen aquí


Esto funciona bien para conjuntos de datos pequeños, pero no escala bien incluso a datos moderadamente grandes (n = 5000).
máximo

Aquí está el error de informes para n = 5000: Error: el uso de la pila C 7969904 está demasiado cerca del límite
máximo

Sí, aquí el ejemplo tiene frame = número de observaciones. Edité la respuesta para la escalabilidad, donde mantienes los # cuadros constantes en 100 y luego escalas para que los cuadros = número de grupos
chemdork123
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.