Summarizing Circular Data in R: Aspect Angle

Submitted by dylan on Wed, 2012-10-10 15:26.

The orientation of terrain surface (aspect) can have dramatic effects on landscape-scale variation in soil temperature and moisture. Summarizing aspect angle is complicated by the fact that sampled values are measured on a circular scale. The circular package for R can be used to summarize, model, and visualize this type of data.

An example function is presented below that demonstrates several components of the circular package: special data structures, summary functions, plotting functions and their application to circular data derived from measurements collected by compass. Spread and central tendency are depicted with a combination of circular histogram and kernel density estimate. The circular mean, and relative confidence in that mean are depicted with an arrow: longer arrow lengths correspond to greater confidence in the mean.

Circular HistogramCircular Histogram

 
Code Example

# load library (you may have to install this library first)
library(circular)

# plot a vector of aspect measurements, with units of degrees, measured via compass
aspect.plot <- function(p, p.bins=60, p.bw=30, p.axis=seq(0, 350, by=10), plot.title=NULL) {
        # remove NA
        p <- na.omit(p)
       
        # make a circular class object: using native degrees, template sets zero and direction
        c.p <- circular(p, units='degrees', template="geographics")
       
        # compute the mean
        m.p <- mean(c.p)
       
        # compute mean resultant length
        rho.p <- rho.circular(c.p)
               
        # setup custom axis
        a.p <- circular(p.axis, units='degrees', template="geographics")
       
        # circular histogram
        plot(c.p, axes=FALSE, stack=TRUE, bins=p.bins, shrink=1.45, cex=1, sep=0.06, pch=21, col=1, bg='RoyalBlue')
       
        # add circular density, bw is the smoothness parameter
        lines(density(c.p, bw=p.bw), col='RoyalBlue', lty=2)
       
        # add axes: note work-around for strange bug...
        axis.circular(at=(-a.p) + 90, labels=a.p, cex=0.6)
       
        # annotate north
        text(0, 1.125, 'North', cex=0.85, font=1)
       
        # annotate mean with an arrow
        arrows.circular(m.p, shrink=rho.p, length=0.15, col='RoyalBlue')
       
        # add title
        text(0, -0.25, plot.title, cex=0.85, font=2)
}

# generate some fake data
p.narrow <- runif(n=25, min=215, max=280)
p.wide <- runif(n=25, min=0, max=270)

# set figure margins to 0, 2-column plot
par(mar=c(0,0,0,0), mfcol=c(1,2))

# plot
aspect.plot(p.narrow, p.bw=10, plot.title='Soil A')
aspect.plot(p.wide, p.bw=10, plot.title='Soil B')

"strange bug"

I think I found the source of your 'strange bug' in axis.circular

Notice that a.p makes reference to a "p.axis", but you haven't defined that yet.

--Thanks for the great example to help me get started.