Usa gganimate per costruire un'osservazione istogramma mediante osservazione? Deve funzionare per set di dati più grandi (~ n = 5000)


12

Vorrei campionare i punti da una distribuzione normale, quindi creare un dotplot uno per uno usando il gganimatepacchetto fino a quando il frame finale mostra il dotplot completo.

Una soluzione che funziona per set di dati più grandi ~ 5.000 - 20.000 punti è essenziale.

Ecco il codice che ho finora:

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)

Il df si presenta così:

> 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

Il grafico statico mostra il dotplot corretto:

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

Tuttavia, la gganimateversione no (vedi sotto). Mette solo i punti sull'asse x e non li impila.

plot+
  transition_reveal(along=index)

Trama statica

inserisci qui la descrizione dell'immagine

Qualcosa di simile a questo sarebbe l'ideale: credito: https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1 inserisci qui la descrizione dell'immagine


Heya. Posso suggerire un titolo diverso per una migliore ricerca? Ho davvero iniziato ad apprezzare questo istogramma animato e penso che sia un'ottima visualizzazione ... Sth come "Istogramma a punti animato, costruito dall'osservazione mediante osservazione" sarebbe forse più pertinente?
Tjebo

Risposte:


11

Un'altra opzione è quella di disegnare i punti con un altro geom. dovrai prima fare dei conteggi sui tuoi dati (e binning) ma non è necessario allungarli.

Ad esempio, puoi usare geom_point, ma la sfida sarà quella di ottenere le dimensioni dei tuoi punti giusti, in modo che tocchino / non tocchino. Questo dipende dalla dimensione del dispositivo / file.

Ma puoi anche solo usare ggforce::geom_ellipseper disegnare i tuoi punti :)

geom_point (prova ed errore con le dimensioni 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 (Controllo completo della dimensione 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) 

aggiornando nel link che fornisci al fantastico esempio di Thomas, puoi vedere che usa un approccio simile: usa geom_circle invece di geom_ellipse, che ho scelto per un migliore controllo sia del raggio verticale che orizzontale.

Per ottenere l'effetto "gocce che cadono", avrai bisogno di transition_statesuna lunga durata e di molti fotogrammi al secondo.

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) 

Creato il 29-04-2010 dal pacchetto reprex (v0.3.0)

qualche ispirazione da: ggplot dotplot: Qual è l'uso corretto di geom_dotplot?


Sto cercando i punti che si presentano uno per uno, non in file in base al valore Y.
max

2
@max vedi aggiornamento - basta sostituire y con indice.
Tjebo,

3

Prova questo. L'idea di base è quella di raggruppare gli obs ai frame, cioè divisi per indice e quindi accumulare i campioni ai frame, ovvero nel frame 1 viene mostrato solo il primo obs, nei frame 2 obs 1 e 2, ..... Forse lì è un modo più elegante per raggiungere questo obiettivo, ma funziona:

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

Creato il 2020-04-27 dal pacchetto reprex (v0.3.0)


funziona, ma diventa rapidamente impossibile per set di dati più grandi poiché la tabella contiene molte righe di dati duplicati.
massimo

ad esempio, per tracciare 5000 punti, il frame di dati ha 12 milioni di righe :(
max

Scusa per la risposta tardiva. Un po 'occupato al momento. Sì. Vedo il tuo punto. Sono abbastanza sicuro che ci debba essere una soluzione migliore e più diretta per questo tipo di problema. Tuttavia, sono ancora un novizio di Gganimate e fino ad ora non ho avuto il tempo di dare un'occhiata a tutte le sue possibilità e funzionalità. Quindi, temo di non poter trovare una soluzione migliore per il momento.
stefan,

3

Penso che la chiave qui sia immaginare come creare manualmente questa animazione, vale a dire aggiungere i punti un'osservazione alla volta al dotplot risultante. Con questo in mente, l'approccio che ho usato qui è stato quello di creare un ggplotoggetto che consistesse in strati di trama = numero di osservazioni, quindi passare da uno strato all'altro attraverso 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)

inserisci qui la descrizione dell'immagine

Si noti che ho impostato keep_layers=FALSEper evitare la sovrapposizione. Se traccia l' ggplotoggetto iniziale , vedrai cosa intendo, poiché la prima osservazione viene tracciata 100 volte, la seconda 99 volte ... ecc.

Che dire del ridimensionamento per set di dati più grandi?

Poiché numero di frame = numero di osservazioni, è necessario regolare per la scalabilità. Qui, mantieni costanti i frame #, nel senso che devi lasciare che il codice raggruppi i frame in segmenti, cosa che sto facendo tramite la seq()funzione, specificando length.out=100. Nota anche nel nuovo esempio, il set di dati contiene n=5000. Per mantenere il dotplot nella cornice, è necessario rendere le dimensioni dei punti davvero minuscole. Probabilmente ho fatto i punti un po 'troppo piccoli qui, ma tu sei l'idea. Ora i # frame = numero di gruppi di osservazioni.

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)

inserisci qui la descrizione dell'immagine


Funziona bene per piccoli set di dati, ma non si adatta bene anche a dati moderatamente grandi (n = 5000).
max

Ecco l'errore sono i rapporti per n = 5000: Errore: l'utilizzo dello stack C 7969904 è troppo vicino al limite
massimo

Sì, qui l'esempio ha frame = numero di osservazioni. Ho modificato la risposta per la scalabilità, dove tieni costanti i frame # a 100 e poi ridimensiona in modo che i frame = numero di gruppi
chemdork123
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.