class: center, middle, inverse, title-slide #
gganimate with a Spooky Twist ### Adapted from Katherine Goode, ISU Graphics Group, October 31, 2019 ### Updated: 2022-11-01 --- <style> .remark-slide-content { background-color: #FFFFFF; border-top: 80px solid #F9C389; font-size: 20px; font-weight: 300; line-height: 1.5; padding: 1em 2em 1em 2em } .inverse { background-color: #696767; border-top: 80px solid #696767; text-shadow: none; background-image: url(https://github.com/goodekat/presentations/blob/master/2019-isugg-gganimate-spooky/figures/spider.png?raw=true); background-position: 50% 75%; background-size: 150px; } .your-turn{ background-color: #8C7E95; border-top: 80px solid #F9C389; text-shadow: none; background-image: url(https://github.com/goodekat/presentations/blob/master/2019-isugg-gganimate-spooky/figures/spider.png?raw=true); background-position: 95% 90%; background-size: 75px; } .title-slide { background-color: #F9C389; border-top: 80px solid #F9C389; background-image: none; } .title-slide > h1 { color: #111111; font-size: 40px; text-shadow: none; font-weight: 400; text-align: left; margin-left: 15px; padding-top: 80px; } .title-slide > h2 { margin-top: -25px; padding-bottom: -20px; color: #111111; text-shadow: none; font-weight: 300; font-size: 35px; text-align: left; margin-left: 15px; } .title-slide > h3 { color: #111111; text-shadow: none; font-weight: 300; font-size: 25px; text-align: left; margin-left: 15px; margin-bottom: -30px; } </style> <style type="text/css"> .left-code { color: #777; width: 40%; height: 92%; float: left; } .right-plot { width: 59%; float: right; padding-left: 1%; } </style> # Overview .pull-left[ 1. Introduction 2. Transition functions 3. View functions 4. Shadow functions 5. Other ] .pull-right[ <img src="figures/cat.jpg" alt="Black cat on a stool dressed with wings dressed as a bat" width="807" height="500px" style="display: block; margin: auto;" /> ] --- class: inverse, middle, center # Introduction --- # What is gganimate? - An extension to ggplot2 for creating animated graphics in R - Originally written by David Robinson - Updated by Thomas Lin Pedersen in 2018/2019 - Code from a year(ish) ago will probably not work anymore - Uses the R package [tweenr](https://github.com/thomasp85/tweenr) (also by Lin Pedersen) - Pedersen describes it as... > "a theoretical backbone of how to describe animation, and it's also an implementation of set theoretical backbones." <img src="figures/logo.png" alt="gganimate logo" width="150px" style="display: block; margin: auto 0 auto auto;" /> --- # gapminder Example - life expectancy <img src="slides_files/figure-html/gapminder-min-year-1.png" alt="Scatterplot of GDP per capita vs. life expectancy in 1952 where the size of each point is the population of the country and the countries are coloured by continent. African nations are in the lower left corner with life expectancies between 30 and 50 years. The Americas show a greater spread with life Expectancy between 40 and 70. Asia shows a wider spread in GDP per capita with most countries clustered around a life expectancy of 50 years. Most European countries have a life expectancy of between 60 and 75 years." style="display: block; margin: auto;" /> --- # gapminder Example - life expectancy <img src="slides_files/figure-html/gapminder-max-year-1.png" alt="Scatterplot of GDP per capita vs. life expectancy in 2007 where the size of each point is the population of the country and the countries are coloured by continent. African nations show a larger spread in GDP per capita with life expectancy ranging between 40 and 75 years. In the Americas and Europe most countries' life expectancy ranges from 70 to 80 with life Expectancy. Asia shows a wider spread in GDP per capita with most countries clustered around a life expectancy of 60 to 80 years. All continents show a positive relationship between life expectancy and GDP per capita." style="display: block; margin: auto;" /> --- # gapminder Example <img src="slides_files/figure-html/gapminder-1.gif" alt="Animation of a scatterplot of GDP per capita vs. life expectancy in 1 year increments from 1952-2007 where the size of each point is the population of the country and the countries are coloured by continent. All continents show an increase in the life expectancy over time and there appears to be a steady positive relationship between GDP per capita and life expectancy. By the end of 2007, African nations show a larger spread in GDP per capita with life expectancy ranging between 40 and 75 years. In the Americas and Europe most countries' life expectancy ranges from 70 to 80 with life Expectancy. Asia shows a wider spread in GDP per capita with most countries clustered around a life expectancy of 60 to 80 years. All continents show a positive relationship between life expectancy and GDP per capita." style="display: block; margin: auto;" /> --- # How does it work? From the documentation... > gganimate provides "a range of new grammar classes that can be added to the plot object in order to customize how it should change with time." <br> | New Grammar Classes | | | :----- | :------------ | | transitions | defines how the data changes in the graphic across time | | views | defines changes in the viewpoint of the graphic (i.e. x and y axes) | | shadows | defines how to display "memories" in the animation | | enter/exit | defines how the data should appear and disappear during the animation | | ease_aes | defines the aesthetics associated with the ease during transitions | --- # Set up Packages to load if you are following along ```r # Installs gganimate install.packages("gganimate") # Loads the library library(gganimate) # Other libraries that will be used library(ggplot2) library(tidyverse) ``` --- # Bat Data Study recorded the flying trajectories of bats (individual and paired) - **Paper**: Giuggioli L, McKetterick TJ, Holderied M (2015) Delayed response and biosonar perception explain movement coordination in trawling bats. PLOS Computational Biology. [doi:10.1371/journal.pcbi.1004089.t001](https://journals.plos.org/ploscompbiol/article/figure?id=10.1371/journal.pcbi.1004089.t001) - **Original Data**: Holderied M, Giuggioli L, McKetterick TJ (2015) Data from: Delayed response and biosonar perception explain movement coordination in trawling bats. Movebank Data Repository. [doi:10.5441/001/1.62h1f7k9](https://www.datarepository.movebank.org/handle/10255/move.421) - **My Subset of the Data**: - extracted the first 5 individual bat trajectories - selected 4 variables - `id`: bat id - `time`: time of observed location (in milliseconds) - `latitude`: latitude at the time of observation - `longitude`: longitude at the time of observation --- # Bat Data **How to access my subset of the data:** ```r # Link where the data are stored link <- "https://raw.githubusercontent.com/goodekat/presentations/master/2019-isugg-gganimate-spooky/bat-data/bats-subset.csv" ``` ```r # Access the data and convert id to a factor bats <- read.csv(link) %>% mutate(id = factor(id)) ``` ```r # Data structure str(bats) ``` ``` ## 'data.frame': 1743 obs. of 4 variables: ## $ id : Factor w/ 5 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ... ## $ time : num 0 0.019 0.039 0.059 0.079 0.099 0.119 0.139 0.16 0.18 ... ## $ latitude : num 51.4 51.4 51.4 51.4 51.4 ... ## $ longitude: num -2.66 -2.66 -2.66 -2.66 -2.66 ... ``` --- # Bat Data Plot of the 5 bat trajectories <img src="slides_files/figure-html/unnamed-chunk-3-1.png" style="display: block; margin: auto;" /> --- # Basics for creating a gganimation **Step 1**: Start with the ggplot data and mapping .left-code[ ```r *bats %>% * filter(id == 1) %>% * ggplot(aes(x = longitude, * y = latitude, * color = time)) ``` ] .right-plot[ <img src="slides_files/figure-html/output1-1.png" style="display: block; margin: auto;" /> ] --- # Basics for creating a gganimation **Step 2**: Add layers using geoms to create static .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude, color = time)) + * geom_point() ``` ] .right-plot[ <img src="slides_files/figure-html/output2-1.png" style="display: block; margin: auto;" /> ] --- # Basics for creating a gganimation **Step 3**: Add formatting options .left-code[ ```r ggplot(bats %>% filter(id == 1), aes(x = longitude, y = latitude, color = time)) + geom_point() + * scale_color_gradient2( * midpoint = 6, * low = "orange", * mid = "purple", * high = "black") ``` ] .right-plot[ <img src="slides_files/figure-html/output3-1.png" style="display: block; margin: auto;" /> ] --- # Basics for creating a gganimation **Step 4**: Add gganimate options .left-code[ ```r *bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude, color = time)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + * transition_reveal(time) ``` ] .right-plot[ <img src="slides_files/figure-html/output4-1.gif" style="display: block; margin: auto;" /> ] --- class: inverse, middle, center # Transitions --- # Transition: <font color="black"> States </font> `transition_states`: Transition between several *distinct stages* of the data Options: - `states`: The unquoted name of the column holding the state levels in the data. - `transition_length`: The relative length of the transition. Will be recycled to match the number of states in the data - `state_length`: The relative length of the pause at the states. Will be recycled to match the number of states in the data - `wrap = TRUE`: Should the animation wrap-around? If TRUE the last state will be transitioned into the first. --- # Transition: <font color="black"> States </font> Static plot of the 5 trajectories colored by time .left-code[ ```r bats %>% ggplot(aes(x = longitude, y = latitude, color = time)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") ``` ] .right-plot[ <img src="slides_files/figure-html/statesout1-1.png" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> States </font> Add `transition_states` with `states = id` Something is a bit strange... .left-code[ ```r bats %>% ggplot(aes(x = longitude, y = latitude, color = time)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + labs(title = 'Bat ID: {closest_state}') + * transition_states(states = id) ``` ] .right-plot[ <img src="slides_files/figure-html/statesout2-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> States </font> From the documentation on the importance of specifying the `group`: <br> > The group aesthetic, if not set, will be calculated from the interaction of all discrete aesthetics in the layer (excluding label) > It is often better to set it explicitly when animating, to make sure your data is interpreted in the right way. > Unmatched rows will appear/disappear, potentially using an enter or exit function. --- # Transition: <font color="black"> States </font> Add `group = id` .left-code[ ```r bats %>% ggplot(aes(x = longitude, y = latitude, color = time, * group = id)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + labs(title = 'Bat ID: {closest_state}') + transition_states(states = id) ``` ] .right-plot[ <img src="slides_files/figure-html/statesout3-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> States </font> Change the transition length and time .left-code[ ```r bats %>% ggplot(aes(x = longitude, y = latitude, color = time, group = id)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + labs(title = "Bat {closest_state}") + transition_states( states = id, * transition_length = 3, * state_length = 3) ``` ] .right-plot[ <img src="slides_files/figure-html/statesout4-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="#8B16CE"> Time </font> `transition_time`: Transition through distinct states in *time* Options: - `time`: An unquoted expression giving the time, and thus state membership, of each observation. - `range`: The time range to animate. If NULL it will be set to the range of time --- # Transition: <font color="#8B16CE"> Time </font> Static plot of trajectory for bat 1 .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() ``` ] .right-plot[ <img src="slides_files/figure-html/timeout1-1.png" style="display: block; margin: auto;" /> ] --- # Transition: <font color="#8B16CE"> Time </font> Add `transition_time` with `time = time` Duplicate points appearing in the same frame .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + * transition_time(time = time) ``` ] .right-plot[ <img src="slides_files/figure-html/timeout2-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="#8B16CE"> Time </font> Try adjusting the number of frames using `animate` .left-code[ ```r *time_plot <- bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + transition_time(time = time) *animate(plot = time_plot, * nframes = 300) ``` ] .right-plot[ <img src="slides_files/figure-html/timeout3-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="#8B16CE"> Time </font> Example of adjusting the time range .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + transition_time(time = time, * range = c(1, 3)) ``` ] .right-plot[ <img src="slides_files/figure-html/timeout4-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> `transition_reveal`: Reveal data along a given *dimension* Options: - `along`: An unquoted expression giving the dimension to tween along. For a gradually revealing time series this should be set to the same as the x aesthetic. - `range`: The time range to animate. If NULL it will be set to the range of along - `keep_last = TRUE`: For non-path/polygon layers should the last row be kept for subsequent frames. --- # Transition: <font color="black"> Reveal </font> Static plot of trajectory for bat 1 (again) .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() ``` ] .right-plot[ <img src="slides_files/figure-html/revealout1-1.png" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> Add `transition_reveal` with `along = time` Note: No duplicated observations in a frame .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + * transition_reveal(along = time) ``` ] .right-plot[ <img src="slides_files/figure-html/revealout2-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> `transition_time` vs `transition_reveal` (from the documentation): > In contrast to `transition_time()`, `transition_reveal()` calculates intermediary values at exact positions instead of coercing raw values into the closest frame. > It further keeps old data for path and polygon type layers so that they are gradually build up instead of being a set of disconnected segments... --- # Transition: <font color="black"> Reveal </font> Change to using `geom_path` instead of `geom_point` - allows data to gradually appear with `transition_reveal` .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + * geom_path() + transition_reveal(along = time) ``` ] .right-plot[ <img src="slides_files/figure-html/revealout3-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> If using both `geom_path` and `geom_point`, the history of the path will appear for the whole animation, but the point will only appear at the current time. .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + * geom_path() + * geom_point() + transition_reveal(time) ``` ] .right-plot[ <img src="slides_files/figure-html/revealout4-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> History of points can be kept by assigning the `along` variable to a group .left-code[ ```r bats %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_path() + * geom_point(aes(group = time)) + transition_reveal(along = time) ``` ] .right-plot[ <img src="slides_files/figure-html/revealout5-1.gif" style="display: block; margin: auto;" /> ] --- # Transition: <font color="black"> Reveal </font> Use the `ggimage` R package to replace the point with an image .left-code[ ```r library(ggimage) bat_image_link <- "https://raw.githubusercontent.com/goodekat/presentations/master/2019-isugg-gganimate-spooky/figures/bat-cartoon.png" bats %>% mutate( * image = bat_image_link ) %>% filter(id == 1) %>% ggplot(aes(x = longitude, y = latitude)) + geom_path() + * geom_image(aes(image = image), * size = 0.3) + transition_reveal(time) ``` ] .right-plot[ <img src="slides_files/figure-html/revealout6-1.gif" style="display: block; margin: auto;" /> ] --- class: your-turn # Your Turn Use a transition to create the animation shown below. If you want to use the same colors, add `scale_color_manual(values = bat_colors)` with `bat_colors <- c("darkorange", "orangered", "violetred", "purple", "black")`. <img src="slides_files/figure-html/unnamed-chunk-4-1.gif" style="display: block; margin: auto;" /> --- class: your-turn # Your Turn: Static Version ```r bats %>% mutate(image = bat_image_link) %>% ggplot(aes(x = longitude, y = latitude, group = id, color = id)) + geom_path() + geom_image(aes(image = image), size = 0.1) + scale_color_manual(values = bat_colors) ``` --- class: your-turn # Your Turn: Solution ```r bats %>% mutate(image = bat_image_link) %>% ggplot(aes(x = longitude, y = latitude, group = id, color = id)) + geom_path() + geom_image(aes(image = image), size = 0.1) + scale_color_manual(values = bat_colors) + transition_reveal(along = time) ``` <img src="slides_files/figure-html/unnamed-chunk-6-1.gif" style="display: block; margin: auto;" /> --- # Other Transitions <br> | Function | Use | | :-------- | :---------------- | | transition_events | Transition individual events in and out | | transition_filter | Transition between different filters | | transition_layers | Build up a plot, layer by layer | | transition_components | Transition individual components through their own lifecycle | | transition_manual | Create an animation by specifying the frame membership directly | | transition_null | Keep all data constant across the animation | --- class: inverse, middle, center # Views --- # View: <font color="#8B16CE"> Follow </font> Adjusts the axes to follow the data in the frame .left-code[ ```r bats %>% mutate(image = bat_image_link) %>% filter(id == 3) %>% ggplot(aes(x = longitude, y = latitude)) + geom_image(aes(image = image), size = 0.1) + geom_path() + transition_reveal(time) + * view_follow() ``` ] .right-plot[ <img src="slides_files/figure-html/view_follow_out-1.gif" style="display: block; margin: auto;" /> ] --- # View: <font color="black"> Step </font> Follows the data but switches between being static and changing to the range of the data .left-code[ ```r bats %>% mutate(image = bat_image_link) %>% filter(id == 3) %>% ggplot(aes(x = longitude, y = latitude)) + geom_image(aes(image = image), size = 0.1) + geom_path() + transition_reveal(time) + * view_step(pause_length = 3, * step_length = 1, * nsteps = 5) #<< ``` ] .right-plot[ <img src="slides_files/figure-html/view_step_out1-1.gif" style="display: block; margin: auto;" /> ] --- # View: <font color="black"> Step </font> Works will with `transistion_states` .left-code[ ```r bats %>% ggplot(aes(x = longitude, y = latitude, color = time, group = id)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black" ) + transition_states(states = id) + * view_step(pause_length = 3, * step_length = 1, * nsteps = 5) ``` ] .right-plot[ <img src="slides_files/figure-html/view_step_out2-1.gif" style="display: block; margin: auto;" /> ] --- # Other Views | Function | Use | | -------- | :----------------- | | view_zoom | Pan and zoom smoothly between different states | | view_static | Keep a fixed view that include all of the data | --- class: your-turn # Your Turn Apply a viewing option to the animation from the last 'your turn' to create an animation such as the one below. <img src="slides_files/figure-html/unnamed-chunk-7-1.gif" style="display: block; margin: auto;" /> --- class: your-turn # Your Turn: Solution ```r bats %>% mutate(image = bat_image_link) %>% ggplot(aes(x = longitude, y = latitude, group = id, color = id)) + geom_path() + geom_image(aes(image = image), size = 0.1) + scale_color_manual(values = bat_colors) + transition_reveal(along = time) + view_follow() ``` <img src="slides_files/figure-html/unnamed-chunk-8-1.gif" style="display: block; margin: auto;" /> --- class: inverse, middle, center # Shadows --- # Shadow: <font color="#8B16CE"> Wake </font> Leaves a "wake" behind the points as they move through time - options allow for the adjustment of length of wake, size of wake, and so forth .left-code[ ```r bats %>% filter(id == 3) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + transition_reveal(time) + * shadow_wake(wake_length = 0.1, * alpha = 0.5) ``` ] .right-plot[ <img src="slides_files/figure-html/shadow_wake_out-1.gif" style="display: block; margin: auto;" /> ] --- # Shadow: <font color="black"> Trail </font> Leaves points behind as a memory of the trajectory the points followed (keeps every nth frame) - allows for the adjustments of the distance between points to remember, alpha of memory, and so forth .left-code[ ```r bats %>% filter(id == 3) %>% ggplot(aes(x = longitude, y = latitude)) + geom_point() + transition_reveal(time) + * shadow_trail(distance = 0.01, * alpha = 0.5, * shape = 2) ``` ] .right-plot[ <img src="slides_files/figure-html/shadow_trail_out-1.gif" style="display: block; margin: auto;" /> ] --- # Other Shadows | Function | Use | | -------- | :----------------- | | shadow_mark | Show original data as background marks | | shadow_null | A non-existent shadow | --- class: your-turn # Your Turn Apply use a shadow option to create an animation such as the one below <img src="slides_files/figure-html/unnamed-chunk-9-1.gif" style="display: block; margin: auto;" /> --- class: your-turn # Your Turn: Solution ```r bats %>% mutate(image = bat_image_link) %>% ggplot(aes(x = longitude, y = latitude, group = id, color = id)) + geom_point() + scale_color_manual(values = bat_colors) + transition_reveal(along = time) + shadow_trail(distance = 0.2, alpha = 0.5, colour = 'grey92') ``` <img src="slides_files/figure-html/unnamed-chunk-10-1.gif" style="display: block; margin: auto;" /> --- class: inverse, middle, center # Other --- # Enter/Exit Specifies how the objects transition from one time to the next .pull-left[ `enter_manual()` `enter_appear()` `enter_fade()` `enter_grow()` `enter_recolour()` `enter_recolor()` `enter_fly()` `enter_drift()` `enter_reset()` ] .pull-right[ `exit_manual()` `exit_disappear()` `exit_fade()` `exit_shrink()` `exit_recolour()` `exit_recolor()` `exit_fly()` `exit_drift()` `exit_reset()` ] --- # Enter/Exit .left-code[ ```r bats %>% ggplot(aes( x = longitude, y = latitude, color = time, group = id)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + transition_states( states = id, transition_length = 2, state_length = 1) + * enter_fade() + * exit_fade() + * exit_fly(x_loc = -2.66300, * y_loc = 51.40692) ``` ] .right-plot[ <img src="slides_files/figure-html/enter_exit_out-1.gif" style="display: block; margin: auto;" /> ] --- # ease_aes "defines how the aesthetics associated with the ease during transitions" - `quadratic`: Models a power-of-2 function - `cubic`: Models a power-of-3 function - `quartic`: Models a power-of-4 function - `quintic`: Models a power-of-5 function - `sine`: Models a sine function - `circular`: Models a pi/2 circle arc - `exponential`: Models an exponential function - `elastic`: Models an elastic release of energy - `back`: Models a pullback and release - `bounce`: Models the bouncing of a ball Modifiers: `-in`, `-out`, `-in-out` Demonstrations of the easings: [https://easings.net/en](https://easings.net/en) --- # ease_aes Use `sine-in-out` to smooth the fade transitions .left-code[ ```r bats %>% ggplot(aes( x = longitude, y = latitude, color = time, group = id)) + geom_point() + scale_color_gradient2( midpoint = 6, low = "orange", mid = "purple", high = "black") + transition_states( states = id, transition_length = 2, state_length = 1) + enter_fade() + exit_fade() + * ease_aes('sine-in-out') ``` ] .right-plot[ <img src="slides_files/figure-html/ease_aes_out-1.gif" style="display: block; margin: auto;" /> ] --- # Rendering and Output .pull-left[ Render a gganim object - `animate()` - `print()` - `knit_print.gganim()` <br> Renderers provided by gganimate - `gifski_renderer()` - `file_renderer()` - `av_renderer()` - `ffmpeg_renderer()` - `magick_renderer()` - `sprite_renderer()` ] .pull-right[ Output - `anim_save()`: Save an animation to a file - `last_animation()`: Retrieve the last rendered animation - `frame_vars()`: Access metadata about the frames in an animation - `split_animation()`: Split an animation into chunks ] --- # Resources Official Website: https://gganimate.com/index.html GitHub: https://github.com/thomasp85/gganimate Talks on gganimate: https://gganimate.com/articles/extra/talks.html Workshop by Mitchell O'Hara-Wild: https://github.com/numbats/gganimate-workshop <img src="figures/halloween-dogs.png" width="625px" style="display: block; margin: auto;" />