Animated sorted bar chart with bars overtaking each other

animated time series bar chart
animated bar graph gif
plotly bar chart race
gganimate
bar chart over time
animated horizontal bar chart
r plotly animated bar chart
animation in r

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


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)

Create Trending Animated Bar Charts using R, Recently, Animated Bar Plots have started going Viral on Social Media Question Animated sorted bar chart with bars overtaking each other  Animated sorted bar chart with bars overtaking each other; ggplot2 pie and donut chart on same plot; Add dynamic subtitle using ggplot; ggplot2: geom_polygon with no fill; Venn diagram from list of clusters and co-occurring factors; plotting 3d scatter in matplotlib; Fix Node Position in D3 Force Directed Layout; Saving Matplotlib graphs to


This is what I came up with, so far, based in good part on @Jon's answer.

p <- gdp  %>%
  # build rank, labels and relative values
  group_by(Year) %>%
  mutate(Rank = rank(-Value),
         Value_rel = Value/Value[Rank==1],
         Value_lbl = paste0(" ",round(Value/1e9)))  %>%
  group_by(Country.Name) %>%
  # keep top 10
  filter(Rank <= 10) %>%
  # plot
  ggplot(aes(-Rank,Value_rel, fill = Country.Name)) +
  geom_col(width = 0.8, position="identity") +
  coord_flip() + 
  geom_text(aes(-Rank,y=0,label = Country.Name,hjust=0)) +       #country label
  geom_text(aes(-Rank,y=Value_rel,label = Value_lbl, hjust=0)) + # value label
  theme_minimal() +
  theme(legend.position = "none",axis.title = element_blank()) +
  # animate along Year
  transition_states(Year,4,1)

animate(p, 100, fps = 25, duration = 20, width = 800, height = 600)

I might come back to improve it.

The moving grid could be simulated by removing the actual grid and having geom_segment lines moving and fading out thanks to an alpha parameter changing when it approaches 100 billion.

To have labels changing values between years (which gives a nice feeling of urgency in the original chart) I think we have no choice but multiplying the rows while interpolating labels, we'll need to interpolate Rank too.

Then with a few minor cosmetic changes we should be pretty close.

How to do that animated 'race' bar chart, Generate an animation that cycles through each football season from the rank will determine the order that the bars appear in the 'race' chart. Disclaimer: The code used here is heaving borrowed (You can say, inspired and copied) from the answers of this Stack Overflow Question Animated sorted bar chart with bars overtaking each other


This is what I came up, I just use Jon and Moody code as a template and make few changes.

library(tidyverse)
library(gganimate)
library(gapminder)
theme_set(theme_classic())

gdp <- read.csv("https://raw.github.com/datasets/gdp/master/data/gdp.csv")
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))
colnames(gdp) <- gsub("Country.Name", "country", colnames(gdp))
colnames(gdp) <- gsub("Country.Code", "code", colnames(gdp))
colnames(gdp) <- gsub("Value", "value", colnames(gdp))
colnames(gdp) <- gsub("Year", "year", colnames(gdp))

gdp$value <- round(gdp$value/1e9)

gap <- gdp %>%
  group_by(year) %>%
  # The * 1 makes it possible to have non-integer ranks while sliding
  mutate(rank = min_rank(-value) * 1,
         Value_rel = value/value[rank==1],
         Value_lbl = paste0(" ",value)) %>%
  filter(rank <=10) %>%
  ungroup()

p <- ggplot(gap, aes(rank, group = country, 
                     fill = as.factor(country), color = as.factor(country))) +
  geom_tile(aes(y = value/2,
                height = value,
                width = 0.9), alpha = 0.8, color = NA) +
  geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
  geom_text(aes(y=value,label = Value_lbl, hjust=0)) +
  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 = "GDP in billion USD",
       caption = "Sources: World Bank | Plot generated by Nitish K. Mishra @nitishimtech") +
  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, 200, fps = 10, duration = 40, width = 800, height = 600, renderer = gifski_renderer("gganim.gif"))

Here I am using duration 40 second, which is slow. You can change duration and make it faster or slower as you needed.

amrrs/animated_bar_charts_in_R: Code to demo how to , borrowed (You can say, inspired and copied) from the answers of this Stack Overflow Question Animated sorted bar chart with bars overtaking each other  Let’s load the data and some packages we will need. Then, first, we need to rank each team in each season, as the rank will determine the order that the bars appear in the ‘race’ chart. We also create a relative value for each team compared to the team at the top of the ranking, as this will help with scaling the bars.


Trying to create animated bar plot with sliding bars that overtake , I've been struggling to create an animated data over time bar chart for months now. My goal is to have bars that slide and overtake each other  Animated sorted bar chart with bars overtaking each other. Animated plot with a moving facet zoom via gganimate and ggforce? Any way to pause at specific frames/time points with transition_reveal in gganimate? Animate ggplot time series plot with a sliding window. 3D scatterplot using custom image


Bar chart race in r, 5 Nov 2018 Animated sorted bar chart with bars overtaking each other · r animation ggplot2 data-visualization gganimate. This is how a Race Bar Charts in  animated_bar_charts_in_R. The goal of this project is explain how to build Animated Bar Charts in R (which is kinda trending on Social Media these days) Disclaimer: The code used here is heavily borrowed (You can say, inspired and copied) from the answers of this Stack Overflow Question Animated sorted bar chart with bars overtaking each other


Animated barplot transition with R – the R Graph Gallery, The gganimate package allows to build animated chart using the ggplot2 syntax directly from make sure you understood how to build a basic bar chart with R and ggplot2 . For each value of the variable, a step on the chart will be drawn. Select the data range that you want to create an overlapped chart, and then click Insert > Insert Column or Bar Chart > Clustered Chart, see screenshot: 2. After creating the clustered chart, right-click one series bar, and then choose Change Series Chart Type from the context menu, see screenshot: 3.