Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>For sake of argument we'll assume that we are using the <code>data.in</code> data frame, which has two data columns and some kind of date / time information. We'll ignore the date and time information initially.</p> <h2>The ggplot function</h2> <p>I've coded the function below. I'm interested in other people's experience or suggestions on how to improve this.</p> <pre><code># WindRose.R require(ggplot2) require(RColorBrewer) plot.windrose &lt;- function(data, spd, dir, 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) &amp; is.numeric(dir)){ # assume that we've been given vectors of the speed and direction vectors data &lt;- 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 ---- n.in &lt;- NROW(data) dnu &lt;- (is.na(data[[spd]]) | is.na(data[[dir]])) data[[spd]][dnu] &lt;- NA data[[dir]][dnu] &lt;- NA # figure out the wind speed bins ---- if (missing(spdseq)){ spdseq &lt;- seq(spdmin,spdmax,spdres) } else { if (debug &gt;0){ cat("Using custom speed bins \n") } } # get some information about the number of bins, etc. n.spd.seq &lt;- length(spdseq) n.colors.in.range &lt;- n.spd.seq - 1 # create the color map spd.colors &lt;- colorRampPalette(brewer.pal(min(max(3, n.colors.in.range), min(9, n.colors.in.range)), palette))(n.colors.in.range) if (max(data[[spd]],na.rm = TRUE) &gt; spdmax){ spd.breaks &lt;- c(spdseq, max(data[[spd]],na.rm = TRUE)) spd.labels &lt;- c(paste(c(spdseq[1:n.spd.seq-1]), '-', c(spdseq[2:n.spd.seq])), paste(spdmax, "-", max(data[[spd]],na.rm = TRUE))) spd.colors &lt;- c(spd.colors, "grey50") } else{ spd.breaks &lt;- spdseq spd.labels &lt;- paste(c(spdseq[1:n.spd.seq-1]), '-', c(spdseq[2:n.spd.seq])) } data$spd.binned &lt;- cut(x = data[[spd]], breaks = spd.breaks, labels = spd.labels, ordered_result = TRUE) # clean up the data data. &lt;- na.omit(data) # figure out the wind direction bins dir.breaks &lt;- c(-dirres/2, seq(dirres/2, 360-dirres/2, by = dirres), 360+dirres/2) dir.labels &lt;- 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)), paste(360-dirres/2,"-",dirres/2)) # assign each wind direction to a bin dir.binned &lt;- cut(data[[dir]], breaks = dir.breaks, ordered_result = TRUE) levels(dir.binned) &lt;- dir.labels data$dir.binned &lt;- dir.binned # Run debug if required ---- if (debug&gt;0){ cat(dir.breaks,"\n") cat(dir.labels,"\n") cat(levels(dir.binned),"\n") } # deal with change in ordering introduced somewhere around version 2.2 if(packageVersion("ggplot2") &gt; "2.2"){ cat("Hadley broke my code\n") data$spd.binned = with(data, factor(spd.binned, levels = rev(levels(spd.binned)))) spd.colors = rev(spd.colors) } # create the plot ---- p.windrose &lt;- 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 (!is.na(countmax)){ p.windrose &lt;- p.windrose + ylim(c(0,countmax)) } # print the plot print(p.windrose) # return the handle to the wind rose return(p.windrose) } </code></pre> <h1>Proof of Concept and Logic</h1> <p>We'll now check that the code does what we expect. For this, we'll use the simple set of demo data.</p> <pre><code># try the default settings p0 &lt;- plot.windrose(spd = data.in$ws.80, dir = data.in$wd.80) </code></pre> <p>This gives us this plot: <a href="https://i.stack.imgur.com/7WF1y.png" rel="noreferrer"><img src="https://i.stack.imgur.com/7WF1y.png" alt="Unit Test Results"></a> So: we've correctly binned the data by direction and wind speed, and have coded up our out-of-range data as expected. Looks good!</p> <h1>Using this function</h1> <p>Now we load the real data. We can load this from the URL:</p> <pre><code>data.in &lt;- read.csv(file = "http://midcdmz.nrel.gov/apps/plot.pl?site=NWTC&amp;start=20010824&amp;edy=26&amp;emo=3&amp;eyr=2062&amp;year=2013&amp;month=1&amp;day=1&amp;endyear=2013&amp;endmonth=12&amp;endday=31&amp;time=0&amp;inst=21&amp;inst=39&amp;type=data&amp;wrlevel=2&amp;preset=0&amp;first=3&amp;math=0&amp;second=-1&amp;value=0.0&amp;user=0&amp;axis=1", col.names = c("date","hr","ws.80","wd.80")) </code></pre> <p>or from file:</p> <pre><code>data.in &lt;- read.csv(file = "A:/blah/20130101.csv", col.names = c("date","hr","ws.80","wd.80")) </code></pre> <h2>The quick way</h2> <p>The simple way to use this with the M2 data is to just pass in separate vectors for <code>spd</code> and <code>dir</code> (speed and direction):</p> <pre><code># try the default settings p1 &lt;- plot.windrose(spd = data.in$ws.80, dir = data.in$wd.80) </code></pre> <p>Which gives us this plot:</p> <p><a href="https://i.stack.imgur.com/1P29Y.png" rel="noreferrer"><img src="https://i.stack.imgur.com/1P29Y.png" alt="enter image description here"></a></p> <p>And if we want custom bins, we can add those as arguments:</p> <pre><code>p2 &lt;- plot.windrose(spd = data.in$ws.80, dir = data.in$wd.80, spdseq = c(0,3,6,12,20)) </code></pre> <p><a href="https://i.stack.imgur.com/u6LzY.png" rel="noreferrer"><img src="https://i.stack.imgur.com/u6LzY.png" alt="enter image description here"></a></p> <h2>Using a data frame and the names of columns</h2> <p>To make the plots more compatible with <code>ggplot()</code>, you can also pass in a data frame and the <em>name</em> of the speed and direction variables:</p> <pre><code>p.wr2 &lt;- plot.windrose(data = data.in, spd = "ws.80", dir = "wd.80") </code></pre> <h2>Faceting by another variable</h2> <p>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 <code>data.in</code>, and converting to month and year:</p> <pre><code># first create a true POSIXCT timestamp from the date and hour columns data.in$timestamp &lt;- as.POSIXct(paste0(data.in$date, " ", data.in$hr), tz = "GMT", format = "%m/%d/%Y %H:%M") # Convert the time stamp to years and months data.in$Year &lt;- as.numeric(format(data.in$timestamp, "%Y")) data.in$month &lt;- factor(format(data.in$timestamp, "%B"), levels = month.name) </code></pre> <p>Then you can apply faceting to show how the wind rose varies by month:</p> <pre><code># recreate p.wr2, so that includes the new data p.wr2 &lt;- plot.windrose(data = data.in, spd = "ws.80", dir = "wd.80") # now generate the faceting p.wr3 &lt;- p.wr2 + facet_wrap(~month, ncol = 3) # and remove labels for clarity p.wr3 &lt;- p.wr3 + theme(axis.text.x = element_blank(), axis.title.x = element_blank()) </code></pre> <p><a href="https://i.stack.imgur.com/q0DhU.png" rel="noreferrer"><img src="https://i.stack.imgur.com/q0DhU.png" alt="enter image description here"></a></p> <h1>Comments</h1> <p>Some things to note about the function and how it can be used:</p> <ul> <li>The inputs are: <ul> <li>vectors of speed (<code>spd</code>) and direction (<code>dir</code>) <em>or</em> the name of the data frame and the names of the columns that contain the speed and direction data.</li> <li>optional values of the bin size for wind speed (<code>spdres</code>) and direction (<code>dirres</code>). </li> <li><code>palette</code> is the name of a <a href="http://colorbrewer.org" rel="noreferrer">colorbrewer</a> sequential palette, </li> <li><code>countmax</code> sets the range of the wind rose. </li> <li><code>debug</code> is a switch (0,1,2) to enable different levels of debugging.</li> </ul></li> <li>I wanted to be able to set the maximum speed (<code>spdmax</code>) and the count (<code>countmax</code>) for the plots so that I can compare windroses from different data sets</li> <li>If there are wind speeds that exceed (<code>spdmax</code>), those are added as a grey region (see the figure). I should probably code something like <code>spdmin</code> as well, and color-code regions where the wind speeds are less than that.</li> <li>Following a request, I implemented a method to use custom wind speed bins. They can be added using the <code>spdseq = c(1,3,5,12)</code> argument.</li> <li>You can remove the degree bin labels using the usual ggplot commands to clear the x axis: <code>p.wr3 + theme(axis.text.x = element_blank(),axis.title.x = element_blank())</code>.</li> <li>At some point recently ggplot2 changed the ordering of bins, so that the plots didn't work. I think this was version 2.2. But, if your plots look a bit weird, change the code so that test for "2.2" is maybe "2.1", or "2.0".</li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload