10 min read

Digging through history: Roman emperors!

In this extremely delayed blog post, I’m digging into the rulers of one of the most important empires in history, the Roman emperors!

The dataset is a fairly small one, with roughly 63 rows and 16 features. I was planning on using the tidymodels interface, but given the size of the dataset, I’m going to table the idea.

The data contains quite a lot of information despite its size. We’ve got information about birth cities, provinces, causes of death, and even how the data for each emperor was verified.

I create a new reign_length variable to keep track of the amount of time each emperor ruled. I do this via the difftime function.

The killer and cause variables pop out to me at first glance, I think it’d be interesting to inspect them closer later.

library(tidyverse) #for streamlined data analysis
library(ggthemes) #for extra theming
library(hrbrthemes) #contains some pretty nice themes
library(extrafont) #for extra fonts
library(ggforce) #for annotations and stuff

theme_set(theme_ipsum()) #setting up a theme

emperors <- readr::read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-08-13/emperors.csv") #reading in the data

emperors %>% 
  glimpse() #inspecting the data
## Rows: 68
## Columns: 16
## $ index       <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ name        <chr> "Augustus", "Tiberius", "Caligula", "Claudius", "Nero", "G…
## $ name_full   <chr> "IMPERATOR CAESAR DIVI FILIVS AVGVSTVS", "TIBERIVS CAESAR …
## $ birth       <date> 0062-09-23, 0041-11-16, 0012-08-31, 0009-08-01, 0037-12-1…
## $ death       <date> 0014-08-19, 0037-03-16, 0041-01-24, 0054-10-13, 0068-06-0…
## $ birth_cty   <chr> "Rome", "Rome", "Antitum", "Lugdunum", "Antitum", "Terraci…
## $ birth_prv   <chr> "Italia", "Italia", "Italia", "Gallia Lugdunensis", "Itali…
## $ rise        <chr> "Birthright", "Birthright", "Birthright", "Birthright", "B…
## $ reign_start <date> 0026-01-16, 0014-09-18, 0037-03-18, 0041-01-25, 0054-10-1…
## $ reign_end   <date> 0014-08-19, 0037-03-16, 0041-01-24, 0054-10-13, 0068-06-0…
## $ cause       <chr> "Assassination", "Assassination", "Assassination", "Assass…
## $ killer      <chr> "Wife", "Other Emperor", "Senate", "Wife", "Senate", "Othe…
## $ dynasty     <chr> "Julio-Claudian", "Julio-Claudian", "Julio-Claudian", "Jul…
## $ era         <chr> "Principate", "Principate", "Principate", "Principate", "P…
## $ notes       <chr> "birth, reign.start are BCE. Assign negative for correct I…
## $ verif_who   <chr> "Reddit user zonination", "Reddit user zonination", "Reddi…
emperors <- emperors %>% 
  mutate(reign_length = abs(as.integer(difftime(reign_end, reign_start, units = "days")))/365) %>% #adding in a variable to look at reign lengths
  select(-index) %>% #not needed
  filter(!is.na(birth))

The dataset has a birth_cty column, containing the cities of birth for each emperor. I plot them out below:

emperors %>% 
  filter(!is.na(birth_cty)) %>% 
  count(birth_cty, sort = TRUE) %>% 
  head(8) %>% #keeping the 8 most frequently occurring cities
  mutate(birth_cty = fct_reorder(birth_cty, n)) %>% #reordering for neater plotting
  ggplot(aes(birth_cty, n)) + 
  geom_col(aes(fill = birth_cty), color = 'black') + 
  coord_flip() +
  scale_fill_brewer(palette = "RdPu") +
  labs(x = "Emperor birth cities",
       y = "# of emperors per city",
       title = "Birth cities of Roman emperors") +
  guides(fill = FALSE)
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database

## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database

## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

The Roman empire was filled with internal strife and conflict, with families and a lot of parties vying for the title of Caesar. Naturally, it makes sense to look at which emperors ruled the longest amidst all this turmoil.

I also color the points by dynasty to enable us to visualize trends present amongst emperors from different dynasties.

emperors %>% 
  arrange(desc(abs(reign_length))) %>% 
  head(20) %>% 
  mutate(name = fct_reorder(name, reign_length)) %>% 
  ggplot(aes(name, reign_length)) + 
  geom_point(aes(color = dynasty)) + 
  geom_hline(aes(yintercept = median(reign_length)),linetype = 'dashed') + # for depicting the average reign duration
  coord_flip() +
  scale_color_brewer(palette = "Dark2") +
  labs(x = "Emperor",
       y = "Length of reign (in years)",
       title = "20 longest-reigning Roman emperors",
       subtitle = "Coloured via dynasty\nThe dotted line shows the median reign length",
       color = "Dynasty")
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database

## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
Wow, Constantine the Great is leading the pack by far.

Next up, I’m trying to look at how these emperors died.

I use the geom_mark_circle() function from ggforce to annotate a point of interest on the plot, along with a few theming customizations done under theme().

emperors %>% 
  count(killer, cause, sort = TRUE) %>%
  ggplot(aes(killer, cause)) + 
  geom_point(aes(size = n*1.6), stroke = 1) + #for better visibility
  geom_point(aes(size = n, color = n), stroke = 1) +
  geom_mark_circle(aes(fill = cause, filter = cause == 'Natural Causes'& killer == 'Lightning'), size=0.000001, description = 'Carus\nCaesar', expand = 0.001, label.fontsize = 8, show.legend = FALSE) +
  scale_color_distiller(palette = "Reds", direction = 1) +
  coord_flip() +
  theme(axis.text.x = element_text(angle = 90)) + #to rotate the text on the x-axis
  guides(size = FALSE) +
  labs(title = "Causes of death, and killers",
       x = "Cause of death",
       y = "Name of killer",
       color = "# of killers/cause")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database

## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
Surprisingly enough, most of the deaths were due to natural causes... I was probably expecting something a bit more _exciting_, as befits the legend of such an empire. The `geom_mark_*()` family of functions from `ggforce` really add that extra bit of information that's needed sometimes.

And… that’s it. I couldn’t really devote a lot of time to this, but I hope to be out with comprehensive blog posts soon.

Criticism and feedback is always appreciated! Thanks for reading!