#'
#' rose.R
#'
#' Rose diagrams
#'
#' $Revision: 1.11 $ $Date: 2020/12/19 05:25:06 $
#'
rose <- function(x, ...) UseMethod("rose")
rose.default <- local({
rose.default <- function(x, breaks = NULL, ...,
weights=NULL,
nclass=NULL,
unit=c("degree", "radian"),
start=0, clockwise=FALSE,
main) {
if(missing(main) || is.null(main))
main <- short.deparse(substitute(x))
stopifnot(is.numeric(x))
if(!is.null(weights))
check.nvector(weights, length(x), things="observations", vname="weights")
#' determine units
missu <- missing(unit)
unit <- match.arg(unit)
unit <- validate.angles(x, unit, missu)
FullCircle <- switch(unit, degree = 360, radian = 2*pi)
#' reduce to [0, 2pi]
x <- x %% FullCircle
#' determine breakpoints strictly inside full circle
breaks <- makebreaks(x, c(0, FullCircle), breaks, nclass)
#' histogram without weights
h <- do.call.matched(hist.default,
list(x=x, breaks=breaks, ..., plot=FALSE),
skipargs=graphicsAargh,
sieve=TRUE)
result <- h$result
otherargs <- h$otherargs
#' redo weights, if given
if(!is.null(weights)) {
wh <- whist(x=x, breaks=breaks, weights=weights)
result$count <- wh
result$density <- wh/diff(breaks)
}
#
do.call(rose.histogram,
c(list(x=result, main=main,
unit=unit, start=start, clockwise=clockwise),
otherargs))
}
graphicsAargh <- c("density", "angle", "col", "border",
"xlim", "ylim", "xlab", "ylab", "axes")
makebreaks <- function(x, r, breaks=NULL, nclass=NULL) {
use.br <- !is.null(breaks)
if (use.br) {
if (!is.null(nclass))
warning("'nclass' not used when 'breaks' is specified")
} else if (!is.null(nclass) && length(nclass) == 1L) {
breaks <- nclass
} else breaks <- "Sturges"
use.br <- use.br && (nB <- length(breaks)) > 1L
if (use.br)
breaks <- sort(breaks)
else {
if (is.character(breaks)) {
breaks <- match.arg(tolower(breaks),
c("sturges",
"fd",
"freedman-diaconis",
"scott"))
breaks <- switch(breaks,
sturges = nclass.Sturges(x),
`freedman-diaconis` = ,
fd = nclass.FD(x),
scott = nclass.scott(x),
stop("unknown 'breaks' algorithm"))
}
else if (is.function(breaks)) {
breaks <- breaks(x)
}
if (length(breaks) == 1) {
if (!is.numeric(breaks) || !is.finite(breaks) ||
breaks < 1L)
stop("invalid number of 'breaks'")
breaks <- seq(r[1], r[2], length.out=breaks)
}
else {
if (!is.numeric(breaks) || length(breaks) <= 1)
stop(gettextf("Invalid breakpoints produced by 'breaks(x)': %s",
format(breaks)), domain = NA)
breaks <- sort(breaks)
}
}
return(breaks)
}
rose.default
})
rose.histogram <- function(x, ...,
unit=c("degree", "radian"),
start=0, clockwise=FALSE,
main, labels=TRUE, at=NULL, do.plot=TRUE) {
if(missing(main) || is.null(main))
main <- short.deparse(substitute(x))
#' determine units
missu <- missing(unit)
unit <- match.arg(unit)
#' validate
bks <- x$breaks
unit <- validate.angles(bks, unit, missu)
# FullCircle <- switch(unit, degree = 360, radian = 2*pi)
#' get sector sizes
y <- x$density
ymax <- max(y)
#' draw disc
insideclearance <- 0.1
outsidespace <- if(!is.null(at) && length(at) == 0) 0 else
if(identical(labels, FALSE)) 0.1 else 0.25
R <- (1+insideclearance) * ymax
DD <- disc(R)
Rout <- (1 + outsidespace) * R
disco <- disc(Rout)
dont.complain.about(DD, disco)
result <- do.call.matched(plot.owin,
resolve.defaults(list(x=quote(disco),
main=main,
type="n"),
list(...)))
do.call.matched(plot.owin,
resolve.defaults(list(x=quote(DD),
hatch=FALSE,
add=TRUE),
list(...)),
extrargs=graphicsPars("owin"),
skipargs="col")
if(do.plot) {
#' draw sectors
ang <- ang2rad(bks, unit=unit, start=start, clockwise=clockwise)
eps <- min(diff(ang), pi/128)/2
for(i in seq_along(y)) {
aa <- seq(ang[i], ang[i+1], by=eps)
aa[length(aa)] <- ang[i+1]
yi <- y[i]
xx <- c(0, yi * cos(aa), 0)
yy <- c(0, yi * sin(aa), 0)
do.call.matched(polygon, list(x=xx, y=yy, ...))
}
#' add tick marks
circticks(R, at=at, unit=unit, start=start, clockwise=clockwise,
labels=labels)
}
#'
return(invisible(result))
}
rose.density <- function(x, ..., unit=c("degree", "radian"),
start=0, clockwise=FALSE,
main, labels=TRUE, at=NULL, do.plot=TRUE) {
if(missing(main) || is.null(main))
main <- short.deparse(substitute(x))
ang <- x$x
rad <- x$y
missu <- missing(unit)
unit <- match.arg(unit)
unit <- validate.angles(ang, unit, missu)
#'
result <- roseContinuous(ang, rad, unit, ...,
start=start, clockwise=clockwise,
main=main, labels=labels, at=at,
do.plot=do.plot)
return(invisible(result))
}
rose.fv <- function(x, ..., unit=c("degree", "radian"),
start=0, clockwise=FALSE,
main, labels=TRUE, at=NULL, do.plot=TRUE) {
if(missing(main) || is.null(main))
main <- short.deparse(substitute(x))
ang <- with(x, .x)
rad <- with(x, .y)
missu <- missing(unit)
unit <- match.arg(unit)
unit <- validate.angles(ang, unit, missu)
#'
result <- roseContinuous(ang, rad, unit, ...,
start=start, clockwise=clockwise,
main=main, labels=labels, at=at,
do.plot=do.plot)
return(invisible(result))
}
roseContinuous <- function(ang, rad, unit, ...,
start=0, clockwise=FALSE,
main,
labels=TRUE, at=NULL,
do.plot=TRUE) {
rmax <- max(rad)
#' draw disc
insideclearance <- 0.1
outsidespace <- if(!is.null(at) && length(at) == 0) 0 else
if(identical(labels, FALSE)) 0.1 else 0.25
R <- (1+insideclearance) * rmax
DD <- disc(R)
Rout <- (1 + outsidespace) * R
disco <- disc(Rout)
dont.complain.about(DD, disco)
result <- do.call.matched(plot.owin,
resolve.defaults(list(x=quote(disco),
main=main,
type="n"),
list(...)))
do.call.matched(plot.owin,
resolve.defaults(list(x=quote(DD),
add=TRUE,
hatch=FALSE),
list(...)),
extrargs=graphicsPars("owin"),
skipargs="col")
#' draw plot
if(do.plot) {
ang <- ang2rad(ang, unit=unit, start=start, clockwise=clockwise)
xx <- rad * cos(ang)
yy <- rad * sin(ang)
do.call.matched(polygon, list(x=xx, y=yy, ...), extrargs="lwd")
circticks(R, at=at, unit=unit, start=start, clockwise=clockwise,
labels=labels)
}
return(result)
}
ang2rad <- local({
compasspoints <- c(E=0,N=90,W=180,S=270)
ang2rad <- function(ang, unit=c("degree", "radian"),
start=0, clockwise=FALSE) {
unit <- match.arg(unit)
clocksign <- if(clockwise) -1 else 1
stopifnot(length(start) == 1)
if(is.character(start)) {
if(is.na(match(toupper(start), names(compasspoints))))
stop(paste("Unrecognised compass point", sQuote(start)), call.=FALSE)
startdegrees <- compasspoints[[start]]
start <- switch(unit,
degree = startdegrees,
radian = pi * (startdegrees/180))
# start is measured anticlockwise
ang <- start + clocksign * ang
} else {
stopifnot(is.numeric(start))
# start is measured according to value of 'clockwise'
ang <- clocksign * (start + ang)
}
rad <- switch(unit,
degree = pi * (ang/180),
radian = ang)
return(rad)
}
ang2rad
})
circticks <- function(R, at=NULL, unit=c("degree", "radian"),
start=0, clockwise=FALSE, labels=TRUE) {
unit <- match.arg(unit)
FullCircle <- switch(unit, degree = 360, radian = 2*pi)
if(is.null(at)) {
at <- FullCircle * (0:23)/24
major <- ((0:23) %% 6 == 0)
} else {
if(length(at) == 0) return(invisible(NULL))
nat <- (at/FullCircle) * 4
major <- abs(nat - round(nat)) < 0.01
}
atradians <- ang2rad(ang=at, unit=unit, start=start, clockwise=clockwise)
tx <- R * cos(atradians)
ty <- R * sin(atradians)
expan <- ifelse(major, 1.1, 1.05)
segments(tx, ty, expan * tx, expan * ty, lwd=major+1)
if(!identical(labels, FALSE)) {
if(identical(labels, TRUE)) {
labels <- switch(unit,
degree=paste(round(at)),
radian=parse(text= simplenumber(at/pi, "pi", "*", 1e-3)))
} else stopifnot(is.vector(labels) && length(labels) == length(at))
big <- expan + 0.1
text(big * tx, big * ty, labels=labels)
}
invisible(NULL)
}
validate.angles <- function(angles, unit=c("degree", "radian"), guess=TRUE) {
#' validate
width <- diff(range(angles))
if(missing(unit) && guess && width <= 6.2832) {
warning("Very small range of angles: treating them as radian")
unit <- "radian"
} else unit <- match.arg(unit)
FullCircle <- switch(unit, degree = 360, radian = 2*pi)
if(width > 1.002 * FullCircle)
stop("Range of angles exceeds a full circle")
return(unit)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.