Skip to content

Instantly share code, notes, and snippets.

@cpsievert
Created February 27, 2017 19:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cpsievert/df05c69d9c141defdfb826b77b3fbde7 to your computer and use it in GitHub Desktop.
Save cpsievert/df05c69d9c141defdfb826b77b3fbde7 to your computer and use it in GitHub Desktop.
Notebook for Microsoft Azure
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating key frame animations in plotly via R\n",
"\n",
"**Note:** This notebook is from the [Key Frame Animations](https://cpsievert.github.io/plotly_book/key-frame-animations.html) section of the [plotly for R](https://cpsievert.github.io/plotly_book) book. In order to run this code, you currently need the development version of plotly:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"Rmd_chunk_options": "eval = FALSE",
"autoscroll": false,
"collapsed": true
},
"outputs": [],
"source": [
"if (!require(devtools)) install.packages(\"devtools\")\n",
"devtools::install_github(\"ropensci/plotly\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Both `plot_ly()` and `ggplotly()` support [key frame](https://en.wikipedia.org/wiki/Key_frame) animations through the `frame` attribute/aesthetic. They also support an `ids` attribute/aesthetic to ensure smooth transitions between objects with the same id (which helps facilitate [object constancy](https://bost.ocks.org/mike/constancy/)). The figure below recreates the famous gapminder animation of the evolution in the relationship between GDP per capita and life expectancy evolved over time. The data is recorded on a yearly basis, so the year is assigned to `frame`, and each point in the scatterplot represents a country, so the country is assigned to `ids`, ensuring a smooth transition from year to year for a given country.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"Rmd_chunk_options": "animation-ggplotly, fig.cap = \"Animation of the evolution in the relationship between GDP per capita and life expectancy in numerous countries.\", screenshot.alt = \"screenshots/animation-ggplotly\"",
"autoscroll": false,
"collapsed": true
},
"outputs": [],
"source": [
"suppressMessages(library(plotly))\n",
"data(gapminder, package = \"gapminder\")\n",
"gg <- ggplot(gapminder, aes(gdpPercap, lifeExp, color = continent)) +\n",
" geom_point(aes(size = pop, frame = year, ids = country)) +\n",
" scale_x_log10()\n",
"ggplotly(gg, height = 400)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"As long as a `frame` variable is provided, an animation is produced with play/pause button(s) and a slider component for controlling the animation. These components can be removed or customized via the `animation_button()` and `animation_slider()` functions. Moreover, various animation options, like the amount of time between frames, the smooth transition duration, and the type of transition easing may be altered via the `animation_opts()` function. The figure above shows the same data as the figure below, but doubles the amount of time between frames, uses linear transition easing, places the animation buttons closer to the slider, and modifies the default `currentvalue.prefix` settings for the slider.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"Rmd_chunk_options": "animation-opts, fig.cap = \"Modifying animation defaults with `animation_opts()`, `animation_button()`, and `animation_slider()`.\", screenshot.alt = \"screenshots/animation-opts\"",
"autoscroll": false,
"collapsed": true
},
"outputs": [],
"source": [
"base <- gapminder %>%\n",
" plot_ly(x = ~gdpPercap, y = ~lifeExp, size = ~pop,\n",
" text = ~country, hoverinfo = \"text\", height = 400) %>%\n",
" layout(xaxis = list(type = \"log\"))\n",
"\n",
"base %>%\n",
" add_markers(color = ~continent, frame = ~year, ids = ~country) %>%\n",
" animation_opts(1000, easing = \"elastic\") %>%\n",
" animation_button(\n",
" x = 1, xanchor = \"right\", y = 0, yanchor = \"bottom\"\n",
" ) %>%\n",
" animation_slider(\n",
" currentvalue = list(prefix = \"YEAR \", font = list(color=\"red\"))\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"If `frame` is a numeric variable (or a character string), frames are always ordered in increasing (alphabetical) order; but for factors, the ordering reflects the ordering of the levels. Consequently, factors provide the most control over the ordering of frames. In the figure below, the continents (i.e., frames) are ordered according their average life expectancy across countries within the continent. Furthermore, since there is no meaningful relationship between objects in different frames of the visualiztion below, the smooth transition duration is set to 0. This helps avoid any confusion that there is a meaningful connection between the smooth transitions. Note that these options control both animations triggered by the play button or via the slider.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"Rmd_chunk_options": "animation-factors, fig.cap = \"Animation of GDP per capita versus life expectancy by continent. The ordering of the contintents goes from lowest average (across countries) life expectancy to highest.\", screenshot.alt = \"screenshots/animation-factors\"",
"autoscroll": false,
"collapsed": true
},
"outputs": [],
"source": [
"meanLife <- with(gapminder, tapply(lifeExp, INDEX = continent, mean))\n",
"gapminder$continent <- factor(\n",
" gapminder$continent, levels = names(sort(meanLife))\n",
")\n",
"\n",
"base %>%\n",
" add_markers(data = gapminder, frame = ~continent) %>%\n",
" hide_legend() %>%\n",
" animation_opts(frame = 1000, transition = 0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Both the `frame` and `ids` attributes operate on the trace level -- meaning that we can target specific layers of the graph to be animated. One obvious use case for this is to provide a background which displays every possible frame (which is not animated) and overlay the animated frames onto that background. The figure below shows the same information as the previous one, but layers animated frames on top of a background of all the frames. As a result, it is easier to put a specific year into a global context.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"Rmd_chunk_options": "animation-targets, fig.cap = \"Overlaying animated frames on top of a background of all possible frames.\", screenshot.alt = \"screenshots/animation-targets\"",
"autoscroll": false,
"collapsed": true
},
"outputs": [],
"source": [
"base %>%\n",
" add_markers(color = ~continent, alpha = 0.2, showlegend = F) %>%\n",
" add_markers(color = ~continent, frame = ~year, ids = ~country) %>%\n",
" animation_opts(1000)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "R",
"language": "R",
"name": "ir"
},
"language_info": {
"codemirror_mode": "r",
"file_extension": ".r",
"mimetype": "text/x-r-source",
"name": "R",
"pygments_lexer": "r"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment