---
layout: post
title: "How to recreate WEB DuBois's fan plot from the 1900 Paris Exposition using R and ggplot2 (CC342)"
blurb: "Recreating Panel 27"
author: "PD Schloss"
date: 2025-02-17 8:00
comments: false
youtube: ib_j4dqVX1s
start_hash: 
end_hash: 
---

Pat uses R to recreate the iconic fan plot figure that WEB DuBois presented at the 1900 Paris Exposition showing the difference in occupations of Blacks and whites using tools from the ggplot2, dplyr, and showtext packages. The functions he uses from these packages include aes, annotate, arrange, coord_equal, cos, cumsum, element_blank, element_rect, element_text, factor, filter, font_add, function, geom_point, geom_polygon, geom_text, ggplot, ggsave, labs, lag, library, map, margin, mutate, nest, paste, paste0, pivot_longer, scale_fill_manual, select, seq, showtext_auto, showtext_opts, sin, sum, theme, tibble, tribble, and unnest. Pat's newsletter describing how he would go about generating this figure can be [found here](https://shop.riffomonas.org/posts/making-pie-charts-for-my-biggest-fans). The book Pat mentions by Whitney Battle-Baptiste and Britt Rusert, titled "W.E.B. Du Bois's Data Portraits: Visualizing Black America" is [available through Amazon](https://amzn.to/4heJOWZ). A great set of talks about the DuBois data portraits is [available on YouTube](https://www.youtube.com/watch?v=ZZST1AZj-dQ&ab_channel=Tableau). The Anthony Starks GitHub repository can be [found here](https://github.com/ajstarks/dubois-data-portraits/). If you have a figure that you would like to see me discuss in a future newsletter and episode of Code Club, email me at pat@riffomonas.org!

<iframe style="margin: 0 auto;display:block;" width="560" height="315" src="https://www.youtube.com/embed/{{ page.youtube }}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## Code

```R
library(tidyverse)
library(showtext)

font_add("b52", regular = "B52-ULC W00 ULC.ttf")
font_add("vasarely", regular = "vasarely-light.otf")
showtext_auto()
showtext_opts(dpi = 300)

generate_wedge <- function(theta_i, theta_f, radius = 1, resolution = 100) {
  
  thetas <- seq(theta_i, theta_f, length.out = resolution)
  
  tibble(
    x = c(0, radius * cos(thetas), 0),
    y = c(0, radius * sin(thetas), 0)
  ) 
}

basic_data <- tribble(
  ~category, ~negroes, ~whites,
  "agriculture",   62,      64,
  "manufacturing",  5,    13.5,
  "services",      28,     5.5,
  "professions",  0.5,       4,
  "trade",        4.5,      13,
  "blank",         60,      60
  ) %>%
  pivot_longer(-category, names_to = "race", values_to = "percent") %>%
  mutate(race_category = paste(race, category, sep = "_"),
         race_category = factor(
           race_category,
           levels = c("negroes_blank", "negroes_professions",
                      "negroes_trade", "negroes_manufacturing",
                      "negroes_services", "negroes_agriculture",
                      "whites_blank", "whites_professions",
                      "whites_trade", "whites_manufacturing",
                      "whites_services", "whites_agriculture"))) %>%
  arrange(race_category) %>%
  mutate(theta = 2 * pi * percent / sum(percent),
         final_theta = cumsum(theta) - first(theta) / 2,
         initial_theta = lag(final_theta, default = 0)) %>%
  filter(category != "blank") %>%
  select(-theta)


wedge_data <- basic_data %>%
  nest(theta = c(initial_theta, final_theta)) %>%
  mutate(wedge = map(.x = theta, 
                     ~generate_wedge(.x$initial_theta, .x$final_theta))) %>%
  select(-theta) %>%
  unnest(c(wedge))

label_data <- basic_data %>%
  mutate(theta = (initial_theta + final_theta) / 2,
         radius = c(0.98, 0.96, 0.95, 0.9, 0.8,
                    0.97, 0.91, 0.93, 0.97, 0.8),
         size = c(5, 6, 7, 8, 8,
                  7, 7, 7, 5, 8),
         label = paste0(percent, "%"),
         x = radius * cos(theta),
         y = radius * sin(theta)) %>%
  select(-race_category)

legend_data <- tribble(
  ~x_dot, ~x_label, ~y, ~category, ~label,
  -0.83, -0.75,      0.15, "agriculture", "AGRICULTURE, FISHERIES\n            AND MINING.",
  -0.83, -0.75,     -0.15, "manufacturing", "MANUFACTURING AND\nMECHANICAL INDUSTRIES.",
   0.83,  0.59,      0.25, "services",  "DOMESTIC AND\nPERSONAL SERVICE",
   0.83,  0.75,      0.00, "professions",  "PROFESSIONS.",
   0.83,  0.61,     -0.25, "trade", "TRADE AND\nTRANSPORTATION."
)

wedge_data %>%
  ggplot(aes(x = x, y = y, group = race_category, fill = category)) +
  geom_polygon(show.legend = FALSE, color = "black", linewidth = 0.1) +
  geom_text(data = label_data,
            aes(x = x, y = y, label = label), inherit.aes = FALSE,
            family = "vasarely", size = label_data$size, size.unit = "pt") +
  geom_point(data = legend_data, show.legend = FALSE,
             aes(x = x_dot, y = y, fill = category), inherit.aes = FALSE,
             shape = 21, size = 7, stroke = 0.25) +
  geom_text(data = legend_data, inherit.aes = FALSE, show.legend = FALSE,
            aes(x = x_label, y = y, label = label),
            hjust = c(0, 0, 0.5, 1, 0.5), lineheight = 0.8,
            family = "vasarely", size = 6, size.unit = "pt") +
  annotate(geom = "text", x = 0, y = c(1.03, -1.03),
           label = c("NEGROES.", "WHITES."), 
           family = "vasarely", size = 8, size.unit = "pt") +
  coord_equal(xlim = c(-1, 1), ylim = c(-1, 1),
              expand = FALSE, clip = "off") +
  scale_fill_manual(breaks = c("agriculture", "manufacturing", "professions",
                               "services", "trade"),
                    values = c("#dc143c", "#4682b4", "#d2b48c", "#ffd700",
                               "gray80")) +
  labs(title = "OCCUPATIONS OF NEGROES AND WHITES IN GEORGIA.",
       x = NULL, y = NULL) +
  theme(
    plot.title = element_text(family = "b52", size = 12, hjust = 0.5,
                              margin = margin(t = 20,  b = 35)),
    axis.text = element_blank(),
    axis.ticks = element_blank(),
    panel.grid = element_blank(),
    panel.background = element_rect(fill = "white", color = "white"),
    plot.margin = margin(b = 50)
  )

ggsave("plate_27.png", width = 5, height = 6.24, unit = "in")
```