Andy Clifton Andy Clifton - 2 months ago 47x
R Question

Wind rose with ggplot (R)?

I am looking for good R code (or package) that uses ggplot2 to create wind roses that show the frequency, magnitude and direction of winds.

I'm particularly interested in ggplot2 as building the plot that way gives me the chance to leverage the rest of the functionality in there.

Test data

Download a year of weather data from the 80-m level on the National Wind Technology's "M2" tower. This link will create a .csv file that is automatically downloaded. You need to find that file (it's called "20130101.csv"), and read it in.

# read in a data file <- read.csv(file = "A:/drive/somehwere/20130101.csv",
col.names = c("date","hr","ws.80","wd.80"),
stringsAsFactors = FALSE))

This would work with any .csv file and will overwrite the column names.

Sample data

If you don't want to download that data, here are the first 10 data points: <- structure(list(date = c("1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013", "1/1/2013"), hr = 1:10, ws.80 = c(2.031, 1.7304, 3.8314, 4.9038, 3.0625, 1.7628, 2.5992, 1.6655, 3.2368, 1.4226), wd.80 = c(321.335, 316.3581, 342.1085, 343.2032, 323.6292, 240.4112, 163.916, 158.6368, 343.4622, 313.4253)), .Names = c("date", "hr", "ws.80", "wd.80"), row.names = c(NA, 10L), class = "data.frame")


For sake of argument we'll assume that we are using the data frame, which has two data columns and some kind of date / time information. We'll ignore the date and time information initially.

The ggplot function

I've coded the function below. I'm interested in other people's experience or suggestions on how to improve this.

# WindRose.R

plot.windrose <- function(data,
                      spdres = 2,
                      dirres = 30,
                      spdmin = 2,
                      spdmax = 20,
                      spdseq = NULL,
                      palette = "YlGnBu",
                      countmax = NA,
                      debug = 0){

# Look to see what data was passed in to the function
  if (is.numeric(spd) & is.numeric(dir)){
    # assume that we've been given vectors of the speed and direction vectors
    data <- data.frame(spd = spd,
                       dir = dir)
    spd = "spd"
    dir = "dir"
  } else if (exists("data")){
    # Assume that we've been given a data frame, and the name of the speed 
    # and direction columns. This is the format we want for later use.    

  # Tidy up input data ---- <- NROW(data)
  dnu <- ([[spd]]) |[[dir]]))
  data[[spd]][dnu] <- NA
  data[[dir]][dnu] <- NA

  # figure out the wind speed bins ----
  if (missing(spdseq)){
    spdseq <- seq(spdmin,spdmax,spdres)
  } else {
    if (debug >0){
      cat("Using custom speed bins \n")
  # get some information about the number of bins, etc.
  n.spd.seq <- length(spdseq) <- n.spd.seq - 1

  # create the color map
  spd.colors <- colorRampPalette(brewer.pal(min(max(3,

  if (max(data[[spd]],na.rm = TRUE) > spdmax){    
    spd.breaks <- c(spdseq,
                    max(data[[spd]],na.rm = TRUE))
    spd.labels <- c(paste(c(spdseq[1:n.spd.seq-1]),
                          max(data[[spd]],na.rm = TRUE)))
    spd.colors <- c(spd.colors, "grey50")
  } else{
    spd.breaks <- spdseq
    spd.labels <- paste(c(spdseq[1:n.spd.seq-1]),
  data$spd.binned <- cut(x = data[[spd]],
                         breaks = spd.breaks,
                         labels = spd.labels,
                         ordered_result = TRUE)

  # figure out the wind direction bins
  dir.breaks <- c(-dirres/2,
                  seq(dirres/2, 360-dirres/2, by = dirres),
  dir.labels <- c(paste(360-dirres/2,"-",dirres/2),
                  paste(seq(dirres/2, 360-3*dirres/2, by = dirres),
                        seq(3*dirres/2, 360-dirres/2, by = dirres)),
  # assign each wind direction to a bin
  dir.binned <- cut(data[[dir]],
                    breaks = dir.breaks,
                    ordered_result = TRUE)
  levels(dir.binned) <- dir.labels
  data$dir.binned <- dir.binned

  # Run debug if required ----
  if (debug>0){    


  # create the plot ----
  p.windrose <- ggplot(data = data,
                       aes(x = dir.binned,
                           fill = spd.binned)) +
    geom_bar() + 
    scale_x_discrete(drop = FALSE,
                     labels = waiver()) +
    coord_polar(start = -((dirres/2)/360) * 2*pi) +
    scale_fill_manual(name = "Wind Speed (m/s)", 
                      values = spd.colors,
                      drop = FALSE) +
    theme(axis.title.x = element_blank())

  # adjust axes if required
  if (!{
    p.windrose <- p.windrose +

  # print the plot

  # return the handle to the wind rose

Using this function

The quick way

The simple way to use this with the M2 data is to just pass in separate vectors for spd and dir (speed and direction):

# try the default settings
p <- plot.windrose(spd =$ws.80,
                   dir =$wd.80)

A wind rose with regular bins

And if we want custom bins, we can add those as arguments:

p <- plot.windrose(spd =$ws.80,
                   dir =$wd.80,
                   spdseq = c(0,3,6,12,20))

A wind rose with custom bins

Using a data frame and the names of columns

To make the plots more compatible with ggplot(), you can also pass in a data frame and the name of the speed and direction variables:

p.wr2 <- plot.windrose(data =,
              spd = "ws.80",
              dir = "wd.80")

Faceting by another variable

We can also plot the data by month or year using ggplot's faceting capability. Let's start by getting the time stamp from the date and hour information in, and converting to month and year:

# first create a true POSIXCT timestamp from the date and hour columns$timestamp <- as.POSIXct(paste0($date, " ",$hr,":00"),
                                tz = "GMT",
                                format = "%m/%d/%Y %H:%M")

# Convert the time stamp to years and months$Year <- as.numeric(format($timestamp, "%Y"))$month <- factor(format($timestamp, "%B"),
                        levels =

Then you can apply faceting to show how the wind rose varies by month:

# recreate p.wr2, so that includes the new data
p.wr2 <- plot.windrose(data =,
              spd = "ws.80",
              dir = "wd.80")
# now generate the faceting
p.wr3 <- p.wr2 + facet_wrap(~month,
                            ncol = 3)

enter image description here


Some things to note about the function and how it can be used:

  • The inputs are:
    • vectors of speed (spd) and direction (dir) or the name of the data frame and the names of the columns that contain the speed and direction data.
    • optional values of the bin size for wind speed (spdres) and direction (dirres).
    • palette is the name of a colorbrewer sequential palette,
    • countmax sets the range of the wind rose.
    • debug is a switch (0,1,2) to enable different levels of debugging.
  • I wanted to be able to set the maximum speed (spdmax) and the count (countmax) for the plots so that I can compare windroses from different data sets
  • If there are wind speeds that exceed (spdmax), those are added as a grey region (see the figure). I should probably code something like spdmin as well, and color-code regions where the wind speeds are less than that.
  • Following a request, I implemented a method to use custom wind speed bins. They can be added using the spdseq = c(1,3,5,12) argument.
  • You can remove the degree bin labels using the usual ggplot commands to clear the x axis: p.wr3 + theme(axis.text.x = element_blank(),axis.title.x = element_blank()).
  • Updated 5/5/15 to deal with a situation where max($ws80) is less than spdmax.