Fivethirtyeight.com Style Graphs in ggplot2

@Cmrn_DP put together some code to make Matplotlib graphs that look like fivethirtyeight.com graphs. I see the attraction–fivethirtyeight graphs have a very simple, attractive look–but I’m not much of a Matplotlib user, so I took a few minutes to try and get the same style in Rs ggplot2 package. Here’s the result:

ggplot2 graph

I’m pretty happy with it! There are however a couple of things I couldn’t really replicate. First, I didn’t play with fonts much. I like the 538 fonts, but I also like ggplot’s default font a heck of a lot (Helvetica? I’m not a font snob) so I just kept it. If you did want to change fonts, you would use “family=’Times'” or whatever in the style sections of the code below. I also didn’t try to replicate the box at the bottom of every 538 graph that has the site logo and the source of the data. This is really outside the scope of what ggplot can do.

Finally, I didn’t put a subtitle on the graph. Again, this is really difficult with ggplot but I have good news: you don’t want to do this anyways. The subtitle on 538 graphs is the result of a really frustrating design decision they made to not have axis labels on any of their graphs. Instead, the subtitle tells you what the y-axis is (the quantity being plotted), and the x-axis is designed to be self evident (usually a year or a date). Sure, having no axis labels makes the charts very attractive and minimalist, but it’s also kind of actively hostile to the viewer, so I tossed out the subtitle and included some actual axis labels.

The only other downside is that you have to tweak a lot of the values I have here if you want different resolutions, or to make your annotations show up in the right places for a different set of random data.

And here’s the code:

library(ggplot2)
library(grid)
 
line1<-rnorm(100,mean=15-seq(1,6,by=.05),sd=1)
line2<-rnorm(100,mean=4+seq(1,21,by=.2),sd=.5)
time<-seq(1,100,by=1)
data<-data.frame(time,line1,line2)
 
ggplot(data,aes(time)) +
                # The actual lines
                geom_line(aes(y=line1),size=1.6) +
                geom_line(aes(y=line2),size=1.6) +
                theme_bw() +
                # Set the entire chart region to a light gray color
                theme(panel.background=element_rect(fill="#F0F0F0")) +
                theme(plot.background=element_rect(fill="#F0F0F0")) +
                theme(panel.border=element_rect(colour="#F0F0F0")) +
                # Format the grid
                theme(panel.grid.major=element_line(colour="#D0D0D0",size=.75)) +
                scale_x_continuous(minor_breaks=0,breaks=seq(0,100,10),limits=c(0,100)) +
                scale_y_continuous(minor_breaks=0,breaks=seq(0,26,4),limits=c(0,25)) +
                theme(axis.ticks=element_blank()) +
                # Dispose of the legend
                theme(legend.position="none") +
                # Set title and axis labels, and format these and tick marks
                ggtitle("Some Random Data I Made") +
                theme(plot.title=element_text(face="bold",hjust=-.08,vjust=2,colour="#3C3C3C",size=20)) +
                ylab("Data Label") +
                xlab("Days Since Beginning") +
                theme(axis.text.x=element_text(size=11,colour="#535353",face="bold")) +
                theme(axis.text.y=element_text(size=11,colour="#535353",face="bold")) +
                theme(axis.title.y=element_text(size=11,colour="#535353",face="bold",vjust=1.5)) +
                theme(axis.title.x=element_text(size=11,colour="#535353",face="bold",vjust=-.5)) +
                # Big bold line at y=0
                geom_hline(yintercept=0,size=1.2,colour="#535353") +
                # Plot margins and finally line annotations
                theme(plot.margin = unit(c(1, 1, .5, .7), "cm")) +
                annotate("text",x=14.7,y=15.3,label="Line 1",colour="#f8766d") +
                annotate("text",x=25,y=7.5,label="Line 2",colour="#00bdc4")

ggsave(filename="/538.png",dpi=600)

Update: an earlier version of this displayed a low-res graph that looked crappy on high-res devices. Fixed this using ggsave, which I was previously unfamiliar with.

Share on FacebookTweet about this on TwitterShare on LinkedIn

8 thoughts on “Fivethirtyeight.com Style Graphs in ggplot2

  1. Nice! But could it be that you forgot the colour parameter in geom_lines (eg for the first line: “geom_line(aes(y=line1,colour=”#f8766d”),size=1.6) +”). I also had to remove vjust of the y-axis in order to get the same graph. Maybe due to my settings…? Greets!

    • I just let ggplot use default colors, so specifying the line color isn’t necessary to produce what I did.

      Vjust and hjust are kinda frustrating. What you get from them seems to depend on whether you are using OSX or Windows (on my Windows machine at work this looks a bit different). ggplot also seems to use a different default font for different operating systems. It’s a bit frustrating that a bit of code doesn’t produce the exact same thing on both systems.

  2. I was also wondering how you managed to do that :) And I completely agree with you about vjust and hjust…

  3. So it turns out the much better way to use vjust to get labels away from the axis (and by default ggplot2 should be ashamed of how close it sticks axis titles to axis labels) is to use vjust in combination with lineheight. For example, on the x-axis, you can use lineheight=3,vjust=0 to move the x-axis title down and away from the tick labels. The reason this is preferable is because vjust is technically only defined for 0 and 1, so this will allow you to get a consistent look across systems.

Comments are closed.