Annotated source code to make [this chart] 1 using [R] 2 and [ggplot2
] 3.
The source data is included as justices.csv
and a [write-up of the process] 4 can be found on my blog.
# An attempt to duplicate a chart published by the New York Times graphics
# department: http://nyti.ms/1ofeih6. After selecting the data around 80% of the
# work is done in the first ggplot() call. The other 20% --- that took at least
# 80% of the time --- are the following lines. They make this chart look like
# the original. If there's one thing I'd do better next time, it would be to
# make it resolution-independent (this version only really works at 1050x707).
# More details can be found at http://flother.is/2016/supreme-court-ideology/.
library(dplyr)
library(ggplot2)
justices <- read.csv("justices.csv")
median_justice <- justices %>%
# Remove the justices that left the court mid-term. With them, those terms
# will have ten justices and so the median won't track one justice, but take
# the mean between justice 5 and 6. I made a decision to take the median from
# the existing eight plus the new justice.
filter(!(term == 1937 & name == "Sutherland"),
!(term == 1938 & name == "Douglas"),
!(term == 1956 & name == "Reed"),
!(term == 2005 & name == "Roberts")) %>%
group_by(term) %>%
summarise(score=median(score)) %>%
ungroup() %>%
mutate(name = "Median justice")
# Store current justices; their names will be placed to the right of the chart.
recent_justices <- justices %>%
filter(term == 2014) %>%
mutate(pos = score,
# Tweak positions of these justices so they don't overlap on the chart.
pos = ifelse(name == "Ginsburg", pos - .2, pos),
pos = ifelse(name == "Breyer", pos + .05, pos),
pos = ifelse(name == "Kagan", pos - .1, pos))
# Split Scalia out so we can highlight him in the chart.
scalia <- justices %>% filter(name == "Scalia")
justices <- justices %>% filter(name != "Scalia")
ggplot(mapping = aes(term, score, group = name, colour = nominated_by),
size = 0.5) +
# Custom horizontal gridlines. If I use ggplot's default gridlines they go
# farther right than they do in the Times's original chart.
annotate("segment", colour = "#dddddd", x = 1936, xend = 2014,
y = seq(-4, 4, 2), yend = seq(-4, 4, 2)) +
# All justices bar Scalia.
geom_line(data = justices, alpha = 0.7) +
# Scalia.
geom_line(data = scalia, colour = "#bb3c40", alpha = 1, size = 1) +
# Median justice.
geom_line(data = median_justice, colour = "black") +
# Names of current justices as text labels on the right of the chart.
geom_text(data = recent_justices,
aes(label = name, x = 2014, y = pos),
size = 3.5,
colour = "black",
hjust = "outward",
nudge_x = 0.5) +
# Add a simple legend in top left corner.
annotate("text", x = 1940, y = 3,
label = "MORE CONSERVATIVE\nMORE LIBERAL", hjust = 0, vjust = 0,
size = 3.5) +
annotate("text", x = 1939, y = 3.05,
label = "↑\n↓", hjust = 0, vjust = 0,
size = 3.5) +
# White rectangle behind Vinson and Warren's names to obscure the justice
# lines. Otherwise they're hard to read.
annotate("rect", xmin = 1952, xmax = 1956, ymin = 1.5, ymax = 1.25,
fill = "white") +
annotate("rect", xmin = 1968, xmax = 1972, ymin = -1.345, ymax = -1.145,
fill = "white") +
# Highlight lines for chief justices (and a few others) by labelling them with
# the justices' names.
annotate("text", label = "Vinson", x = 1952, y = 1.356, hjust = -.05,
vjust = 0.3, size = 3.5) +
annotate("text", label = "Frankfurter", x = 1961, y = 1.861, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Warren", x = 1968, y = -1.245, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Douglas", x = 1974, y = -6.619, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Burger", x = 1985, y = 2.004, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Brennan", x = 1989, y = -3.737, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Marshall", x = 1990, y = -4.307, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Blackmun", x = 1993, y = -1.863, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Rehnquist", x = 1998, y = 1.8, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Souter", x = 1997, y = -.65, hjust = -.05,
vjust = 0.4, size = 3.5) +
annotate("text", label = "O'Connor", x = 2005, y = -0.041, hjust = -.1,
vjust = 0.4, size = 3.5) +
annotate("text", label = "Stevens", x = 2009, y = -2.669, hjust = -.1,
vjust = 0.4, size = 3.5) +
# Label the median justice's line with text and a pointer line.
annotate("text", label = "Median justice", x = 1987, y = 0.2,
fontface = "bold", size = 3.5) +
annotate("segment", colour = "black", x = 1987, xend = 1987,
y = .35, yend = 0.8) +
# Limit the x-axis extremes without affecting the underlying data.
coord_cartesian(xlim = c(1939, 2020)) +
# Remove default x-axis labels as they don't line up with the custom
# gridlines.
scale_x_continuous(breaks = NULL, limits = c(1935, NA)) +
# Custom y-axis labels to make sure the zero line is the visual centre.
scale_y_continuous(breaks = c(-4, -2, 0, 2, 4),
labels = c("-4", "-2", "0", "+2", "+4")) +
# Custom x-axis labels: start of every decade plus "2014 term" on the right.
annotate("text",
x = c(seq(1940, 2010, 10), 2014),
y = -4.7,
label = c(seq(1940, 2010, 10), "2014 term"),
size = 3.5,
hjust = 0.2) +
# Colour justice lines based on the party of the president who nominated them.
scale_colour_manual(name = "",
values = c("Republican" = "#d27d80",
"Democrat" = "#62a3c5"),
labels = c("Republican" = "Nominated by a Republican",
"Democrat" = "Nominated by a Democrat")) +
labs(title = "Supreme Court justice ideology based on Martin-Quinn scores",
x = "", y = "") +
theme_minimal() +
# Move the title to the left, shift the legend up to the top (after the title)
# remove the default gridlines, set the axis font size.
theme(plot.title = element_text(hjust = -0.04, face = 'bold'),
legend.position = c(.6, 0.977),
legend.justification = c(0.1, 0),
legend.direction = "horizontal",
panel.grid = element_blank(),
axis.text = element_text(size = 10))
# Save the file to the working directory.
ggsave("supreme_court.png", width = 10.5, height = 7.07, dpi = 100, units = "in")