I have created two animated time series charts based on the same dataset. One is a line chart showing the trend in the total, and one is a bar chart showing the top 5 sub-groups. I want to see if I can combine these into one time series animation, either side by side or with the line chart as a small inset.
I've built the separate animations, but its unclear to me if I can combine them, or if I need to create a new image with each frame having the inset, and then animate that.
Here's a minimal example dataset:
data <- data.frame(
Year = c(rep(1988, 5), rep(1989, 5), rep(1990, 5)),
Total = c(rep(1000, 5), rep(1100, 5), rep(1200, 5),
SubGroup = c("A", "B", "C", "D", "E", "B", "A", "C", "D", E", "B", "C", "A", "E", "D"),
Amount = c(200, 180, 100, 80, 60, 210, 200, 150, 100, 80, 250, 240, 200, 150, 110)
)
Here's my code for the top 5 barchart animation:
# generate top 5 ranking by year
anim_table <- data %>%
dplyr::group_by(Year) %>%
dplyr::mutate(
rank = min_rank(-Amount) * 1,
Value_rel = Amount / Amount[rank == 1],
Value_lbl = paste0(" ", Amount)
) %>%
dplyr::filter(rank <= 5) %>%
dplyr::ungroup() %>%
dplyr::arrange(Year, rank)
# create static barchart
p1 <- ggplot2::ggplot(anim_table, aes(rank)) +
ggplot2::geom_tile(aes(
y = Amount / 2,
height = Amount,
width = 0.9,
fill = "blue"
), alpha = 0.8, color = NA) +
ggplot2::geom_text(aes(y = 0, label = paste(SubGroup, " ")), size = 12, vjust = 0.2, hjust = 1) +
ggplot2::geom_text(aes(y = Amount, label = Value_lbl, hjust = 0)) +
ggplot2::coord_flip(clip = "off", expand = FALSE) +
ggplot2::scale_y_continuous(labels = scales::comma) +
ggplot2::scale_x_reverse() +
ggplot2::guides(color = FALSE, fill = FALSE) +
ggplot2::labs(
title = "{closest_state}", x = "", y = "Amount",
caption = "Source: whatever"
) +
ggplot2::theme(
plot.title = element_text(color = "darkblue", face = "bold", hjust = 0, size = 30),
axis.ticks.y = element_blank(),
axis.text.y = element_blank(),
plot.margin = margin(2, 2, 1, 16, "cm")
) +
# animate over Years
gganimate::transition_states(Year, transition_length = 4, state_length = 1) +
gganimate::ease_aes("cubic-in")
gganimate::animate(p1, 200, fps = 5, duration = 100, width = 2000, height = 1200, renderer = gifski_renderer("anim1.gif"))
And here is the code for my simple animated line chart:
p2 <- ggplot2::ggplot(
hc_total,
aes(Year, Total)
) +
ggplot2::geom_line() +
ggplot2::scale_color_viridis_d() +
ggplot2::labs(x = "Year", y = "Total") +
gganimate::transition_reveal(Year)
gganimate::animate(p2, 200, fps = 5, duration = 100, width = 2000, height = 1200, renderer = gifski_renderer("anim2.gif"))
Looking to work out how to combine these two animations into a single animation either side by side or with the line chart as an inset.
You can do that with ImageMagick. The two GIFs must have the same number of frames and preferably the same size.
For Windows, I wrote this R function which runs ImageMagick:
appendGIFs <- function(gif1, gif2, gifout, vertically=FALSE,
delay=20, convert = "C:/PortableApps/ImageMagick/convert"){
command <- sprintf("%s ( %s -coalesce -set page %s+0+0 -coalesce ) null: ( %s -coalesce ) -gravity %s -layers composite -set delay %d -loop 0 %s",
convert, gif1, ifelse(vertically, "%[w]x%[fx:h*2]", "%[fx:w*2]x%[h]"),
gif2, ifelse(vertically, "south", "east"), delay, gifout)
system(command)
}
Here, gif1
is the path to the first GIF, gif2
is the path to the second GIF, gifout
is the name of the output GIF, convert
is the path to the convert
executable of ImageMagick (may be you can set convert = "magick convert"
if ImageMagick is in your path).
For Linux, here is the command to run in order to append side by side:
convert file1.gif'[0]' -coalesce \\( file2.gif'[0]' -coalesce \\) \\
+append -channel A -evaluate set 0 +channel \\
file1.gif -coalesce -delete 0 \\
null: \\( file2.gif -coalesce \\) \\
-gravity East -layers Composite output.gif
To append vertically:
convert file1.gif'[0]' -coalesce \\( file2.gif'[0]' -coalesce \\) \\
-append -channel A -evaluate set 0 +channel \\
file1.gif -coalesce -delete 0 \\
null: \\( file2.gif -coalesce \\) \\
-gravity South -layers Composite output.gif
For Mac, I don't know (I would try the Linux way).
The following should work on any OS.
appendGIFs <- function(gif1, gif2, gifout="result.gif", vertically=FALSE,
delay=20, convert = "C:/PortableApps/ImageMagick/convert"){
currentdir <- getwd()
on.exit(setwd(currentdir))
tmpdir <- tempdir()
invisible(file.remove(list.files(tmpdir, full.names = TRUE, pattern = "*.gif$")))
file.copy(gif1, to = file.path(tmpdir, "gif1.gif"))
file.copy(gif2, to = file.path(tmpdir, "gif2.gif"))
setwd(tmpdir)
command <- sprintf("%s gif1.gif -coalesce gif1-%%05d.gif", convert)
system(command)
command <- sprintf("%s gif2.gif -coalesce gif2-%%05d.gif", convert)
system(command)
nframes <- length(list.files(tmpdir, pattern = "^gif1-.*gif$"))
for(i in 1:nframes){
command <- sprintf("%s gif1-%05d.gif gif2-%05d.gif %sappend gif-%05d.gif",
convert, i-1, i-1, ifelse(vertically, "-", "+"), i)
system(command)
}
command <- sprintf("%s -loop 0 -delay %d gif-*.gif result.gif", convert, delay)
system(command)
file.copy("result.gif", file.path(currentdir, gifout), overwrite=TRUE)
}