Tutorials  / 

A Quick and Easy Way to Make Spiral Charts in R

Now that we’ve discovered another way to annoy chart snobs, here’s how you can make your own spirals.

Many people were dismayed by a spiral chart that served as a header image for a New York Times Opinion piece:

The graphic was made by Gus Wezerek and Sara Chodosh.

I thought it was fine. Others had other opinions.

Disregarding whether or not it was the “best” way to visualize the data, clearly the more important question is how to make such a chart. Here’s how to make it in R.

Assuming you already have R installed, all you need is the spiralize package by Zuguang Gu. Bring the data, and it does all the spiraling calculations for you.

Install the package with the package installer (found via the main menus) or enter the following in the R console:


Then load the package:


Load data

Like the original NYT chart, you’ll visualize the 7-day average of Covid-19 cases in the United States. I’ve included the relevant data in the download of this tutorial, but you can download it direct from Our World in Data.

Make sure your working directory in R is set to wherever you downloaded the tutorial source. The file path, in this case “data/owid-covid-data.csv”, is relative to your working directory.Load the data:

covid <- read.csv("data/owid-covid-data.csv", stringsAsFactors = FALSE)

Subset to just the United States, and create a column dt, which is a Date-Time object version of the date column:

us <- covid[covid$location == "United States" & !is.na(covid$new_cases_smoothed),]
us$dt <- strptime(us$date, format="%Y-%m-%d")

To match the NYT version, also subset to dates prior to January 7, 2022:

us <- us[us$dt < "2022-01-07",]

Get the maximum case count:

ymax <- max(us$new_cases_smoothed)

Make spiral

Initialize the spiral by setting the time range (January 1, 2020 to January 6, 2022), the units of time (days), and what each spiral round means (years):

# Initialize.
spiral_initialize_by_time(xlim=c("2020-01-01 00:00:00", "2022-01-06 00:00:00"), 
                          unit_on_axis = "days", period="years",
                          start=90, end=(709/365)*360+(28/365)*360+90,

The start argument indicates that the spiral should start at 90 degrees. The end argument indicates in degrees where the spiral ends. Here’s what you get:

Then create the “track” of the spiral. This is where you specify the range of the data. If you’re thinking in Cartesian coordinates instead of polar, it’s like setting the y-range in your plot.

# Create the track.
spiral_track(ylim=c(0, ymax*.7),
             background=FALSE, background_gp = gpar(col = NA, fill = NA))

You might want to consult the stacked area chart tutorial for more on drawing polygons.Then draw the polygon, which puts half the case count on the bottom and the other half on top:

# Use a polygon.
spiral_polygon(x=c(us$dt, rev(us$dt)), 
               y=c(us$new_cases_smoothed/2, -rev(us$new_cases_smoothed/2)),
               gp = gpar(col="#d32e2b", fill="#d32e2b50"))

The result:

Almost there.

In the source, you can see how I extended the baseline to start at January 1, 2020. The U.S. data starts on January 28, 2020.Draw the middle line:

# Middle baseline.
spiral_lines(x=us$dt, y=0)

At this point, I’d export and bring into Adobe Illustrator to edit, which I’m pretty sure is what Wezerek and Chodosh did. But the spiralize package also provides a spiral_text() function if you want:

# Text.
spiral_text(x="2020-01-01", y=50000, text="2020", 
            facing = "curved_inside", just = "right",
            gp=gpar(cex=1, fontfamily="Courier"))
spiral_text(x="2021-01-01", y=50000, text="2021", 
            facing = "curved_inside", just = "right",
            gp=gpar(cex=1, fontfamily="Courier"))
spiral_text(x="2022-01-01", y=50000, text="2022", 
            facing = "curved_inside", just = "right",
            gp=gpar(cex=1, fontfamily="Courier"))

It seemed finicky in my testing though:

So do what you want with that information.

Wrapping up

I split this up into pieces so that you can understand the steps, but overall it’s only a few lines of code that do the following after loading the data:

  1. Setup the plotting area.
  2. Add the visual encodings.

Get all the details on the GitHub page.Nifty. There are also more options beyond the line and polygon. You can draw bars, dots, vertical lines, points and segments.

If you can draw a regular plot in R, then you can use the spiralize package to make the same plot in spiral form. Everyone will love it, I assure you.

If you’re interested in working with spirals in R from scratch (for maximum control), there’s another tutorial for that.

Made possible by FlowingData members.
Become a member to support an independent site and learn to make great charts.

Join Now

About the Author

Nathan Yau is a statistician who works primarily with visualization. He earned his PhD in statistics from UCLA, is the author of two best-selling books — Data Points and Visualize This — and runs FlowingData. Introvert. Likes food. Likes beer.

Add Comment

You must be logged in and a member to post a comment.