Use o gganimate para criar uma observação de histograma por observação? Precisa trabalhar para conjuntos de dados maiores (~ n = 5000)

12

Gostaria de provar pontos de uma distribuição normal e, em seguida, criar um gráfico de pontos, um por um, usando o gganimatepacote até que o quadro final mostre o gráfico de pontos completo.

Uma solução que funcione para conjuntos de dados maiores ~ 5.000 - 20.000 pontos é essencial.

Aqui está o código que tenho até agora:

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)

O df fica assim:

> 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

O gráfico estático mostra o gráfico de pontos correto:

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

No entanto, a gganimateversão não possui (veja abaixo). Ele apenas coloca os pontos no eixo x e não os empilha.

plot+
  transition_reveal(along=index)

Gráfico estático

insira a descrição da imagem aqui

Algo semelhante a este seria ideal: Crédito: https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1 insira a descrição da imagem aqui

max
fonte
Heya. Posso sugerir um título diferente para melhor pesquisabilidade? Eu realmente comecei a gostar desse histograma animado e acho que é uma ótima visualização ... Então, como "Histograma de pontos animados, observação construída por observação" talvez fosse mais pertinente?
Tjebo

Respostas:

11

Outra opção é desenhar os pontos com outro geom. você precisará fazer algumas contagens dos dados primeiro (e binning), mas isso não exige que os dados sejam mais longos.

Por exemplo, você pode usar geom_point , mas o desafio será acertar as dimensões dos seus pontos, para que eles toquem / não toquem. Isso depende do tamanho do dispositivo / arquivo.

Mas você também pode usar apenas ggforce::geom_ellipsepara desenhar seus pontos :)

geom_point (tentativa e erro com dimensões do 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 (controle total do tamanho do ponto)

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) 

atualizar no link que você fornece ao exemplo incrível de thomas, você pode ver que ele usa uma abordagem semelhante - ele usa geom_circle em vez de geom_ellipse, que eu escolhi por causa de um melhor controle do raio vertical e horizontal.

Para obter o efeito "gotas caindo", você precisará de transition_statesuma duração longa e de muitos quadros 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) 

Criado em 2020-04-29 pelo pacote reprex reprex (v0.3.0)

alguma inspiração de: ggplot dotplot: Qual é o uso adequado do geom_dotplot?

Tjebo
fonte
Estou procurando os pontos que devem aparecer um por um, não em linhas de acordo com o valor Y.
max
2
@max ver atualização - basta substituir y pelo índice.
Tjebo 29/04
3

Tente isso. A idéia básica é agrupar os quadros obs, ou seja, dividir por índice e acumular as amostras em quadros, ou seja, no quadro 1, apenas o primeiro obs é mostrado, no quadro 2 obs 1 e 2, ..... Talvez haja é uma maneira mais elegante de conseguir isso, mas 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`.

Criado em 2020-04-27 pelo pacote reprex (v0.3.0)

Stefan
fonte
isso funciona, mas rapidamente se torna inviável para conjuntos de dados maiores, pois a tabela contém muitas linhas de dados duplicados.
max
por exemplo, para plotar 5000 pontos, o quadro de dados possui 12 milhões de linhas :(
máximo
Desculpe pela resposta tardia. Um pouco ocupado no momento. Sim. Eu entendo o seu ponto. Tenho certeza de que deve haver uma solução melhor e mais direta para esse tipo de problema. No entanto, eu ainda sou um novato em termos de organização e até agora não tive tempo de verificar todas as suas possibilidades e recursos. Então, receio que não possa encontrar uma solução melhor no momento.
stefan
3

Acho que a chave aqui é imaginar como você criaria essa animação manualmente, ou seja, você adicionaria pontos uma observação de cada vez no gráfico de pontos resultante. Com isso em mente, a abordagem que usei aqui foi criar um ggplotobjeto que consistisse em camadas de plotagem = número de observações e depois passar por camada por camada 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)

insira a descrição da imagem aqui

Observe que eu defino keep_layers=FALSEpara evitar a plotagem. Se você plotar o ggplotobjeto inicial , verá o que quero dizer, já que a primeira observação é plotada 100 vezes, a segunda 99 vezes ... etc.

E quanto ao dimensionamento para conjuntos de dados maiores?

Como número de quadros = número de observações, é necessário ajustar a escalabilidade. Aqui, apenas mantenha os # frames constantes, o que significa que você deve deixar o código agrupar os quadros em segmentos, o que estou fazendo através da seq()função, especificando length.out=100. Observe também no novo exemplo, o conjunto de dados contém n=5000. Para manter o gráfico de pontos no quadro, você precisa diminuir o tamanho dos pontos. Provavelmente eu fiz os pontos um pouco pequenos demais aqui, mas você entende a ideia. Agora, os # quadros = número de grupos de observações.

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)

insira a descrição da imagem aqui

chemdork123
fonte
Isso funciona bem para pequenos conjuntos de dados, mas não se adapta bem a dados moderadamente grandes (n = 5000).
max
Aqui está o erro nos relatórios para n = 5000: Erro: o uso da pilha C 7969904 está muito próximo do limite
máximo
Sim, aqui o exemplo tem quadro = número de observações. Eu editei a resposta para escalabilidade, onde você segurar a constante # quadros em 100 e, em seguida, escalando para que os quadros = número de observação grupos
chemdork123