为什么 gganimate 无法按 transition_reveal 的日期正确排序行?

Why does gganimate fail to order lines correctly by date with transition_reveal?

我打算用 gganimate 重现 Ed Hawkins 在 R 中关于气候变化的动画人物。这个数字叫做climate spiral。虽然静态 ggplot 图按年份显示了正确的行顺序(最新数据在顶部),但带有 transition_reveal() 的动画图导致行的顺序错误。

这是一个带有合成数据的可重现示例代码:

library(tidyverse)
library(lubridate)
library(gganimate)
library(RColorBrewer) 

# Create monthly data from 1950 to 2020 (and a component for rising values with time)
df <- tibble(year = rep(1950:2020, each = 12), 
             month = rep(month.abb, 2020-1950+1)) %>%
  mutate(date = dmy(paste("01",month,year)),
         value = rnorm(n(), 0, 2) + row_number()*0.005) %>%
  with_groups(year, mutate, value_yr = mean(value))


temp <- df %>%
  ggplot(aes(x = month(date, label=T), y = value, color = value_yr)) +
  geom_line(size = 0.6, aes(group = year)) +
  geom_hline(yintercept = 0, color = "white") +
  geom_hline(yintercept = c(-4,4), color = c("skyblue3","red1"), size = 0.2) +
  geom_vline(xintercept = 1:12, color = "white", size = 0.2) +
  annotate("label", x = 12.5, y = c(-4,0,4), label = c("-4°C","0°C","+4°C"), 
           color = c("skyblue3","white","red1"), size = 2.5, fill = "#464950", 
           label.size = NA, label.padding = unit(0.1, "lines"),) +
  geom_point(x = 1, y = -11, size = 15, color = "#464950") + 
  geom_label(aes(x = 1, y = -11, label = year), 
             color = "white", size = 4, 
             fill = "#464950", label.size = NA) +
  coord_polar(start = 0) +
  scale_color_gradientn(colors = rev(brewer.pal(n=11, name = "RdBu")),
                        limits = range(df$value_yr)) +
  labs(x = "", y = "") + 
  theme_bw() +
  theme(panel.background = element_blank(),
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        plot.background=element_rect(fill="#464950", color="#464950"),
        axis.text.x = element_text(margin = margin(t = -20, unit = "pt"), 
                                   color = "white"),
        axis.text.y = element_blank(), 
        axis.ticks = element_blank(),
        legend.position = "none") 

现在,我们可以将绘图保存为 PNG 或制作动画并保存为 GIF:

ggsave(temp, filename = "test.png", width = 5, height = 5, dpi = 320)

# Animate by date:
anim <- temp +
  transition_reveal(date) +
  ease_aes('linear')
  
output <- animate(anim, nframes = 100, end_pause = 30,
                  height = 5, width = 5, units = "in", res = 300)

anim_save("test.gif", output)

让我们看看结果!

静态 PNG:

GIF 动画:

乍一看,结果是一样的,但细节显示出差异(例如,标记的蓝线)。

在这个包含合成数据的示例代码中,差异很小。但是对于真实数据,这些数字看起来非常不同,因为许多红线(最近的高温数据点)在背景中消失了。那么,如何按日期保留 transition_reveal() 中的订单?感谢任何帮助,非常感谢!

这本身并不是答案。这就是为什么。鉴于此信息,您必须告诉我您喜欢什么,以便我为您提供解决方案。

我尝试了一些东西——我确信每一个都可以,但没有。所以,我想看看 ggplot 中发生了什么。我的直觉被证明是正确的。您的数据在 png 中的顺序是 value_yr,而不是 year

我在最后重复这个问题:

Either you can put the animation in order of value_yr or you can put the color in ggplot in order by year. Which would you prefer?

我怎么知道?我提取了对象中指定的颜色。

tellMe <- ggplot_build(temp)$data[[1]]
head(tellMe)
#    colour x           y group PANEL flipped_aes size linetype alpha
# 1 #1E60A4 1 -1.75990067     1     1       FALSE  0.6        1    NA
# 2 #1E60A4 2 -0.08968196     1     1       FALSE  0.6        1    NA
# 3 #1E60A4 3 -0.69657130     1     1       FALSE  0.6        1    NA
# 4 #1E60A4 4 -0.10777727     1     1       FALSE  0.6        1    NA
# 5 #1E60A4 5  1.57710505     1     1       FALSE  0.6        1    NA
# 6 #1E60A4 6  1.63277369     1     1       FALSE  0.6        1    NA 

gimme <- tellMe %>% group_by(group) %>% 
  summarise(color = unique(colour)) %>% 
  print(n = 100) # there are less than 100, I just want them all

head(gimme)
# # A tibble: 6 × 2
#   group color  
#   <int> <chr>  
# 1     1 #1E60A4
# 2     2 #114781
# 3     3 #175290
# 4     4 #053061
# 5     5 #1C5C9E
# 6     6 #3E8BBF 

对我来说,这表明颜色不是按组顺序排列的,所以我想查看颜色以可视化顺序。

我用过这个功能。我知道它来自一个演示,但我不记得是哪一个。我只是为了把它包括在这里,但我没有找到它。

# this is from a demo (not sure which one anymore!
showCols <- function(cl=colors(), bg = "lightgrey",
                     cex = .75, rot = 20) {
  m <- ceiling(sqrt(n <-length(cl)))
  length(cl) <- m*m; cm <- matrix(cl, m)
  require("grid")
  grid.newpage(); vp <- viewport(w = .92, h = .92)
  grid.rect(gp=gpar(fill=bg))
  grid.text(cm, x = col(cm)/m, y = rev(row(cm))/m, rot = rot,
            vp=vp, gp=gpar(cex = cex, col = cm))
}

showCols(gimme$color)

左上角的颜色是最早的年份,下面的数值是次年,依此类推。最近的年份是 right-most 列中的最低值。

df %>% group_by(yr) %>% summarise(value_yr = unique(value_yr))
# they are in 'value_yr' order in ggplot, not year
# # A tibble: 71 × 2
#       yr value_yr
#    <int>    <dbl>
#  1  1950  0.0380 
#  2  1951 -0.215  
#  3  1952 -0.101  
#  4  1953 -0.459  
#  5  1954 -0.00130
#  6  1955  0.559  
#  7  1956 -0.457  
#  8  1957 -0.251  
#  9  1958  1.10   
# 10  1959  0.282  
# # … with 61 more rows 

Either you can put the animation in order of value_yr or you can put the color in ggplot in order by year. Which would you prefer?



更新

您不会使用 transition_reveal 对同一元素进行分组和 t运行sition。不幸的是,我不能告诉你为什么,但它似乎卡在了 1958!

要使左侧的 gif 与右侧的 ggplot png 匹配:

首先,我修改了对 ggplotgeom_line

的调用
  ggplot(aes(x = month(date, label = T), y = value, 
             group = yr, color = yr)) +
  geom_line(size = .6)

然后我尝试使用 transition_reveal,但注意到随后的年份被分层 其他年份之下。我无法解释这种奇怪的行为。当我 运行 showCol 更改 temp, 后,颜色是有序的。这排除了我最初认为的问题。

我修改了对象 anim,使用 transition_manual 强制绘制图层的顺序。

anim <- temp +
  transition_manual(yr, cumulative = T) +
  ease_aes('linear')

就是这样。现在层匹配。 至于这在您更改颜色分配之前是否可行:原始图,左侧为年度手动 t运行 版本,右侧为 ggplot png:

看起来那样也行得通。所以,我最初的 drawn-out 解释并没有我想象的那么有用,但至少你现在有了一个可行的解决方案。 (叹气。)