Rich vs. Poor in the G-20

Comparing the share of wealth held by each income quintile (richest 20% to poorest 20%) in G-20 Nations

World Bank Data
A4 Size Viz
Governance
Inequality
Author

Aditya Dahiya

Published

May 18, 2024

Comparative Wealth Inequality across G-20 Nations

The graphic presented draws on data from the World Bank Databank, a comprehensive resource providing a wide array of economic and social data from household surveys conducted globally. This visual representation illustrates the distribution of wealth among different income groups across various countries. Each bar in the graphic represents a country, segmented into quintiles that display the percentage of total wealth owned by each 20% of the population, ranging from the poorest to the richest.

Notably, countries like South Africa and Brazil are highlighted for their pronounced wealth inequality, where the wealthiest 20% control a substantial portion of the country’s wealth, leaving the poorest 20% with a minimal share. In contrast, nations such as South Korea, France, and Canada exemplify a more equitable distribution, with wealth more evenly dispersed among all income groups. This graphic underscores the disparities in wealth distribution and offers insights into the economic landscapes of different countries.

This graph illustrates the distribution of wealth among different income groups across various countries, segmented by quintiles. It highlights significant disparities in wealth ownership, with countries like South Africa and Brazil showing high inequality, while nations such as South Korea, France, and Canada demonstrate more equitable wealth distribution.

This graph illustrates the distribution of wealth among different income groups across various countries, segmented by quintiles. It highlights significant disparities in wealth ownership, with countries like South Africa and Brazil showing high inequality, while nations such as South Korea, France, and Canada demonstrate more equitable wealth distribution.

How I made this graphic?

Loading required libraries, data import & creating custom functions

Code
# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Combining plots

# Extras and Annotations
library(magick)               # Image processing

# Credits: Geospatial Science and Human Security at ORNL
# Credits: @jpiburn, @petrbouchal, @bapfeld
# install.packages("wbstats")
library(wbstats)

# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Combining plots

# Extras and Annotations
library(magick)               # Image processing

# Credits: Geospatial Science and Human Security at ORNL
# Credits: @jpiburn, @petrbouchal, @bapfeld
# install.packages("wbstats")
library(wbstats)

indicators <- wbstats::wb_indicators()

selected_indicators <- indicators |>
  filter(str_detect(indicator, "Income share held by")) |>
  pull(indicator_id)

rawdf <- wb_data(
  indicator = selected_indicators,
  start_date = 1980,
  end_date = 2023
) |>
  janitor::clean_names()

Exploratory Data Analysis & Data Wrangling

Code
# indicators |> 
#   filter(str_detect(indicator_desc, "Population, ages")) |> 
#   filter(str_detect(indicator, "total")) |> 
#   select(indicator)
# 
# indicators |> 
#   mutate(indicator_desc = str_to_lower(indicator_desc)) |> 
#   filter(str_detect(indicator_desc, "quintile")) |> 
#   filter(str_detect(indicator_desc, "income")) |> 
#   select(indicator) |> 
#   print(n = Inf)
#
rawdf |>
  drop_na() |>
  # visdat::vis_miss()
  distinct(country, iso2c) |> 
  print(n = Inf)


selected_countries <- c("AR", "AU", "BR", "CA", "CN",
                        "FR", "DE", "IN", "ID", "IT",
                        "JP", "MX", "KR", "RU", "SA",
                        "ZA", "TR", "UK", "US")

country_levels <- rawdf |> 
#  filter(iso2c %in% selected_countries) |> 
  drop_na() |> 
  group_by(iso2c) |> 
  slice_max(order_by = date, n = 1) |> 
  ungroup() |> 
  select(iso2c, country, si_dst_05th_20) |> 
  filter(iso2c %in% selected_countries) |> 
  arrange(desc(si_dst_05th_20)) |> 
  pull(country)


df <- rawdf |> 
  filter(iso2c %in% selected_countries) |> 
  drop_na() |> 
  group_by(iso2c) |> 
  slice_max(order_by = date, n = 1) |> 
  ungroup() |> 
  select(
    iso2c, country,
    ends_with("20")
    ) |> 
  pivot_longer(
    cols = -c(iso2c, country),
    values_to = "value",
    names_to = "quantile"
  ) |> 
  mutate(
    quantile = str_remove(quantile, "si_dst_"),
    quantile = str_remove(quantile, "_20"),
    quantile = if_else(quantile == "frst", "01st", quantile),
    quantile = fct(
      quantile,
      levels = c("01st", "02nd", "03rd", "04th", "05th")
    ),
    country = fct(country, levels = country_levels),
    country = fct_rev(country),
    iso2c = str_to_lower(iso2c)
  )

# An Additional Dataframe for texxt annotations
lowest_country <- df |> 
  filter(quantile == "05th") |> 
  slice_min(order_by = value, n = 1) |> 
  pull(iso2c)

# A dataframe for annotations in the plot
ann_df <- df |> 
  filter(iso2c == lowest_country) |> 
  select(quantile, value) |> 
  mutate(
    ann_text = c(
      "2nd poorest\n20%",
      "Middle 20%\nof population",
      "2nd richest\n20%",
      "Share of wealth held by\nrichest 20% of population",
      "Poorest\n20%"
    )
  ) |>
  arrange(desc(quantile)) |> 
  mutate(
    x_start = lag(value),
    x_start = replace_na(x_start, 0),
    x_start = cumsum(x_start),
    x_end = x_start + value,
    mid_value = (x_start  + x_end)/2)

Visualization Parameters

Code
# Font for titles
font_add_google("Train One",
  family = "title_font"
) 
# Font for the caption
font_add_google("Barlow Condensed",
  family = "caption_font"
) 

# Font for plot text
font_add_google("Bubbler One",
  family = "body_font"
) 

ts <- 80

showtext_auto()

# Colour Palettes
mypal <- paletteer::paletteer_d("PNWColors::Sunset2")

bg_col <- "white"                        # Background Colour
text_col <- mypal[1] |> darken(0.6)      # Colour for text
text_hil <- mypal[1] |> darken(0.3)      # Colour for highlighted text
 
# Caption stuff for the plot
sysfonts::font_add(
  family = "Font Awesome 6 Brands",
  regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- "&#xf09b"
github_username <- "aditya-dahiya"
xtwitter <- "&#xe61b"
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username}  </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")

Annotation Text for the Plot

Code
plot_title <- "Rich vs. Poor in the G-20"

plot_caption <- paste0(
  "**Data**: World Bank's Databank (2022)", 
  " |  **Code:** ", 
  social_caption_1, 
  " |  **Graphics:** ", 
  social_caption_2
  )

plot_subtitle <- str_wrap("The distribution of wealth among different income groups in various countries. Each bar represents a country, and the segments within each bar indicate the percentage of total wealth owned by each 20% (quintile) of the population, from the poorest to the richest. The data, derived from household surveys, reflect how income is shared among the population. Countries like South Africa and Brazil exhibit significant inequality, with the wealthiest 20% owning a large portion of the country's wealth, while the poorest 20% own very little. In contrast, countries like South Korea, France, and Canada demonstrate a more equitable distribution of wealth, where the wealth is more evenly spread across all income groups.", 90)

The base plot

Code
g_base <- df |> 
  ggplot(
    aes(
      x = value,
      y = country,
      fill = quantile
    )
  ) + 
  geom_col(
    position = position_stack(),
    alpha = 0.5,
    colour = bg_col,
    width = 0.8
  ) +
  geom_text(
    aes(
      label = paste0(value, "%"),
      colour = quantile
    ),
    position = position_stack(),
    hjust = 1, vjust = 0.5,
    size = ts / 5,
    family = "body_font",
    check_overlap = TRUE
  ) +
  
  # Flags along Y-axis
  ggflags::geom_flag(
    mapping = aes(
      country = iso2c,
      x = -5
    ),
    size = 20
  ) +
  
  # Annotations
  geom_errorbar(
    data = ann_df,
    mapping = aes(
      y = 0, 
      xmin = x_start, 
      xmax = x_end,
      colour = quantile),
    linetype = 1,
    position = position_stack(),
    alpha = 0.5,
    linewidth = 1,
    width = 0.2
  ) +
  geom_text(
    data = ann_df,
    mapping = aes(
      x = mid_value,
      y = -0.5,
      label = ann_text,
      colour = quantile
    ),
  hjust = 0.5,  
  family = "caption_font",
  lineheight = 0.3,
  size = ts / 4
  ) +
  
  # Scales
  scale_y_discrete(
    expand = expansion(c(0.15, 0))
  ) +
  scale_x_continuous(
    position = "top",
    breaks = seq(0, 100, 20),
    labels = label_number(suffix = "%"),
    expand = expansion(c(0.04, 0))
  ) +
  scale_fill_manual(values = mypal) +
  scale_colour_manual(values = mypal |> darken(0.5)) +
  
  # Labels
  labs(
    title = plot_title,
    subtitle = plot_subtitle,
    caption = plot_caption,
    x = "Precentage of wealth held by each quintile", y = NULL
  ) +
  
  # Themes
  theme_minimal(
    base_family = "body_font",
    base_size = ts
  ) +
  theme(
    legend.position = "none",
    axis.line.x = element_blank(),
    axis.title = element_text(
      colour = text_col,
      margin = margin(0,0,0,0, "mm"),
      face = "bold",
      size = 2 * ts,
      hjust = 1
    ),
    axis.text.y = element_text(
      colour = text_col,
      face = "bold",
      margin = margin(0,0,0,0, "mm"),
      size = 1.2 * ts
    ),
    axis.text.x = element_text(
      colour = text_col,
      face = "bold",
      margin = margin(0,0,0,0, "mm")
    ),
    axis.ticks = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.x = element_blank(),
    plot.background = element_rect(
      fill = bg_col,
      colour = "transparent"
    ),
    plot.title = element_text(
      hjust = 0.5,
      family = "title_font",
      colour = text_hil,
      size = 3 * ts,
      margin = margin(15,0,5,0, "mm")
    ),
    plot.subtitle = element_text(
      hjust = 0,
      family = "body_font",
      colour = text_col,
      lineheight = 0.3,
      margin = margin(0,0,0,0, "mm")
    ),
    plot.caption = element_textbox(
      hjust = 0.5,
      colour = text_hil,
      family = "caption_font"
    ),
    plot.title.position = "plot"
  )

Adding annotations to the plot

Code
# A QR Code for the infographic
url_graphics <- paste0(
  "https://aditya-dahiya.github.io/projects_presentations/data_vizs/",
  # The file name of the current .qmd file
  "wb_income_quantiles",         
  ".html"
)
# remotes::install_github('coolbutuseless/ggqr')
# library(ggqr)
plot_qr <- ggplot(
  data = NULL, 
  aes(x = 0, y = 0, label = url_graphics)
  ) + 
  ggqr::geom_qr(
    colour = text_hil |> lighten(0.1), 
    fill = bg_col,
    size = 2.5
    ) +
  coord_fixed() +
  theme_void() +
  theme(plot.background = element_rect(
    fill = NA, 
    colour = NA
    ),
    panel.background = element_rect(
      fill = NA,
      colour = NA
    )
  )

# Compiling the plots

g <- g_base +
  inset_element(
    p = plot_qr,
    left = 0.8, right = 0.99,
    bottom = 0.77, top = 0.9,
    align_to = "full",
    clip = FALSE
  ) + 
  plot_annotation(
    theme = theme(
      plot.background = element_rect(
        fill = "transparent",
        colour = "transparent"
      )
    )
  )

Savings the graphics

Code
ggsave(
  filename = here::here("data_vizs", "a4_wb_income_quantiles.png"),
  plot = g,
  width = 2 * 210,        
  height = 2 * 297,   
  units = "mm",
  bg = bg_col
)


# Saving a thumbnail for the webpage
image_read(here::here("data_vizs", "a4_wb_income_quantiles.png")) |> 
  image_resize(geometry = "400") |> 
  image_write(here::here("data_vizs", 
                         "thumbnails", 
                         "wb_gdp_income_quantiles.png"))