Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gganimate plot where points stay and line fades

Here is a reproducible example of a static plot, which I want to animate (I want to show how a MCMC sampler behaves).

library(tidyverse)
library(gganimate)

set.seed(1234)
plot_data <- tibble(x=cumsum(rnorm(100)),
                    y=cumsum(rnorm(100)),
                    time=1:length(x)) 

ggplot(data=plot_data,
       aes(x=y, y=x)) +
  geom_point() + geom_line()

enter image description here

What I'd like to see is the points being visible when they are drawn and a bit faded (i.e. alpha goes from e.g. 1 to 0.3) afterwards, while there would be a line that only shows the recent history (and ideally fades showing the most recent history the least faded and more than a few steps back totally disappearing).

The following achieves more or less what I want for my points (so in a sense I just want to add fading lines to this connecting the last few points - points fading more slowly across some frames would be even nicer):

ggplot(data=plot_data,
       aes(x=y, y=x)) +
  geom_point() +
  transition_time(time) +
  shadow_mark(past = T, future=F, alpha=0.3)

Plot showing how points should fade

What I am struggling with is how to add two different behaviors for two geoms e.g. point and line. E.g. in the below the points disappear (I don't want them to) and the lines do not fade (I want them to).

p <- ggplot(data=plot_data,
       aes(x=y, y=x)) +
  geom_point() +
  transition_time(time) +
  shadow_mark(past = T, future=F, alpha=0.3)

p + geom_line() +
  transition_reveal(along = time) +
  shadow_mark(past = T, future=F, alpha=0.3) 
like image 569
Björn Avatar asked Oct 07 '19 14:10

Björn


1 Answers

I had trouble using the built-in shadow_* functions to control more than one behavior at a time; it seemed to just apply the most recent one. (Using gganimate 1.0.3.9000)

One way to get around this is to calculate the transitions manually. For instance, we could copy the data 100 times, one copy for each frame, and then specify the alpha for our points layer and the alpha for our segment layer separately.

plot_data %>%
  uncount(100, .id = "frame") %>%
  filter(time <= frame) %>%
  arrange(frame, time) %>%
  group_by(frame) %>%
  mutate(x_lag = lag(x), 
         y_lag = lag(y),
         tail = last(time) - time,
         # Make the points solid for 1 frame then alpha 0.3
         point_alpha = if_else(tail == 0, 1, 0.3),
         # Make the lines fade out over 20 frames
         segment_alpha = pmax(0, (20-tail)/20)) %>%
  ungroup() %>%

  ggplot(aes(x=y, y=x, xend = y_lag, yend = x_lag, group = time)) +
  geom_segment(aes(alpha = segment_alpha)) +
  geom_point(aes(alpha = point_alpha)) +
  scale_alpha(range = c(0,1)) +
  guides(alpha = F) +
  transition_manual(frame)

enter image description here

(For this render, I wrapped it in animate( [everything above], width = 600, height = 400, type = "cairo"))

like image 198
Jon Spring Avatar answered Oct 12 '22 13:10

Jon Spring