Użyj gganimate, aby zbudować obserwację histogramu poprzez obserwację? Musi pracować dla większych zestawów danych (~ n = 5000)


10

Chciałbym próbkować punkty z rozkładu normalnego, a następnie budować wykres punktowy jeden po drugim, używając gganimatepakietu, aż ostatnia ramka pokaże pełny wykres punktowy.

Niezbędne jest rozwiązanie, które działa dla większych zbiorów danych ~ 5 000 - 20 000 punktów.

Oto kod, który mam do tej pory:

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 wygląda następująco:

> 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

Wykres statyczny pokazuje prawidłowy wykres punktowy:

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

Jednak gganimatewersja nie ma (patrz poniżej). Umieszcza tylko kropki na osi X i nie układa ich w stos.

plot+
  transition_reveal(along=index)

Wykres statyczny

wprowadź opis zdjęcia tutaj

Coś podobnego do tego byłoby idealne: Credit: https://gist.github.com/thomasp85/88d6e7883883315314f341d2207122a1 wprowadź opis zdjęcia tutaj


Heya Czy mogę zasugerować inny tytuł dla lepszej możliwości wyszukiwania? Ten animowany histogram naprawdę mi się podobał i myślę, że to świetna wizualizacja ... Czy coś w stylu „Animowany histogram kropkowy, zbudowana obserwacja przez obserwację” może być bardziej odpowiednie?
Tjebo

Odpowiedzi:


9

Inną opcją jest narysowanie punktów inną geom. najpierw trzeba będzie liczyć na swoje dane (i binowanie), ale nie wymaga to przedłużenia danych.

Na przykład możesz użyć geom_point , ale wyzwaniem będzie prawidłowe wyregulowanie wymiarów punktów, aby dotykały / nie dotykały. Zależy to od rozmiaru okna / pliku.

Ale możesz także użyć ggforce::geom_ellipsedo narysowania kropek :)

geom_point (próba i błąd z rozmiarem rzutni)

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 (Pełna kontrola wielkości punktu)

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) 

aktualizacja w linku, który podajesz do niesamowitego przykładu Thomasa, widać, że stosuje on podobne podejście - używa geom_circle zamiast geom_ellipse, które wybrałem ze względu na lepszą kontrolę zarówno w pionie, jak i w poziomie.

Aby uzyskać efekt „spadających kropel”, potrzebujesz transition_statesdługiego czasu działania i wielu klatek na sekundę.

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) 

Utworzono 2020-04-29 przez pakiet reprezentx (v0.3.0)

inspiracja z: ggplot dotplot: Jakie jest właściwe zastosowanie geom_dotplot?


Szukam punktów, które będą przychodzić jeden po drugim, a nie w rzędach według wartości Y.
maks.

2
@max zobacz aktualizację - wystarczy zastąpić y indeksem.
Tjebo

3

Spróbuj tego. Podstawową ideą jest zgrupowanie obs do ramek, tj. Podzielenie według indeksu, a następnie nagromadzenie próbek do ramek, tj. W ramce 1 pokazany jest tylko pierwszy obs, w ramce 2 obs 1 i 2, ... Być może tam jest bardziej eleganckim sposobem na osiągnięcie tego, ale działa:

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

Utworzono 2020-04-27 przez pakiet reprezentx (v0.3.0)


to działa, ale szybko staje się niewykonalne dla większych zestawów danych, ponieważ tabela zawiera wiele wierszy zduplikowanych danych.
maks.

na przykład, aby wykreślić 5000 punktów, ramka danych ma 12 milionów wierszy :(
maks.

Przepraszam za spóźnioną odpowiedź. W tej chwili trochę zajęty. Tak. Rozumiem co masz na myśli. Jestem całkiem pewien, że musi istnieć lepsze i prostsze rozwiązanie tego rodzaju problemu. Jednak wciąż jestem nowicjuszem do gganizacji i do tej pory nie miałem czasu, aby sprawdzić wszystkie jego możliwości i funkcje. Obawiam się, że nie mogę w tej chwili znaleźć lepszego rozwiązania.
Stefan

3

Myślę, że kluczem tutaj jest wyobrażenie sobie, jak utworzyłbyś tę animację ręcznie, co oznacza, że ​​dodawałeś kropki po jednej obserwacji na wynikowy wykres punktowy. Mając to na uwadze, zastosowałem tutaj podejście polegające na stworzeniu ggplotobiektu, który składałby się z warstw wykresu = liczba obserwacji, a następnie krok po kroku przez warstwę 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)

wprowadź opis zdjęcia tutaj

Zauważ, że ustawiłem, keep_layers=FALSEaby uniknąć overplottingu. Jeśli wykreślisz początkowy ggplotobiekt, zobaczysz, co mam na myśli, ponieważ pierwsza obserwacja jest wykreślana 100 razy, druga 99 razy ... itd.

Co ze skalowaniem dla większych zestawów danych?

Ponieważ liczba klatek = liczba obserwacji, należy dostosować skalowalność. Tutaj po prostu utrzymuj # ramki na stałym poziomie, co oznacza, że ​​musisz pozwolić kodowi grupować ramki w segmenty, co robię za pomocą seq()funkcji, określając length.out=100. Uwaga: w nowym przykładzie zestaw danych zawiera n=5000. Aby utrzymać kropkę w ramce, musisz zrobić naprawdę małe rozmiary kropek. Prawdopodobnie zrobiłem tutaj kropki trochę za małe, ale masz pomysł. Teraz # klatek = liczba grup obserwacji.

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)

wprowadź opis zdjęcia tutaj


Działa to dobrze w przypadku małych zestawów danych, ale nie skaluje się dobrze nawet do danych średnio umiarkowanych (n = 5000).
maks.

Oto błąd raportów dla n = 5000: Błąd: użycie stosu C 7969904 jest zbyt blisko limitu
maks.

Tak, tutaj przykład ma ramkę = liczbę obserwacji. Zredagowałem odpowiedź na pytanie o skalowalność, w której trzymasz stałą # klatek na poziomie 100, a następnie
skalujesz
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.