rggplot2gganimate

gganimate: shadow_mark cannot exclude a geom_line layer


I am new to the gganimate package, and I encountered a problem in using shadow_mark. I have three layers of animation objects, and I only want to keep the trail in one of them. However, exclude_layer doesn't seem to work on, specifically, geom_line.

Here is the problematic code block:

try_anim = ggplot(data = df) +
  scale_y_continuous(limits = c(0,1)) +
  stat_function(data=df["mu_s1"], fun = f, linetype = "dashed", color="black") + 
  stat_function(data=df["mu_s1"], fun = g, color="black", na.rm = TRUE) +
  geom_point(data = df3, aes(x=a, y=g_a, group = t), size = 1.5) + 
  geom_line(data = df3, aes(x=a, y=g_a, group = t), size = 1.5) +  # problematic line
  geom_point(aes(x=mu_s1, y=z), color = "red", size = 2.5) + # only want shadow_mark on this line 
  transition_time(t) +
  shadow_mark(color="red", size = 1, exclude_layer = 1:2) 

animate(try_anim, fps = 10, duration = 10, renderer = gifski_renderer("~/tryanim.gif"))

If the issue needs to be diagnosed with an MWE, here is one:

rm(list=ls())
library(ggplot2)
library(gganimate)
library(gifski)

# model premises 
mu = 1/2 
p = 2/3 
q = 0.8 
r00 = 1
r01 = 0
r11 = 1
r10 = 0
mu_p_bar =(r00-r10)/((r00-r10)+(r11-r01))
v00 = 1.25
v01 = 0.25
v11 = 0.75
v10 = -0.25
mu_a_bar =(v00-v10)/((v00-v10)+(v11-v01))

# define functions 
f <- function(x){
  ifelse(x <= mu_p_bar, (r01-r00)*x+r00, ifelse(x > mu_p_bar, (r11-r10)*x+r10, NA))
}

g <- function(x){
  ifelse(x==mu_a_bar, 
         NA, 
         (x < mu_a_bar)*((r01-r00)*x+r00) + (x >mu_a_bar)*((r11-r10)*x+r10))
}

# create datasets used in the code
nsample = 100
mu_s1 = seq(0, 1, length.out = nsample)
mu_s10 <- ((1-q)*mu_s1)/((1-q)*mu_s1+q*(1-mu_s1))
mu_s11 <- ((q)*mu_s1)/((q)*mu_s1+(1-q)*(1-mu_s1))
g_mu_s10 = g(x=mu_s10)
g_mu_s11 = g(x=mu_s11)
s <- (g(x=mu_s11) - g(x=mu_s10)) / (mu_s11 - mu_s10)
z <- s*(mu_s1-mu_s10)+g(x=mu_s10)

df <- data.frame(mu_s1, mu_s10, mu_s11, g_mu_s10, g_mu_s11, s,z)
df <- df[order(mu_s1),]
df["t"] = c(1:nsample)

df1 = df[c("mu_s10","g_mu_s10", "t")]
names(df1) <- c("a", "g_a", "t")
df1["side"] = 0
df2 = df[c("mu_s11","g_mu_s11", "t")]
names(df2) <- c("a", "g_a", "t")
df2["side"] = 1
df3 = rbind(df1, df2)

# the problematic code block
try_anim = ggplot(data = df) +
  scale_y_continuous(limits = c(0,1)) +
  stat_function(data=df["mu_s1"], fun = f, linetype = "dashed", color="black") + 
  stat_function(data=df["mu_s1"], fun = g, color="black", na.rm = TRUE) +
  geom_point(data = df3, aes(x=a, y=g_a, group = t), size = 1.5) + 
  geom_line(data = df3, aes(x=a, y=g_a, group = t), size = 1.5) +  # problematic line
  geom_point(aes(x=mu_s1, y=z), color = "red", size = 2.5) + # only want shadow_mark on this line 
  transition_time(t) +
  shadow_mark(color="red", size = 1, exclude_layer = 1:2) 

animate(try_anim, fps = 10, duration = 10, renderer = gifski_renderer("~/tryanim.gif"))

This is the output, where you can see the unwanted shadow of the lines.

Output

I appreciate any suggestions or help!


Solution

  • The issue is that using exclude_layer = 1:2 you are only excluding the stat_function layers from your animation. Instead, if you only want to have a shadow mark on the last geom_point you have to use exclude_layer = 3:4 (or 1:4 but as far as I get it, this makes no difference) to drop the first geom_point and the geom_line.

    library(ggplot2)
    library(gganimate)
    library(gifski)
    
    try_anim <- ggplot(data = df) +
      scale_y_continuous(limits = c(0, 1)) +
      stat_function(data = df["mu_s1"], fun = f, linetype = "dashed", color = "black") +
      stat_function(data = df["mu_s1"], fun = g, color = "black", na.rm = TRUE) +
      geom_point(data = df3, aes(x = a, y = g_a, group = t), size = 1.5) +
      geom_line(data = df3, aes(x = a, y = g_a, group = t), size = 1.5) + # problematic line
      geom_point(aes(x = mu_s1, y = z), color = "red", size = 2.5) + # only want shadow_mark on this line
      transition_time(t) +
      shadow_mark(color = "red", size = 1, exclude_layer = 3:4)
    
    animate(try_anim, fps = 10, duration = 10, renderer = gifski_renderer("tryanim.gif"))
    

    enter image description here