Economist-Style Plot using R

The graph is motivated by Economist’s plot and can be replicated by using R as follows:

R Codes for Data Cleaning and Visualization

In this post I use uspopage data set from gcookbook package for illustrative purpose.

rm(list = ls())
library(tidyverse)
library(gcookbook)
library(scales)


my_colors <- c("#A8A9AD", "#5C92A1", "#6ED0F5", "#E7D3BA", "#822813", "#ED9176", "#EF5B3E")
brgColor <- "#CDDEE6"
my_font <- "Ubuntu Condensed"

uspopage %>% 
  filter(AgeGroup != "<5") %>% 
  filter(Year >= 1960) %>% 
  filter(Year <= 2000) -> df_mini

AgeGroup <- df_mini$AgeGroup %>% unique()
levels <- c("Other", "Russia", "Angola", "Other Middle East", "Iraq", "Iran", "Saudi Arabia")

df_mini %>% 
  mutate(Country = case_when(AgeGroup == AgeGroup[1] ~ levels[1], 
                             AgeGroup == AgeGroup[2] ~ levels[2], 
                             AgeGroup == AgeGroup[3] ~ levels[3], 
                             AgeGroup == AgeGroup[4] ~ levels[4], 
                             AgeGroup == AgeGroup[5] ~ levels[5], 
                             AgeGroup == AgeGroup[6] ~ levels[6], 
                             TRUE ~ levels[7])) %>% 
  mutate(Country = factor(Country, levels = levels)) -> dfFake

dfFake %>% 
  filter(Year == 2000) %>% 
  pull(Thousands) %>% 
  sum() ->  max_value

my_breaks <- seq(from = 0, to = max_value, length.out = 7)


# Draft Plot: 

dfFake %>% 
  ggplot(aes(x = Year, y = Thousands, fill = Country)) + 
  geom_area() +
  scale_x_continuous(expand = c(0, 2), limits = c(1960, 2010)) +
  scale_y_continuous(expand = c(0, 1000), breaks = my_breaks, position = "right", 
                     limits = c(0, 270000), labels = 0:6) + 
  theme(legend.position = "right") + 
  scale_fill_manual(values = my_colors)

dfFake %>% 
  group_by(Year) %>% 
  summarise(Thousands = sum(Thousands)) %>% 
  ungroup() -> df_BlackLine


# Function creates data frame for white lines: 

df_whiteLine <- function(Country_selected) {
  
  dfFake %>% 
    filter(!Country %in% Country_selected) %>% 
    group_by(Year) %>% 
    summarise(Thousands = sum(Thousands)) %>% 
    ungroup() -> df
  
  return(df)
}

# Data frame for ploting text: 

df_BlackLine %>% 
  filter(Year == 2000) -> l0

df_whiteLine(levels[1]) -> l1
df_whiteLine(levels[1:2]) -> l2
df_whiteLine(levels[1:3]) -> l3
df_whiteLine(levels[1:4]) -> l4
df_whiteLine(levels[1:5]) -> l5
df_whiteLine(levels[1:6]) -> l6

l0 %>% 
  bind_rows(l1 %>% filter(Year == 2000)) %>% 
  bind_rows(l2 %>% filter(Year == 2000)) %>% 
  bind_rows(l3 %>% filter(Year == 2000)) %>% 
  bind_rows(l4 %>% filter(Year == 2000)) %>% 
  bind_rows(l5 %>% filter(Year == 2000)) %>% 
  bind_rows(l6 %>% filter(Year == 2000)) -> df1

df1 %>% 
  mutate(my_lead = lead(Thousands, 1L)) %>% 
  mutate(my_lead = replace_na(my_lead, 0)) %>% 
  mutate(pos = 0.5*(Thousands + my_lead)) -> df_text

my_label <- case_when(str_detect(levels, "Middle") ~ "Other\nMiddle East", TRUE ~ levels)
  
# Draft plot: 

ggplot() + 
  geom_area(data = dfFake, aes(x = Year, y = Thousands, fill = Country), show.legend = FALSE) + 
  theme_minimal(base_family = my_font) + 
  geom_line(data = df_BlackLine, aes(x = Year, y = Thousands), size = 0.8) + 
  geom_line(data = l1, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  geom_line(data = l2, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  geom_line(data = l3, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  geom_line(data = l4, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  geom_line(data = l5, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  geom_line(data = l6, aes(x = Year, y = Thousands), size = 0.8, color = "white") + 
  scale_x_continuous(expand = c(0, 0.5), limits = c(1960, 2006)) +
  scale_y_continuous(expand = c(0, 1000), breaks = my_breaks, position = "right",
                     limits = c(0, 270000), labels = 0:6) +
  theme(legend.position = "right") + 
  scale_fill_manual(values = my_colors) + 
  geom_point(data = df_text, aes(x = Year - 1, y = pos), color = "grey20") + 
  geom_text(data = df_text, aes(x = Year + 1.5, y = pos, label = my_label), 
            hjust = 0, family = my_font, size = 5.5, color = "grey20") + 
  geom_segment(data = df_text, aes(x = Year - 1, xend = Year + 1.3, y = pos, yend = pos), color = "grey20") + 
  theme(panel.grid.major.x = element_blank()) + 
  theme(panel.grid.minor.x = element_blank()) + 
  theme(panel.grid.minor.y = element_blank()) + 
  theme(plot.background = element_rect(fill = brgColor, color = NA)) + 
  theme(panel.background = element_rect(fill = brgColor, color = NA)) + 
  theme(axis.title = element_blank()) + 
  theme(plot.margin = unit(rep(0.8, 4), "cm")) + 
  labs(title = "Growing Dependence", 
       subtitle = "Chinese crude-oil import by country, m b/d", 
       caption = "Source: China Customs") + 
  theme(plot.title = element_text(size = 24)) + 
  theme(plot.subtitle = element_text(size = 16, color = "grey20")) + 
  theme(plot.caption = element_text(size = 13, color = "grey20")) + 
  theme(axis.text = element_text(size = 16))

library(grid)
grid.rect(x = 0.018, y = 0.93, hjust = 1, vjust = 0, gp = gpar(fill = "#e5001c", lwd = 0))  
LS0tCnRpdGxlOiAiT3JpZ2luIG9mIGNydWRlIG9pbCBpbXBvcnRlZCB0byBDaGluYSIKYXV0aG9yOiAnTmd1eWVuIENoaSBEdW5nJwpzdWJ0aXRsZTogIkRhaWx5IEdyYXBoIFNlcmllcyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgIyAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogImZsYXRseSIKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCi0tLQoKYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2KQpgYGAKCiMgRWNvbm9taXN0LVN0eWxlIFBsb3QgdXNpbmcgUgoKVGhlIGdyYXBoIGlzIG1vdGl2YXRlZCBieSBbRWNvbm9taXN0J3MgcGxvdF0oaHR0cHM6Ly9nbG9iYWxyaXNraW5zaWdodHMuY29tLzIwMTYvMDIvYXNzZXNzaW5nLWNoaW5hcy1zdHJhdGVneS1pbi10aGUtbWlkZGxlLWVhc3QvKSBhbmQgY2FuIGJlIHJlcGxpY2F0ZWQgYnkgdXNpbmcgUiBhcyBmb2xsb3dzOiAKICAKIVtdKC9ob21lL2toYW5oYW4vb2lsLnBuZykKCgojIFIgQ29kZXMgZm9yIERhdGEgQ2xlYW5pbmcgYW5kIFZpc3VhbGl6YXRpb24KCkluIHRoaXMgcG9zdCBJIHVzZSAqKnVzcG9wYWdlKiogZGF0YSBzZXQgZnJvbSAqKmdjb29rYm9vayoqIHBhY2thZ2UgZm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3NlLiAKCmBgYHtyLCBldmFsPUZBTFNFfQoKcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdjb29rYm9vaykKbGlicmFyeShzY2FsZXMpCgoKbXlfY29sb3JzIDwtIGMoIiNBOEE5QUQiLCAiIzVDOTJBMSIsICIjNkVEMEY1IiwgIiNFN0QzQkEiLCAiIzgyMjgxMyIsICIjRUQ5MTc2IiwgIiNFRjVCM0UiKQpicmdDb2xvciA8LSAiI0NEREVFNiIKbXlfZm9udCA8LSAiVWJ1bnR1IENvbmRlbnNlZCIKCnVzcG9wYWdlICU+JSAKICBmaWx0ZXIoQWdlR3JvdXAgIT0gIjw1IikgJT4lIAogIGZpbHRlcihZZWFyID49IDE5NjApICU+JSAKICBmaWx0ZXIoWWVhciA8PSAyMDAwKSAtPiBkZl9taW5pCgpBZ2VHcm91cCA8LSBkZl9taW5pJEFnZUdyb3VwICU+JSB1bmlxdWUoKQpsZXZlbHMgPC0gYygiT3RoZXIiLCAiUnVzc2lhIiwgIkFuZ29sYSIsICJPdGhlciBNaWRkbGUgRWFzdCIsICJJcmFxIiwgIklyYW4iLCAiU2F1ZGkgQXJhYmlhIikKCmRmX21pbmkgJT4lIAogIG11dGF0ZShDb3VudHJ5ID0gY2FzZV93aGVuKEFnZUdyb3VwID09IEFnZUdyb3VwWzFdIH4gbGV2ZWxzWzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2VHcm91cCA9PSBBZ2VHcm91cFsyXSB+IGxldmVsc1syXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlR3JvdXAgPT0gQWdlR3JvdXBbM10gfiBsZXZlbHNbM10sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZUdyb3VwID09IEFnZUdyb3VwWzRdIH4gbGV2ZWxzWzRdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2VHcm91cCA9PSBBZ2VHcm91cFs1XSB+IGxldmVsc1s1XSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlR3JvdXAgPT0gQWdlR3JvdXBbNl0gfiBsZXZlbHNbNl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBsZXZlbHNbN10pKSAlPiUgCiAgbXV0YXRlKENvdW50cnkgPSBmYWN0b3IoQ291bnRyeSwgbGV2ZWxzID0gbGV2ZWxzKSkgLT4gZGZGYWtlCgpkZkZha2UgJT4lIAogIGZpbHRlcihZZWFyID09IDIwMDApICU+JSAKICBwdWxsKFRob3VzYW5kcykgJT4lIAogIHN1bSgpIC0+ICBtYXhfdmFsdWUKCm15X2JyZWFrcyA8LSBzZXEoZnJvbSA9IDAsIHRvID0gbWF4X3ZhbHVlLCBsZW5ndGgub3V0ID0gNykKCgojIERyYWZ0IFBsb3Q6IAoKZGZGYWtlICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gVGhvdXNhbmRzLCBmaWxsID0gQ291bnRyeSkpICsgCiAgZ2VvbV9hcmVhKCkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDIpLCBsaW1pdHMgPSBjKDE5NjAsIDIwMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMTAwMCksIGJyZWFrcyA9IG15X2JyZWFrcywgcG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCAyNzAwMDApLCBsYWJlbHMgPSAwOjYpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpCgpkZkZha2UgJT4lIAogIGdyb3VwX2J5KFllYXIpICU+JSAKICBzdW1tYXJpc2UoVGhvdXNhbmRzID0gc3VtKFRob3VzYW5kcykpICU+JSAKICB1bmdyb3VwKCkgLT4gZGZfQmxhY2tMaW5lCgoKIyBGdW5jdGlvbiBjcmVhdGVzIGRhdGEgZnJhbWUgZm9yIHdoaXRlIGxpbmVzOiAKCmRmX3doaXRlTGluZSA8LSBmdW5jdGlvbihDb3VudHJ5X3NlbGVjdGVkKSB7CiAgCiAgZGZGYWtlICU+JSAKICAgIGZpbHRlcighQ291bnRyeSAlaW4lIENvdW50cnlfc2VsZWN0ZWQpICU+JSAKICAgIGdyb3VwX2J5KFllYXIpICU+JSAKICAgIHN1bW1hcmlzZShUaG91c2FuZHMgPSBzdW0oVGhvdXNhbmRzKSkgJT4lIAogICAgdW5ncm91cCgpIC0+IGRmCiAgCiAgcmV0dXJuKGRmKQp9CgojIERhdGEgZnJhbWUgZm9yIHBsb3RpbmcgdGV4dDogCgpkZl9CbGFja0xpbmUgJT4lIAogIGZpbHRlcihZZWFyID09IDIwMDApIC0+IGwwCgpkZl93aGl0ZUxpbmUobGV2ZWxzWzFdKSAtPiBsMQpkZl93aGl0ZUxpbmUobGV2ZWxzWzE6Ml0pIC0+IGwyCmRmX3doaXRlTGluZShsZXZlbHNbMTozXSkgLT4gbDMKZGZfd2hpdGVMaW5lKGxldmVsc1sxOjRdKSAtPiBsNApkZl93aGl0ZUxpbmUobGV2ZWxzWzE6NV0pIC0+IGw1CmRmX3doaXRlTGluZShsZXZlbHNbMTo2XSkgLT4gbDYKCmwwICU+JSAKICBiaW5kX3Jvd3MobDEgJT4lIGZpbHRlcihZZWFyID09IDIwMDApKSAlPiUgCiAgYmluZF9yb3dzKGwyICU+JSBmaWx0ZXIoWWVhciA9PSAyMDAwKSkgJT4lIAogIGJpbmRfcm93cyhsMyAlPiUgZmlsdGVyKFllYXIgPT0gMjAwMCkpICU+JSAKICBiaW5kX3Jvd3MobDQgJT4lIGZpbHRlcihZZWFyID09IDIwMDApKSAlPiUgCiAgYmluZF9yb3dzKGw1ICU+JSBmaWx0ZXIoWWVhciA9PSAyMDAwKSkgJT4lIAogIGJpbmRfcm93cyhsNiAlPiUgZmlsdGVyKFllYXIgPT0gMjAwMCkpIC0+IGRmMQoKZGYxICU+JSAKICBtdXRhdGUobXlfbGVhZCA9IGxlYWQoVGhvdXNhbmRzLCAxTCkpICU+JSAKICBtdXRhdGUobXlfbGVhZCA9IHJlcGxhY2VfbmEobXlfbGVhZCwgMCkpICU+JSAKICBtdXRhdGUocG9zID0gMC41KihUaG91c2FuZHMgKyBteV9sZWFkKSkgLT4gZGZfdGV4dAoKbXlfbGFiZWwgPC0gY2FzZV93aGVuKHN0cl9kZXRlY3QobGV2ZWxzLCAiTWlkZGxlIikgfiAiT3RoZXJcbk1pZGRsZSBFYXN0IiwgVFJVRSB+IGxldmVscykKICAKIyBEcmFmdCBwbG90OiAKCmdncGxvdCgpICsgCiAgZ2VvbV9hcmVhKGRhdGEgPSBkZkZha2UsIGFlcyh4ID0gWWVhciwgeSA9IFRob3VzYW5kcywgZmlsbCA9IENvdW50cnkpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lX21pbmltYWwoYmFzZV9mYW1pbHkgPSBteV9mb250KSArIAogIGdlb21fbGluZShkYXRhID0gZGZfQmxhY2tMaW5lLCBhZXMoeCA9IFllYXIsIHkgPSBUaG91c2FuZHMpLCBzaXplID0gMC44KSArIAogIGdlb21fbGluZShkYXRhID0gbDEsIGFlcyh4ID0gWWVhciwgeSA9IFRob3VzYW5kcyksIHNpemUgPSAwLjgsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZW9tX2xpbmUoZGF0YSA9IGwyLCBhZXMoeCA9IFllYXIsIHkgPSBUaG91c2FuZHMpLCBzaXplID0gMC44LCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBsMywgYWVzKHggPSBZZWFyLCB5ID0gVGhvdXNhbmRzKSwgc2l6ZSA9IDAuOCwgY29sb3IgPSAid2hpdGUiKSArIAogIGdlb21fbGluZShkYXRhID0gbDQsIGFlcyh4ID0gWWVhciwgeSA9IFRob3VzYW5kcyksIHNpemUgPSAwLjgsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZW9tX2xpbmUoZGF0YSA9IGw1LCBhZXMoeCA9IFllYXIsIHkgPSBUaG91c2FuZHMpLCBzaXplID0gMC44LCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBsNiwgYWVzKHggPSBZZWFyLCB5ID0gVGhvdXNhbmRzKSwgc2l6ZSA9IDAuOCwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDAuNSksIGxpbWl0cyA9IGMoMTk2MCwgMjAwNikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAxMDAwKSwgYnJlYWtzID0gbXlfYnJlYWtzLCBwb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMjcwMDAwKSwgbGFiZWxzID0gMDo2KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gZGZfdGV4dCwgYWVzKHggPSBZZWFyIC0gMSwgeSA9IHBvcyksIGNvbG9yID0gImdyZXkyMCIpICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSBkZl90ZXh0LCBhZXMoeCA9IFllYXIgKyAxLjUsIHkgPSBwb3MsIGxhYmVsID0gbXlfbGFiZWwpLCAKICAgICAgICAgICAgaGp1c3QgPSAwLCBmYW1pbHkgPSBteV9mb250LCBzaXplID0gNS41LCBjb2xvciA9ICJncmV5MjAiKSArIAogIGdlb21fc2VnbWVudChkYXRhID0gZGZfdGV4dCwgYWVzKHggPSBZZWFyIC0gMSwgeGVuZCA9IFllYXIgKyAxLjMsIHkgPSBwb3MsIHllbmQgPSBwb3MpLCBjb2xvciA9ICJncmV5MjAiKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gYnJnQ29sb3IsIGNvbG9yID0gTkEpKSArIAogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGJyZ0NvbG9yLCBjb2xvciA9IE5BKSkgKyAKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChyZXAoMC44LCA0KSwgImNtIikpICsgCiAgbGFicyh0aXRsZSA9ICJHcm93aW5nIERlcGVuZGVuY2UiLCAKICAgICAgIHN1YnRpdGxlID0gIkNoaW5lc2UgY3J1ZGUtb2lsIGltcG9ydCBieSBjb3VudHJ5LCBtIGIvZCIsIAogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IENoaW5hIEN1c3RvbXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0KSkgKyAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgY29sb3IgPSAiZ3JleTIwIikpICsgCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgY29sb3IgPSAiZ3JleTIwIikpICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCgpsaWJyYXJ5KGdyaWQpCmdyaWQucmVjdCh4ID0gMC4wMTgsIHkgPSAwLjkzLCBoanVzdCA9IDEsIHZqdXN0ID0gMCwgZ3AgPSBncGFyKGZpbGwgPSAiI2U1MDAxYyIsIGx3ZCA9IDApKSAgCgpgYGAKCgo=