Edit: keyword is 'bar chart race'
How would you go at reproducing this chart from Jaime Albella in R ?
See the animation on visualcapitalist.com or on twitter (giving several references in case one breaks).
I'm tagging this as ggplot2
and gganimate
but anything that can be produced from R is relevant.
data (thanks to https://github.com/datasets/gdp )
gdp <- read.csv("https://raw.github.com/datasets/gdp/master/data/gdp.csv") # remove irrelevant aggregated values words <- scan( text="world income only total dividend asia euro america africa oecd", what= character()) pattern <- paste0("(",words,")",collapse="|") gdp <- subset(gdp, !grepl(pattern, Country.Name , ignore.case = TRUE))
Edit:
Another cool example from John Murdoch :
Most populous cities from 1500 to 2018
Click the Bar Groups tab. Expand the Bar Charts section. By default, each of the bar charts will be assigned to the same group (Group 1). Click next to one of the bar charts that you would like to be in a different group of stacked bars and select New Group.
Edit: added spline interpolation for smoother transitions, without making rank changes happen too fast. Code at bottom.
I've adapted an answer of mine to a related question. I like to use geom_tile
for animated bars, since it allows you to slide positions.
I worked on this prior to your addition of data, but as it happens, the gapminder
data I used is closely related.
library(tidyverse) library(gganimate) library(gapminder) theme_set(theme_classic()) gap <- gapminder %>% filter(continent == "Asia") %>% group_by(year) %>% # The * 1 makes it possible to have non-integer ranks while sliding mutate(rank = min_rank(-gdpPercap) * 1) %>% ungroup() p <- ggplot(gap, aes(rank, group = country, fill = as.factor(country), color = as.factor(country))) + geom_tile(aes(y = gdpPercap/2, height = gdpPercap, width = 0.9), alpha = 0.8, color = NA) + # text in x-axis (requires clip = "off" in coord_*) # paste(country, " ") is a hack to make pretty spacing, since hjust > 1 # leads to weird artifacts in text spacing. geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) + coord_flip(clip = "off", expand = FALSE) + scale_y_continuous(labels = scales::comma) + scale_x_reverse() + guides(color = FALSE, fill = FALSE) + labs(title='{closest_state}', x = "", y = "GFP per capita") + theme(plot.title = element_text(hjust = 0, size = 22), axis.ticks.y = element_blank(), # These relate to the axes post-flip axis.text.y = element_blank(), # These relate to the axes post-flip plot.margin = margin(1,1,1,4, "cm")) + transition_states(year, transition_length = 4, state_length = 1) + ease_aes('cubic-in-out') animate(p, fps = 25, duration = 20, width = 800, height = 600)
For the smoother version at the top, we can add a step to interpolate the data further before the plotting step. It can be useful to interpolate twice, once at rough granularity to determine the ranking, and another time for finer detail. If the ranking is calculated too finely, the bars will swap position too quickly.
gap_smoother <- gapminder %>% filter(continent == "Asia") %>% group_by(country) %>% # Do somewhat rough interpolation for ranking # (Otherwise the ranking shifts unpleasantly fast.) complete(year = full_seq(year, 1)) %>% mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>% group_by(year) %>% mutate(rank = min_rank(-gdpPercap) * 1) %>% ungroup() %>% # Then interpolate further to quarter years for fast number ticking. # Interpolate the ranks calculated earlier. group_by(country) %>% complete(year = full_seq(year, .5)) %>% mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>% # "approx" below for linear interpolation. "spline" has a bouncy effect. mutate(rank = approx(x = year, y = rank, xout = year)$y) %>% ungroup() %>% arrange(country,year)
Then the plot uses a few modified lines, otherwise the same:
p <- ggplot(gap_smoother, ... # This line for the numbers that tick up geom_text(aes(y = gdpPercap, label = scales::comma(gdpPercap)), hjust = 0, nudge_y = 300 ) + ... labs(title='{closest_state %>% as.numeric %>% floor}', x = "", y = "GFP per capita") + ... transition_states(year, transition_length = 1, state_length = 0) + enter_grow() + exit_shrink() + ease_aes('linear') animate(p, fps = 20, duration = 5, width = 400, height = 600, end_pause = 10)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With