R/plotCircular.R

Defines functions plotCircular

Documented in plotCircular

# plotCircular.R
# circular plot
## Changes
## 7/2/10: incorporated titles/axes labekls/colour setting/auto legend



#' Circular Plot Using Segments
#' 
#' A circular plot useful for visualising monthly or weekly data.
#' 
#' A circular plot can be useful for spotting the shape of the seasonal
#' pattern. This function can be used to plot any circular patterns, e.g.,
#' weekly or monthly. The number of segments will be the length of the variable
#' \code{area1}.
#' 
#' The plots are also called rose diagrams, with the segments then called
#' \sQuote{petals}.
#' 
#' @param area1 variable to plot, the area of the segments (or petals) are
#' proportional to this variable.
#' @param area2 2nd variable to plot (optional), the area of the segments are
#' plotted in grey.
#' @param spokes spokes that overlay segments, for example standard errors
#' (optional).
#' @param scale scale the overall size of the segments (default:0.8).
#' @param labels optional labels to appear at the ends of the segments (there
#' should be as many labels as there are \code{area1}).
#' @param stats put area values at the ends of the segments, default:TRUE.
#' @param dp decimal places for statistics, default:1.
#' @param clockwise plot in a clockwise direction, default:TRUE.
#' @param spoke.col spoke colour, default:black.
#' @param lines add dotted lines to separate petals, default:FALSE.
#' @param centrecirc controls the size of the circle at the centre of the plot,
#' default:0.03.
#' @param main title for plot, default:blank
#' @param xlab x axis label, default:blank
#' @param ylab y axis label, default:blank
#' @param pieces.col colours for circular pieces, default:\sQuote{white} for
#' 1st and \sQuote{grey} for second variable. Note that a list of available
#' colours may be found with \sQuote{colors()}
#' @param length make the length of the segments proportional to the dependent
#' variable, default:FALSE
#' @param legend whether to include legend or not, default:TRUE when plotting
#' two variables
#' @param auto.legend list of parameters for legend, see \code{\link{legend}}
#' @param \dots additional arguments to \code{\link{plot}} and/or
#' \code{\link{legend}}. See \code{\link{par}} for more details
#' @author Adrian Barnett \email{a.barnett@qut.edu.au}
#' @references Fisher, N.I. (1993) \emph{Statistical Analysis of Circular
#' Data}. Cambridge University Press, Cambridge.
#' @examples
#' \donttest{
#' # months (dummy data)
#' plotCircular(area1=seq(1,12,1), scale=0.7, labels=month.abb, dp=0)
#' # weeks (random data)
#' daysoftheweek = c('Monday','Tuesday','Wednesday','Thursday','Friday',
#' 'Saturday','Sunday')
#' weekfreq = table(round(runif(100, min=1, max=7)))
#' plotCircular(area1=weekfreq, labels=daysoftheweek, dp=0)
#' # Observed number of AFL players with expected values
#' data(AFL)
#' plotCircular(area1=AFL$players, area2=AFL$expected, scale=0.72,
#'   labels=month.abb, dp=0, lines=TRUE, legend=FALSE)
#' plotCircular(area1=AFL$players, area2=AFL$expected, scale=0.72,
#'   labels=month.abb, dp=0, lines=TRUE, pieces.col=c("green","red"),
#'   auto.legend=list(labels=c("Obs","Exp"), title="# players"),
#'   main="Observed and Expected AFL players")
#' }
#' 
#' @export 
plotCircular<-function(area1,area2=NULL,spokes=NULL,
                       scale=0.8,labels,stats=TRUE,dp=1,
                       clockwise=TRUE,spoke.col='black',lines=FALSE,
                       centrecirc=0.03,
                       main="", xlab="", ylab="",
                       pieces.col=c("white","gray"),
                       length=FALSE,
                       legend=TRUE,
                       auto.legend=list(x="bottomright",fill=NULL, 
                         labels=NULL, title=""), ...){

  ## NB: need some serious argumnt checking
  ## NB2: can find list of available colours using colors()

  ## No legend if only one variable and check area vars same length
  if (is.null(area2)) {
    legend <- FALSE
  } else {
    if (length(area1)!=length(area2))
      cat("Warning: length of", deparse(substitute(area1)),
          "and", deparse(substitute(area2)),"not equal\n")
  }
  
  ##print(pieces.col)
  density.1 <- density.2 <- 0
  if (pieces.col[1] != "white") density.1 <- NA
  if (length(pieces.col)==2) {
    if (pieces.col[2] != "white") density.2 <- NA
  }

  op <- par(no.readonly = TRUE) # the whole list of settable par's.
  on.exit(par(op)) # restore graphic settings whenever function exits

  bins<-length(area1)
  clockstart=pi/2 # default clock start at 12 o'clock
  half<- 2*pi/(bins*2) # for moving text/spokes half-way round
  if (clockwise==TRUE) mult=-1 else mult=1

  ## First plot a circle (of radius 1) as a frame
  detail<-200 # number that controls graphical detail of cheeses
  circle<-matrix(nrow=detail+1,ncol=2,data=0)
  frac<-1/detail
  for (i in 1:(detail+1)){
    circle[i,1]<-1*cos(2*pi*i*frac)
    circle[i,2]<-1*sin(2*pi*i*frac)
  }
  plot(circle,type='l',col='black',bty='n',yaxt='n',main=main,
       xlab=xlab, ylab=ylab, xlim=c(-1,1),ylim=c(-1,1),xaxt='n', ...)

  ## scale cheeses to their area
  aarea1<-area1
  if(is.null(area2)==FALSE){
      aarea2<-area2
  }
  if(length==F){
     aarea1<-sqrt(area1*12/pi)
     if(is.null(area2)==FALSE){
        aarea2<-sqrt(area2*12/pi)
     }
  }

  ## scale the area to the maximum multiplied by the user-defined scale
  ## draw the cheeses
  for (cheeseno in 1:bins){
    if(is.null(area2)==TRUE){
      scaled1<-scale*aarea1/max(aarea1)
      cheese<-matrix(nrow=102,ncol=2,data=0)
      start<-2*pi*((cheeseno-1)/bins)+clockstart
      frac<-1/100
      cheese[1,1]<-centrecirc*mult*cos((2*pi*frac/bins)+start)
      cheese[1,2]<-centrecirc*sin((2*pi*frac/bins)+start)
      cheese[102,1]<-centrecirc*mult*cos((2*pi*100*frac/bins)+start)
      cheese[102,2]<-centrecirc*sin((2*pi*100*frac/bins)+start)
      for (i in 1:100){
        cheese[i+1,1]<-mult*scaled1[cheeseno]*cos((2*pi*i*frac/bins)+start)
        cheese[i+1,2]<-scaled1[cheeseno]*sin((2*pi*i*frac/bins)+start)
      }
      polygon(cheese,density=density.1,angle=0,lty=1,lwd=1,border="black",
              col=pieces.col[1]) 
    }
    
    ## plot with two segments #
    ## 1st pattern
    if(is.null(area2)==FALSE){
      allarea<-c(aarea1,aarea2)
      scaled1<-scale*aarea1/max(allarea)
      scaled2<-scale*aarea2/max(allarea)
      cheese1<-matrix(nrow=52,ncol=2,data=0)
      cheese2<-matrix(nrow=52,ncol=2,data=0)
      start<-2*pi*((cheeseno-1)/bins)+clockstart
      frac<-1/100


      ## centrecirc: do not start at c(0,0) to prevent a dense block
      cheese1[1,1]<-centrecirc*mult*cos((2*pi*0*frac/bins)+start)
      cheese1[1,2]<-centrecirc*sin((2*pi*0*frac/bins)+start)
      cheese1[52,1]<-centrecirc*mult*cos((2*pi*51*frac/bins)+start)
      cheese1[52,2]<-centrecirc*sin((2*pi*51*frac/bins)+start)
      for (i in 1:50){
        cheese1[i+1,1]<-mult*scaled1[cheeseno]*cos((2*pi*i*frac/bins)+start)
        cheese1[i+1,2]<-scaled1[cheeseno]*sin((2*pi*i*frac/bins)+start)
      }
      polygon(cheese1,density=density.1,angle=0,lty=1,lwd=1,border="black",
              col=pieces.col[1]) 

      ## 2nd pattern
      start<-2*pi*((cheeseno-1)/bins)+clockstart
      cheese2[1,1]<-centrecirc*mult*cos((2*pi*50*frac/bins)+start)
      cheese2[1,2]<-centrecirc*sin((2*pi*50*frac/bins)+start)
      cheese2[52,1]<-centrecirc*mult*cos((2*pi*100*frac/bins)+start)
      cheese2[52,2]<-centrecirc*sin((2*pi*100*frac/bins)+start)
      for (i in 51:100){
        cheese2[i+1-50,1]<-mult*scaled2[cheeseno]*cos((2*pi*i*frac/bins)+start)
        cheese2[i+1-50,2]<-scaled2[cheeseno]*sin((2*pi*i*frac/bins)+start)
      }
      polygon(cheese2,density=density.2,angle=0,lty=1,lwd=1,border="black",
              col=pieces.col[2]) 
    } 
  }
  ## add the text
  if (is.null(labels)==FALSE&stats==FALSE){
    for (cheeseno in 1:bins){
      x<-mult*0.92*cos((2*pi*cheeseno/bins)+start+half)
      y<-0.92*sin((2*pi*cheeseno/bins)+start+half)
      text(x,y,labels[cheeseno])
    }
  }

  ## add the labels with stats
  if (is.null(labels)==FALSE&stats==TRUE){
    clabel2<-formatC(area1, format="f", digits=dp) # convert to character
    for (cheeseno in 1:bins){
      x<-mult*0.86*cos((2*pi*cheeseno/bins)+start+half)
      y<-0.86*sin((2*pi*cheeseno/bins)+start+half)
      label1<-labels[cheeseno]
      label<-paste(label1,"\n",clabel2[cheeseno])
      text(x,y,label)
    }
  }

  ## add spokes representing uncertainty
  if (is.null(spokes)==FALSE){
    scaleds<-scale*spokes/max(spokes)
    halfcheese<-(2*pi)/(bins*2);
    for (cheeseno in 1:bins){
      spokes<-matrix(data=0,nrow=2,ncol=2)
      spokes[1,1]<-centrecirc*mult*scaleds[cheeseno]*
        cos((2*pi*cheeseno/bins)+start+half)
      spokes[1,2]<-centrecirc*scaleds[cheeseno]*
        sin((2*pi*cheeseno/bins)+start+half)
      spokes[2,1]<-mult*scaleds[cheeseno]*cos((2*pi*cheeseno/bins)+start+half)
      spokes[2,2]<-scaleds[cheeseno]*sin((2*pi*cheeseno/bins)+start+half)
      lines(spokes,pch=0,type='l',col=spoke.col,lty=1,lwd=1.5) 
    }
  } # end of spokes

  ## add dotted lines to separate months
  if (lines==TRUE){
    halfcheese<-(2*pi)/(bins*2);
    for (cheeseno in 1:bins){
      breaks<-matrix(data=0,nrow=2,ncol=2)
      breaks[1,1]<-centrecirc*cos((2*pi*cheeseno/bins)+start)
      breaks[1,2]<-centrecirc*sin((2*pi*cheeseno/bins)+start)
      breaks[2,1]<-cos((2*pi*cheeseno/bins)+start)
      breaks[2,2]<-sin((2*pi*cheeseno/bins)+start)
      lines(breaks,pch=0,type='l',lty=3,lwd=1) 
    }
  } # end of lines

  ## add legend if set
  if (legend) {
    if (length(auto.legend$x)==0) {
      legend.x <- "bottomright"
    } else {
      legend.x <- auto.legend$x
    }
    
    ##if (length(auto.legend$title)==0) {
    ##  title <- NULL
    ##} else {
    ##  title <- auto.legend$title
    ##}
    
    if (length(auto.legend$labels)==0){
      labels <- c(deparse(substitute(area1)),
                  deparse(substitute(area2)))
    } else {
      labels <- auto.legend$labels
    }
    
    if (length(auto.legend$fill)==0){
      fill <- pieces.col
    } else {
      fill <- auto.legend$fill
    }
    
    ##print(legend.x)
    ##print(labels)
    ##print(fill)
    ##print(title)
    legend(x=legend.x, legend=labels, title=auto.legend$title, fill=fill, ...)
  }
  
} # end of function

## examples
##area<-c(8,7,6,5,4,3.5,2)
##plotCircular(area,scale=0.7,clockwise=TRUE,labels=c("Mon","Tue","Wed","Thu","Fri","Sat","Sun"))
##plotCircular(area,scale=0.8,clockwise=FALSE,labels=c("Mon","Tue","Wed","Thu","Fri","Sat","Sun"))

##area<-c(12,11,6,5,4,3,2)
##plotCircular(area,scale=0.8,clockwise=TRUE,labels=c("Mon","Tue","Wed","Thu","Fri","Sat","Sun"))

Try the season package in your browser

Any scripts or data that you put into this service are public.

season documentation built on March 21, 2022, 9:10 a.m.