使用gganimate显示补间数据的计算

时间:2019-12-11 10:24:21

标签: r ggplot2 gganimate

我想使用 gganimate

  • geom_path绘制两条单独的曲线
  • 调用一个函数,使用这些行中的数据执行计算并返回一个坐标(x,y)
  • geom_point的坐标进行绘制
  • 移动线条,geom_point随着线条的移动而更新

这很简单,如果移动使得单个(x,y)坐标线性移动(只需提前在每个阶段进行计算,然后对其进行动画处理,它将从每个阶段线性移动到下一阶段),但是如果不是,我不确定该怎么办。如果我在aes()中调用一个函数,这似乎是很自然的解决方案,那么它似乎首先要计算一次,然后在行移动时不对其进行更新。

这里是一个例子。

library(tidyverse)
library(gganimate)

# A function to find the x and y coordinate of the minimum y value of either set
min_of_both <- function(x1, y1, x2, y2) {
  cm <- bind_rows(tibble(x = x1, y = y1),
                  tibble(x = x2, y = y2))

  return(list(x = cm[which(cm$y == min(cm$y)),]$x,
              y = min(cm$y)))
}

# Create two parabola paths, curve A which moves downwards from t = 1 to t = 2
curveA <- tibble(xA = -50:50/10, yA = 5+(-50:50/10)^2, t = 1) %>%
  bind_rows(tibble(xA = -50:50/10, yA = -10 + (-50:50/10)^2, t = 2))
# And curve B which is static in both time 1 and 2
curveB <- tibble(xB = -50:50/10, yB = 1 + (-30:70/10)^2)

data <- curveB %>%
  bind_rows(curveB) %>%
  bind_cols(curveA)

# Plot Curve A
p <- ggplot(data, aes(x = xA, y = yA)) + 
  geom_path(color = 'red') +
  # And Curve B
  geom_path(aes(x=xB,y=yB), color = 'blue')+
  # Then plot a single point that uses both curves as input
  # Note I also get problems if trying to run the function through data= instead of mapping=
  # or if I define two separate functions, one for x and one for y, so as to avoid $
  geom_point(aes(
    x = min_of_both(xA,yA,xB,yB)$x, 
    y = min_of_both(xA,yA,xB,yB)$y), 
    size = 3,
    color = 'black') +
  theme_minimal()+
  transition_states(t)+
  ease_aes('sine-in-out')
animate(p)

这会导致(不确定动画是否会在StackOverflow上播放,但抛物线确实会移动):

Animation of two parabolas

黑点旨在在每个时刻标记任一抛物线的最低y坐标,但相反,它在动画的任何点(最后)标记任一抛物线的y最低坐标。

感谢任何提示。

1 个答案:

答案 0 :(得分:1)

经过反复的摸索,我想我已经理解了你的观点并找到了解决方案。最好的前进方法可能是手动绘制路径tween并使用函数计算最小值,同时在绘制之前按.frame分组:

# Same curve setup, but labelling points for grouping later
curveA <- tibble(xA = -50:50/10, 
                 yA = 5+(-50:50/10)^2, 
                 point = 1:101,
                 t = 1) %>%
  bind_rows(tibble(xA = -50:50/10, 
                   yA = -10 + (-50:50/10)^2,
                   point = 1:101,
                   t = 2))

curveB <- tibble(xB = -50:50/10, 
                 yB = 1 + (-30:70/10)^2,
                 point = 1:101,
                 t = 1)


A_frames <-  curveA %>%
  tween_along(ease = 'sine-in-out', 100, along = t, id = point) %>% 
  filter(.phase == "transition") %>% 
  select(xA, yA, point, .frame) %>% 
  arrange(.frame, point)  # arrange by point needed to keep in order

B_frames <-  curveB %>%
  bind_rows(curveB %>% mutate(t = 2)) %>% 
  tween_along(ease = 'sine-in-out', 100, along = t, id = point) %>% 
  filter(.phase == "transition") %>% 
  select(xB, yB, point, .frame) %>%
  arrange(.frame, point)


data <- A_frames %>%
  left_join(B_frames, by = c(".frame", "point")) %>% 
  group_by(.frame) %>% 
  mutate(xmin = min_of_both(xA,yA,xB,yB)$x,
         ymin = min_of_both(xA,yA,xB,yB)$y)

# Plot Curve A
p <- ggplot(data, aes(x = xA, y = yA)) + 
  geom_path(color = 'red') +
  # And Curve B
  geom_path(aes(x=xB,y=yB), color = 'blue')+
  # Then plot a single point that uses both curves as input
  # Note I also get problems if trying to run the function through data= instead of mapping=
  # or if I define two separate functions, one for x and one for y, so as to avoid $
  geom_point(aes(xmin, ymin), 
             size = 3,
             color = 'black') +
  theme_minimal()+
  transition_states(.frame)+
  ease_aes('sine-in-out')

animate(p, fps = 24)

enter image description here