Search code examples
rggplot2randomgganimaterandom-walk

Animating 2D random walk trajectory with ggplot in R


I'm trying to create an animation that shows the trajectory of a random walk on the (x,y) plane over time. Each 'step' is a move in either the vertical or horizontal direction, never both. For example, if you start at (0,0) you have only four options: (0,1), (0,-1), (1,0), (-1,0); you can never go from (0,0) to (1,1) in one step.

I've written the random walk generating function to ensure that steps can only be taken across one axis at a time. Here is a regular ggplot without animation of one of these random walks using geom_path() and geom_point():

Random walk with T=100

When I try to animate the walk, however, it shows diagonal lines connecting the points which is impossible.

Animated trajectory

I can't figure out why this is happening. I've tried using geom_line() instead but that doesn't seem to fix the issue. The code for the animation is below:

ggplot(df,aes(x=x,y=y)) + 
  geom_path() +
  geom_point() +
  transition_reveal(along=ite) + # ite: numerical ordered variable in df representing the time from 0:n
  scale_x_continuous(breaks = seq(min(df$x), max(df$x), by = 1)) +
  scale_y_continuous(breaks = seq(min(df$y), max(df$y), by = 1)) +
  coord_fixed() +
  theme(panel.grid.minor = element_blank())

Solution

  • The number of frames in your animation are smaller than the number of states you are revealing along ite. This means that at least every once in a while, transition_reveal() must "skip" across a few states of ite and this results in sometimes drawing a diagonal line.

    Increase the frames in your animation to match the number of steps for ite. You can specify that in animate() when showing your animation or anim_save() when saving.

    Example and Solution

    Here's a demonstration using my own random walk function that works as you describe.

    Note that it's useful to use {frame_along} within one of the labels in the plot that can be used to follow the particular state which is being shown.

    library(ggplot2)
    library(gganimate)
    
    walk <- function(steps) {
      x <- vector(length=steps)
      y <- vector(length=steps)
      x[1] <- 0
      y[1] <- 0
      direction <- 0
      amt <- 0
      for (i in 2:steps) {
        direction <- rbinom(1, 1, 0.5)
        amt <- sample(c(-1,1), 1)
        if(direction==0) {
          x[i] <- x[i-1] + amt
          y[i] <- y[i-1]
        }
        else{
          x[i] <- x[i-1]
          y[i] <- y[i-1] + amt
        }
      }
      return(data.frame(x,y, ite=1:steps))
    }
    
    df <- walk(200)
    
    p <- 
      ggplot(df, aes(x,y)) +
      geom_path() + geom_point() +
      labs(title="it: {frame_along}") +  # meant to write ite, but ok
      theme_bw()
    
    a <- p + transition_reveal(along=ite)
    

    Here's the plot without animation.

    enter image description here

    Here's the animation. Note how the value for ite ("it: ##") is sometimes "jumping" across a few values.

    enter image description here

    To fix, you can specify nframes= within animate() instead of just calling a directly. You will note in the output that the value for ite is incrementing every state now and not skipping. This is why you always have the straight lines.

    animate(a, nframes=200)
    

    enter image description here