3

I have geographic data like the following (but a much larger amount of data):

library(tidyverse)
library(gganimate)

n <- 500

longitude <- runif(n) 
latitude <- runif(n) 
time <- round(runif(n, 0, 100),1) %>% sort
data <- tibble(longitude,latitude) %>% arrange(longitude) %>% mutate(time = time)

I can make an animation with gganimate as follows:

anim1 <- ggplot(data, aes(x=longitude, y=latitude, group = time)) + 
  geom_point(color = "red", size = 10) +
  transition_components(time, exit_length = 30) +
  exit_fade() +
  shadow_mark(color = "red" ,alpha = 0.1, size = 10) 

animate(anim1, nframes = 100)

And this gives the following output:

enter image description here

Here I am using both exit_fade for the point to fade away, but also shadow_mark with a set alpha in order for the data to persist. And this is basically very very close to what I want.

The problem is that what's really happening here is that gganimate is putting a shadow_mark on immediately after the data point first appears underneath the geom_point, and it then becomes slowly visible while the original point fades. If I just want the point to fade but persist, this is mostly OK (although the fade transition is a little bit weird because the alphas of the original and the shadow_mark are adding together visually during the fade). But it becomes a bigger problem because sometimes I need to also resize the point.

If I use a shadow_mark that is smaller than the original point, you can see the problem:

anim2 <- ggplot(data, aes(x=longitude, y=latitude, group = time)) + 
  geom_point(color = "red", size = 10) +
  transition_components(time, exit_length = 30) +
  exit_fade() +
  shadow_mark(color = "red" ,alpha = 0.1, size = 2) 

animate(anim2, nframes = 100)

Which gives the following:

enter image description here

You can see the smaller point appear while the larger point fades out (here I have left out simultaneous resizing for simplicity, but the problem is still present in that case). So I would like to know if there is a way to do this "properly" in gganimate such that the point actually just fades but persists, without having the shadow_mark just drawn underneath and slowly revealed by the fading of the original point?

The reason for the question is that I would ultimately like to smoothly exit_fade and exit_shrink at the same time such that the final size after the shrink is the same as the smaller size of the shadow_mark in the example above and then the point just persists throughout the rest of the animation in the smaller, semi-faded state.

Note: I realize that I could uncount the data rows and make my own frames and transitions manually within the data and allow the points to persist that way, as has been discussed in other answers. The problem is my real data set is actually very large, and doing an uncount like this results in a data set that is too big for memory. So I would prefer a pure gganimate solution if one exists.

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
Jacques P.
  • 31
  • 2

1 Answers1

2

The following approach duplicates the dataset twice, which does cause the file size to balloon a bit, but probably not as problematic as uncounting would have done. See if it works for you?

data <- data %>%
  mutate(id = seq(1, n()),               # add an ID column to original dataset, 
         type = "original",         
         size = 10,                      # specify initial size / alpha / any other aspect
         alpha = 1)                      # to be changed during animation

rbind(data,

      data %>%                           # create second version of the dataset, 
        mutate(type = "transiting",      # with time lagged by the desired transition
               time = time + 30,         # amount (30 in the question's example), 
               size = size * 0.2,        # & size / alpha / any other aspect defined
               alpha = alpha * 0.1),     # according to end state after transition;

      data %>%                           # create third version of the dataset, 
        mutate(type = "persisting",      # which should be identical to the second,
               time = max(time) + 30,    # except that the time is set to the max time 
               size = size * 0.2,        # across all rows.
               alpha = alpha * 0.1)) %>%
  
  # pass this combined dataset to ggplot & animate as per normal with explicitly 
  # specified parameters for size / alpha / etc, & group aesthetic set to ID value.
  # no need to specify exit_fade or shadow_mark now, as each point DOESN'T exit at all.
  ggplot(aes(x = longitude, y = latitude, group = id,
             size = size, alpha = alpha)) +
  geom_point(colour = "red") +
  transition_components(time) + 
  ggtitle("{frame_time}") + # optional; added to illustrate frame time explicitly
  scale_size_identity() +
  scale_alpha_identity()

animated

Z.Lin
  • 28,055
  • 6
  • 54
  • 94