labcurve.s
## $Id: labcurve.s 613 2009-01-22 22:43:48Z dupontct $
labcurve <- function(curves, labels=names(curves),
method=NULL, keys=NULL, keyloc=c('auto','none'),
type='l', step.type=c('left','right'),
xmethod=if(any(type=='s')) 'unique' else 'grid',
offset=NULL,
xlim=NULL, tilt=FALSE, window=NULL,
npts=100, cex=NULL,
adj='auto', angle.adj.auto=30,
lty=pr$lty, lwd=pr$lwd, col.=pr$col,
transparent=TRUE, arrow.factor=1,
point.inc=NULL, opts=NULL, key.opts=NULL,
empty.method=c('area','maxdim'),
numbins=25,
pl=!missing(add), add=FALSE,
ylim=NULL, xlab="", ylab="",
whichLabel=1:length(curves),
grid=FALSE, xrestrict=NULL, ...)
{
if(grid && !.R.)
{
##warning('specified grid=T under S-Plus, ignored')
grid <- FALSE
}
if(.R. && pl && !add)
{
plot.new(); par(new=TRUE) # enables strwidth etc.
}
if(.R.)
{
oxpd <- par('xpd')
par(xpd=NA)
on.exit(par(xpd=oxpd))
}
gfun <- ordGridFun(.R. && grid) ## see Misc.s
gun <- gfun$unit
diffu <- function(v) diff(oldUnclass(v)) # mainly for POSIXt
## also look at difftime
mcurves <- missing(curves)
pr <- par(c('cex','col','lwd','lty'))
if(!mcurves)
{
nc <- length(curves)
type <- rep(type, length=nc)
lty <- rep(lty, length=nc)
lwd <- rep(lwd, length=nc)
col. <- rep(col., length=nc)
for(i in 1:nc)
{
z <- curves[[i]]
if(pl && !add)
{
if(i==1)
{
xlm <- range(z[[1]],na.rm=TRUE)
ylm <- range(z[[2]],na.rm=TRUE)
}
else
{
xlm <- range(xlm,z[[1]],na.rm=TRUE)
ylm <- range(ylm,z[[2]],na.rm=TRUE)
}
}
if(length(a <- z$type)) type[i] <- a
if(length(a <- z$lty)) lty[i] <- a
if(length(a <- z$lwd)) lwd[i] <- a
if(length(a <- z$col)) col.[i] <- a
}
}
## Optionally bring arguments from opts as if they were listed outside opts
## This is used when opts is passed through to a function calling labcurve
if(length(opts) && is.list(opts))
{
names.opts <- names(opts)
full.names <- c('labels','method','keys','keyloc','type','step.type',
'xmethod','offset','xlim','tilt','window','npts','cex',
'adj','angle.adj.auto','lty','lwd','col.','n.auto.keyloc',
'transparent','arrow.factor','point.inc','key.opts',
'empty.method','numbins','ylim','xlab','ylab')
i <- charmatch(names.opts, full.names, -1)
if(any(i < 1))
stop(paste('Illegal elements in opts:',
paste(names.opts[i < 1], collapse=' ')))
for(j in 1:length(opts)) assign(full.names[i[j]],opts[[j]],immediate=TRUE)
}
if(mcurves) nc <- length(labels)
else if(!is.logical(labels) && nc != length(labels))
stop('length of labels is not equal to # curves')
type <- rep(type, length=nc)
lty <- rep(lty, length=nc)
lwd <- rep(lwd, length=nc)
col. <- rep(col., length=nc)
if(pl)
{
if(mcurves) stop('curves must be given if pl=T')
if(!add)
{
if(!length(xlim)) xlim <- xlm
if(!length(ylim)) ylim <- ylm
namcur <- names(curves[[1]])
if(!is.expression(xlab) && xlab=='' && length(namcur))
xlab <- namcur[1]
if(!is.expression(ylab) && ylab=='' && length(namcur))
ylab <- namcur[2]
if(grid) stop("grid=TRUE when pl=TRUE is not yet implemented")
else
plot(0, 0, xlim=xlim, ylim=ylim, xlab=xlab, ylab=ylab,
type='n', xaxt='n')
if(inherits(xlim,'POSIXt') || inherits(xlim,'POSIXct'))
axis.POSIXct(1)
else if(inherits(xlim,'Date')) axis.Date(1)
else axis(1)
pr <- par(c('cex','col','lwd','lty'))
}
for(i in 1:nc)
{
z <- curves[[i]]
gfun$lines(z[[1]], z[[2]], type=type[i], lty=lty[i],
lwd=lwd[i], col=col.[i], xpd=FALSE)
}
}
if(length(method) && method=='none') return(invisible())
pr <- parGrid(grid)
usr <- pr$usr; uin <- pr$uin
is.keys <- length(keys) > 0
lines.keys <- length(keys)==1 && is.character(keys) && keys=='lines'
if(!length(method))
{
if(is.keys)
method <- if(is.numeric(keys) || lines.keys) 'on top'
else 'offset'
else method <- 'offset'
}
## Expand abbreviations for method - couldn't use match.arg
possible.methods <- c('offset','on top','arrow','mouse','locator')
i <- charmatch(method, possible.methods, -1)
if(i < 1)
stop(paste('method must be one of ',
paste(possible.methods,collapse=' ')))
method <- possible.methods[i]
if(!length(cex)) cex <- pr$cex
if(mcurves && method %nin% c('mouse','locator'))
stop('must specify curves unless method="mouse" or "locator"')
if(!lines.keys && is.keys && length(keys) != nc)
stop('number of keys must = number of curves')
if(method %in% c('mouse','locator'))
{
if(adj=='auto')
adj <- .5
xt <- yt <- numeric(nc)
for(i in 1:nc)
{
if(i %in% whichLabel)
{
cat('\nPosition pointer to desired center of curve label and click for',
labels[i],'\n')
lab.pos <- locator(1)
xt[i] <- lab.pos$x
yt[i] <- lab.pos$y
gfun$text(lab.pos, labels[i], cex=cex, adj=adj, col=col.[i],
...)
}
}
return(invisible(list(x=xt, y=yt, offset=0,
adj=adj, cex=cex, angle=0, col=col., lwd=lwd,
key.opts=key.opts, ...)))
}
if(is.character(keyloc))
keyloc <- match.arg(keyloc)
empty.method <- match.arg(empty.method)
if(!length(offset))
offset <- if(grid) unit(.75,"strheight","m") else
strheight('m','user', cex)*.75
if(!length(xlim)) xlim <- usr[1:2]
if(!length(ylim)) ylim <- usr[3:4]
if(nc==1)
{
ci <- curves[[1]]
xx <- ci[[1]]; yy <- ci[[2]]
s <- is.finite(xx+yy)
xx <- xx[s]; yy <- yy[s]
imid <- trunc((length(xx)+1)/2)
adj <- if(is.character(adj))0.5 else adj
if(any(whichLabel==1))
gfun$text(xt <- gun(xx[imid]),
yt <- gun(yy[imid])+offset,
labels,
cex=cex, adj=adj, col=col., ...)
return(invisible(list(x=xt, y=yt, offset=offset,
adj=adj, cex=cex, col=col., lwd=lwd, angle=0,
key.opts=key.opts, ...)))
}
if(xmethod %nin% c('grid','unique'))
stop('xmethod must be "grid" or "unique"')
step.type <- match.arg(step.type)
if(is.character(adj))
{
adj.does.vary <- TRUE
adj.needs.to.vary <- TRUE
adj <- rep(.5, nc)
}
else
{
adj.does.vary <- length(adj) > 1
adj.needs.to.vary <- FALSE
adj <- rep(adj, length=nc)
}
if(xmethod=='grid') xs <- seq(xlim[1],xlim[2],length=npts) else
{
xs <- unlist(sapply(curves, function(z)z[[1]]))
xs <- sort(unique(xs[!is.na(xs)]))
xs <- xs[xs>=xlim[1] & xs<=xlim[2]]
}
ys <- matrix(NA, nrow=length(xs), ncol=nc)
rng <- matrix(NA, nrow=2, ncol=nc)
for(i in 1:nc)
{
ci <- curves[[i]]
xx <- ci[[1]]; yy <- ci[[2]]
s <- is.finite(xx+yy)
xx <- xx[s]
y <- approx(xx, yy[s], xout=xs,
f=if(step.type=='left') 0 else 1,
method=if(type[i]=='l') "linear" else "constant")$y
y <- pmax(pmin(y,usr[4]),usr[3])
## Where one curve is not defined, consider this gap to have an ordinate
## that is far from the other curves so labels where be placed where
## the other curves haven't started or after they've ended
y[is.na(y)] <- 1e10
ys[,i] <- y
rxx <- range(xx)
if(length(xrestrict))
{
rxx[1] <- max(rxx[1],xrestrict[1])
rxx[2] <- min(rxx[2],xrestrict[2])
}
rng[,i] <- rxx
## Save real range of each x-vector so candidates for labeling
## will be where the curve really exists
}
if(method=='on top' && is.keys && is.numeric(keys))
{
## Draw periodic symbols
sym <- function(curve, pch, inc, offset, type, step.type, col.,
grid, gfun)
{
x <- curve[[1]]; y <- curve[[2]]
s <- is.finite(x+y)
x <- x[s]; y <- y[s]
if(length(x) < 2)
stop("when specifying numeric keys (pch) you must have >=2 data points")
lim <- range(x)
xx <-
if(grid)
convertX(gun(seq(lim[1],lim[2],by=inc) + offset),
'native', valueOnly=TRUE)
else
seq(lim[1], lim[2], by=inc) + offset
if(length(xx)>1) xx <- xx[-1]
xx <- xx[xx<=lim[2]]
if(length(xx)==0)
warning('curve was too short to mark with a symbol.\nMay want to change point.inc or xmethod for labcurve')
else
{
yy <- approx(x, y, xout=xx,
method=if(type=='l') 'linear' else 'constant',
f=if(step.type=='left') 0 else 1)$y
gfun$points(xx, yy, pch=pch, col=col.)
}
}
if(!length(point.inc)) point.inc <- diffu(xlim)/5
for(i in 1:nc)
sym(curves[[i]], keys[i], point.inc, (i-1)*point.inc/nc,
type[i], step.type, col.=col.[i], grid, gfun)
xt <- yt <- NULL
}
else
{
xt <- yt <- direction <- numeric(nc)
angle <- rep(0,nc)
g <- function(x)
{
## finds min(abs(x)) but keeps original sign
ax <- abs(x)
if(all(is.na(ax)))
return(NA)
w <- min(ax, na.rm=TRUE)
(x[ax==w])[1] #use first occurrence
}
for(i in 1:nc)
{
yi <- ys[,i]
yi[xs<rng[1,i] | xs>rng[2,i]] <- NA
diffmat <- ys[,-i,drop=FALSE] - yi
mindiff <- apply(diffmat, 1, g)
z <- abs(mindiff)==max(abs(mindiff),na.rm=TRUE)
maxid <- min(c(1:length(mindiff))[z], na.rm=TRUE)
xt[i] <- xs[maxid]
yt[i] <- ys[maxid,i]
if(!is.na(mindiff[maxid]))
direction[i] <- 1-2*(mindiff[maxid]>0)
yto <- yt[i] + direction[i] *
(if(grid) convertY(offset,'native',valueOnly=TRUE)
else offset)
if(!is.na(yto))
if(yto >= usr[4] || yto <= usr[3])
direction[i] <- -direction[i]
## Find slope of curve i at xt[i]
if(tilt || adj.needs.to.vary)
{
angle[i] <- if(type[i]=='s') 0
else
{
ci <- curves[[i]]
xx <- ci[[1]]; yy <- ci[[2]]
s <- is.finite(xx+yy)
w <-
if(length(window))
window
else
{
nch <-
if(lines.keys) nchar(labels[i])
else if(is.keys)
1*is.numeric(keys) +
nchar(keys[i])*is.character(keys)
else
nchar(labels[i])
w <-
if(grid)
nch*convertX(unit(.75,"strwidth","m"),
'native',valueOnly=TRUE)
else
nch*strwidth('m','user',cex)
}
yy <- approx(xx[s], yy[s], xout=c(xt[i]-w/2,xt[i]+w/2),
rule=2)$y
slope <- diff(yy)/w
180*atan(slope*uin[2]/uin[1])/pi
}
}
if(adj.needs.to.vary)
{
adj[i] <-
if(type[i]=='s')
1*(direction[i]<0)
else
{
if(is.na(angle[i]) || abs(angle[i])<=angle.adj.auto)
.5
else if((direction[i]<0 && slope>0) ||
(direction[i]>0 && slope<0)) 0 else 1
}
}
}
if(!tilt) angle[] <- 0
if(!lines.keys && method=='offset' && (!is.logical(labels) || labels))
{
if(is.keys)
{
if(is.numeric(keys))
for(i in 1:nc)
gfun$points(xt[i], (gun(yt) + direction*offset)[i],
pch=keys[i], col=col.[i])
else if(i %in% whichLabel)
gfun$text(xt, gun(yt) + direction*offset,
keys, cex=cex,
adj=adj[1], col=col., ...)
}
else
{
if(tilt || adj.does.vary)
for(i in whichLabel)
gfun$text(xt[i], gun(yt[i])+direction[i]*offset,
labels[i], cex=cex, srt=angle[i],
adj=adj[i], col=col.[i],...)
else
gfun$text(xt, gun(yt)+direction*offset, labels,
cex=cex, adj=adj[1], col=col., ...)
}
}
retlist <- list(x=xt, y=yt, offset=direction*offset,
adj=adj, cex=cex, col=col., lwd=lwd, angle=if(tilt) angle,
key.opts=key.opts, ...)
}
if(method %in% c('on top','arrow') && (!is.logical(labels) || labels))
{
retlist <- list(x=xt, y=yt, offset=0,
adj=.5, cex=cex, col=col., lwd=lwd, angle=0,
key.opts=key.opts, ...)
if(method == 'on top' && !lines.keys)
{
if(is.keys)
{
if(is.character(keys))
gfun$text(xt, yt, keys, cex=cex, col=col., adj=.5, ...)
## numeric keys (periodic plotting symbols) already handled above
}
else
gfun$text(xt, yt, labels, cex=cex, col=col., adj=.5, ...)
}
else if(method=='arrow')
{
ydelta <- if(grid) unit(1/17,'npc') else diffu(ylim)/17
xdelta <- if(grid) unit(1/26,'npc') else diffu(xlim)/26
lab.pos <- list(x=gun(xt) + xdelta*arrow.factor,
y=gun(yt) + ydelta*arrow.factor)
gfun$arrows(gun(xt)+xdelta*.6*arrow.factor,
gun(yt)+ydelta*.6*arrow.factor,
xt,yt,open=TRUE,size=.06,col=col.)
gfun$text(lab.pos, labels, cex=cex, col=col., ...)
}
}
if(is.keys && (!is.character(keyloc) || keyloc!='none'))
{
## Make legend
s <- whichLabel
if(is.character(keyloc) && keyloc=='auto')
{
## Find emptiest spot for drawing legend by finding
## center of largest empty rectangle large enough to hold
## this rectangle
Xs <- rep(xs, nc)
Ys <- as.vector(ys)
putKeyEmpty(Xs, Ys,
labels=if(lines.keys || is.numeric(keys))
labels[s]
else
paste(keys,' ',labels, sep='')[s],
pch=if(is.numeric(keys))
keys[s],
lty=lty[s], lwd=lwd[s], cex=cex, col=col.[s],
transparent=transparent, plot=TRUE,
key.opts=key.opts, xlim=xlim, ylim=ylim, grid=grid)
} else putKey(keyloc,
labels=if(lines.keys || is.numeric(keys))
labels[s]
else
paste(keys,' ',labels, sep='')[s],
pch=if(is.numeric(keys))
keys[s],
lty=lty[s], lwd=lwd[s], cex=cex, col=col.[s],
transparent=transparent, plot=TRUE,
key.opts=key.opts, grid=grid)
}
invisible(retlist)
}
## Version of legend for R that implements plot=FALSE, adds grid=TRUE
## Also defaults lty, lwd, pch to NULL and checks for length>0 rather
## than missing(), so it's easier to deal with non-applicable parameters
##
## rlegendg is better to use when grid is in effect. In R 2.0, you
## can't use strwidth etc. after a lattice drawing has been rendered
if(.R.)
{
rlegendg <- function(x, y, legend, col=pr$col[1], lty=NULL,
lwd=NULL, pch=NULL, cex=pr$cex[1], other=NULL)
{
pr <- par()
if(is.list(x))
{
y <- x[[2]]
x <- x[[1]]
}
do.lines <- (length(lty) && any(lty > 0)) || length(lwd)
do.points <- length(pch)
cmd <- NULL
if(do.lines)
cmd$lines <- list(col=col, lty=lty, lwd=lwd)
if(do.points)
cmd$points<- list(col=col, pch=pch, cex=cex)
cmd$text <- list(lab=legend)
if(length(other))
cmd <- c(cmd, other)
draw.key(cmd, draw=TRUE,
vp=viewport(x=unit(x,'npc'),y=unit(y,'npc')))
invisible()
}
rlegend <- function (x, y, legend, fill, col = "black", lty=NULL, lwd=NULL,
pch=NULL, angle = NULL,
density = NULL, bty = "o", bg = par("bg"),
pt.bg = NA, cex = 1,
xjust = 0, yjust = 1, x.intersp = 1, y.intersp= 1,
adj = 0, text.width = NULL,
merge = do.lines && has.pch, trace = FALSE,
ncol = 1, horiz = FALSE, plot=TRUE, grid=FALSE,
...)
{
gfun <- ordGridFun(grid) ## see Misc.s
if (is.list(x))
{
if (!missing(y))
{
if (!missing(legend))
stop("`y' and `legend' when `x' is list (need no `y')")
legend <- y
}
y <- x$y
x <- x$x
}
else if (missing(y)) stop("missing y")
if (!is.numeric(x) || !is.numeric(y)) stop("non-numeric coordinates")
if ((nx <- length(x)) <= 0 || nx != length(y) || nx > 2)
stop("invalid coordinate lengths")
xlog <- par("xlog")
ylog <- par("ylog")
rect2 <- function(left, top, dx, dy, ...)
{
r <- left + dx
if (xlog)
{
left <- 10^left
r <- 10^r
}
b <- top - dy
if (ylog)
{
top <- 10^top
b <- 10^b
}
gfun$rect(left, top, r, b, angle = angle, density = density,
...)
}
segments2 <- function(x1, y1, dx, dy, ...)
{
x2 <- x1 + dx
if (xlog)
{
x1 <- 10^x1
x2 <- 10^x2
}
y2 <- y1 + dy
if (ylog)
{
y1 <- 10^y1
y2 <- 10^y2
}
gfun$segments(x1, y1, x2, y2, ...)
}
points2 <- function(x, y, ...)
{
if (xlog) x <- 10^x
if (ylog) y <- 10^y
gfun$points(x, y, ...)
}
text2 <- function(x, y, ...)
{
if (xlog) x <- 10^x
if (ylog) y <- 10^y
gfun$text(x, y, ...)
}
if (trace)
catn <- function(...) do.call("cat", c(lapply(list(...),
formatC), list("\n")))
pr <- parGrid(grid)
cin <- pr$cin
Cex <- (if(length(unique(cex)) > 1)
mean(cex,na.rm=TRUE)
else
cex) * pr$cex
if (!length(text.width))
text.width <- max(strwidth(legend, u = "user", cex = cex))
else if (!is.numeric(text.width) || text.width < 0)
stop("text.width must be numeric, >= 0")
xc <- Cex * xInch(cin[1], warn.log = FALSE, grid=grid)
yc <- Cex * yInch(cin[2], warn.log = FALSE, grid=grid)
xchar <- xc
yextra <- yc * (y.intersp - 1)
ymax <- max(yc, strheight(legend, u = "user", cex = cex))
ychar <- yextra + ymax
if (trace)
catn(" xchar=", xchar, "; (yextra,ychar)=", c(yextra, ychar))
if (!missing(fill))
{
xbox <- xc * 0.8
ybox <- yc * 0.5
dx.fill <- xbox
}
do.lines <- (length(lty) && any(lty > 0)) || length(lwd)
n.leg <- length(legend)
n.legpercol <-
if (horiz)
{
if (ncol != 1)
warning(paste("horizontal specification overrides: Number of columns :=",
n.leg))
ncol <- n.leg
1
}
else ceiling(n.leg/ncol)
if (has.pch <- length(pch))
{
if (is.character(pch) && nchar(pch[1]) > 1)
{
if (length(pch) > 1)
warning("Not using pch[2..] since pch[1] has multiple chars")
np <- nchar(pch[1])
pch <- substr(rep(pch[1], np), 1:np, 1:np)
}
if (!merge)
dx.pch <- x.intersp/2 * xchar
}
x.off <- if (merge) -0.7 else 0
if (xlog) x <- log10(x)
if (ylog) y <- log10(y)
if (nx == 2)
{
x <- sort(x)
y <- sort(y)
left <- x[1]
top <- y[2]
w <- diff(x)
h <- diff(y)
w0 <- w/ncol
x <- mean(x)
y <- mean(y)
if (missing(xjust)) xjust <- 0.5
if (missing(yjust)) yjust <- 0.5
}
else
{
h <- n.legpercol * ychar + yc
w0 <- text.width + (x.intersp + 1) * xchar
if (!missing(fill))
w0 <- w0 + dx.fill
if (has.pch && !merge)
w0 <- w0 + dx.pch
if (do.lines)
w0 <- w0 + (2 + x.off) * xchar
w <- ncol * w0 + 0.5 * xchar
left <- x - xjust * w
top <- y + (1 - yjust) * h
}
if (bty != "n")
{
if (trace)
catn(" rect2(", left, ",", top, ", w=", w, ", h=",
h, "...)", sep = "")
if(plot)
rect2(left, top, dx = w, dy = h, col = bg) ## FEH
}
xt <- left + xchar +
(w0 * rep(0:(ncol - 1), rep(n.legpercol, ncol)))[1:n.leg]
yt <- top - (rep(1:n.legpercol, ncol)[1:n.leg] - 1) * ychar -
0.5 * yextra - ymax
if (!missing(fill))
{
fill <- rep(fill, length.out = n.leg)
if(plot)
rect2(left = xt, top = yt + ybox/2, dx = xbox, dy = ybox,
col = fill)
xt <- xt + dx.fill
}
if (has.pch || do.lines)
col <- rep(col, length.out = n.leg)
if (do.lines)
{
seg.len <- 2
ok.l <-
if (!length(lty))
{
lty <- 1
TRUE
} else
lty > 0
if (!length(lwd)) lwd <- pr$lwd
lty <- rep(lty, length.out = n.leg)
lwd <- rep(lwd, length.out = n.leg)
if (trace)
catn(" segments2(", xt[ok.l] + x.off * xchar, ",",
yt[ok.l], ", dx=", seg.len * xchar, ", dy=0, ...)",
sep = "")
if(plot)
segments2(xt[ok.l] + x.off * xchar, yt[ok.l], dx = seg.len *
xchar, dy = 0, lty = lty[ok.l], lwd = lwd[ok.l],
col = col[ok.l])
xt <- xt + (seg.len + x.off) * xchar
}
if (has.pch)
{
pch <- rep(pch, length.out = n.leg)
pt.bg <- rep(pt.bg, length.out = n.leg)
ok <- is.character(pch) | pch >= 0
x1 <- (if (merge)
xt - (seg.len/2) * xchar
else
xt)[ok]
y1 <- yt[ok]
if (trace)
catn(" points2(", x1, ",", y1, ", pch=", pch[ok],
"...)")
if(plot)points2(x1, y1, pch = pch[ok], col = col[ok], cex = cex,
bg = pt.bg[ok])
if (!merge) xt <- xt + dx.pch
}
xt <- xt + x.intersp * xchar
if(plot)
text2(xt, yt, labels = legend,
adj = adj,
cex = max(1,min(cex, na.rm=TRUE)))
invisible(list(rect = list(w = w, h = h, left = left, top = top),
text = list(x = xt, y = yt)))
}
NULL
}
putKey <- function(z, labels, type=NULL,
pch=NULL, lty=NULL, lwd=NULL,
cex=par('cex'), col=rep(par('col'),nc),
transparent=TRUE, plot=TRUE, key.opts=NULL,
grid=FALSE)
{
if(grid)
{
require('grid')
require('lattice') # use draw.key in lattice
}
if(!.R. && !existsFunction('key'))
stop('must do library(trellis) to access key() function')
nc <- length(labels)
if(!length(pch)) pch <- rep(NA, nc)
if(!length(lty)) lty <- rep(NA, nc)
if(!length(lwd)) lwd <- rep(NA, nc)
pp <- !is.na(pch)
lp <- !is.na(lty) | !is.na(lwd)
lwd <- ifelse(is.na(lwd), par('lwd'), lwd)
if(!length(type)) type <- ifelse(!(pp | lp), 'n',
ifelse(pp & lp, 'b',
ifelse(pp, 'p', 'l')))
pch <- ifelse(is.na(pch) & type!='p' & type!='b',
if(.R.) NA else 0,
pch)
lty <- ifelse(is.na(lty) & type=='p',
if(.R.) NA else 1,
lty)
lwd <- ifelse(is.na(lwd) & type=='p', 1, lwd)
cex <- ifelse(is.na(cex) & type!='p' & type!='b', 1, cex)
if(!.R. && any(is.na(pch)))
stop("pch can not be NA for type='p' or 'b'")
if(!.R. && any(is.na(lty)))
stop("lty can not be NA for type='l' or 'b'")
if(any(is.na(lwd)))
stop("lwd can not be NA for type='l' or 'b'")
if(any(is.na(cex)))
stop("cex can not be NA for type='p' or 'b'")
m <- list()
m[[1]] <- as.name(if(grid) 'draw.key'
else if(.R.) 'rlegend' else 'key')
if(!grid)
{
m$x <- z[[1]]; m$y <- z[[2]]
}
if(.R.)
{
if(grid)
{
w <- list(text=list(labels, col=col))
if(!(all(is.na(lty)) & all(is.na(lwd))))
{
lns <- list()
if(!all(is.na(lty)))
lns$lty <- lty
if(!all(is.na(lwd)))
lns$lwd <- lwd
lns$col <- col
w$lines <- lns
}
if(!all(is.na(pch)))
w$points <- list(pch=pch, col=col)
m$key <- c(w, key.opts)
m$draw <- plot
if(plot)
m$vp <- viewport(x=unit(z[[1]], 'native'),
y=unit(z[[2]], 'native'))
z <- eval(as.call(m))
size <-
if(plot) c(NA,NA)
else
c(convertUnit(grobWidth(z), 'native', 'x', 'location', 'x',
'dimension', valueOnly=TRUE)[1],
convertUnit(grobHeight(z), 'native', 'y', 'location', 'y',
'dimension', valueOnly=TRUE)[1])
return(invisible(size))
}
else
{
m$legend <- labels
m$xjust <- m$yjust <- .5
m$plot <- plot
m$col <- col
m$cex <- cex
if(!all(is.na(lty))) m$lty <- lty
if(!all(is.na(lwd))) m$lwd <- lwd
if(!all(is.na(pch))) m$pch <- pch
if(length(key.opts)) m[names(key.opts)] <- key.opts
w <- eval(as.call(m))$rect
return(invisible(c(w$w[1], w$h[1])))
}
}
m$transparent <- transparent
m$corner <- c(.5,.5)
m$plot <- plot
m$type <- type
if(!plot) labels <- substring(labels, 1, 10)
## key gets length wrong for long labels
m$text <- list(labels, col=col)
if(all(type=='p'))
m$points <- list(pch=pch, cex=cex, col=col)
else
m$lines <-
if(any(type!='l'))
list(lty=lty, col=col, lwd=lwd, pch=pch, cex=cex)
else
list(lty=lty, col=col, lwd=lwd)
if(length(key.opts))
m[names(key.opts)] <- key.opts
invisible(eval(as.call(m))) ## execute key(....)
}
putKeyEmpty <- function(x, y, labels, type=NULL,
pch=NULL, lty=NULL, lwd=NULL,
cex=par('cex'), col=rep(par('col'),nc),
transparent=TRUE, plot=TRUE, key.opts=NULL,
empty.method=c('area','maxdim'),
numbins=25,
xlim=pr$usr[1:2], ylim=pr$usr[3:4],
grid=FALSE)
{
nc <- length(labels)
empty.method <- match.arg(empty.method)
pr <- parGrid(grid)
uin <- pr$uin
if(.R.)
uin <- 1 ## already in x,y units
z <- putKey(list(0, 0), labels, type, pch, lty, lwd, cex, col,
transparent=transparent, plot=FALSE,
key.opts=key.opts, grid=grid)/uin
## /uin converts to x,y units
## Find center of largest empty rectangle large enough to hold
## this rectangle
s <- is.finite(x + y)
if(length(xlim))
s <- s & (x >= xlim[1] & x <= xlim[2])
if(length(ylim))
s <- s & (y >= ylim[1] & y <= ylim[2])
x <- x[s]
y <- y[s]
keyloc <- largest.empty(x, y, xlim=xlim, ylim=ylim,
width=z[1], height=z[2],
method=empty.method, numbins=numbins, grid=grid)
if(is.na(keyloc$x))
{
cat('No empty area large enough for automatic key positioning. Specify keyloc or cex.\n')
cat('Width and height of key as computed by key(), in data units:',
format(z),'\n')
return(keyloc)
}
else if(plot) putKey(keyloc, labels, type,
pch, lty, lwd, cex, col, transparent, plot=TRUE,
key.opts=key.opts, grid=grid)
invisible(keyloc)
}
largest.empty <- function(x, y,
width, height,
numbins=25,
method=c('area','maxdim'),
xlim=pr$usr[1:2], ylim=pr$usr[3:4],
pl=FALSE, grid=FALSE)
{
method <- match.arg(method)
pr <- parGrid(grid)
itype <- 1 * (method=='area') + 2 * (method=='maxdim')
storage.mode(x) <- storage.mode(y) <- storage.mode(xlim) <-
storage.mode(ylim) <- storage.mode(width) <-
storage.mode(height) <- 'double'
storage.mode(numbins) <- storage.mode(itype) <- 'integer'
a <-
if(.R.)
.Fortran('largrec', x, y, as.integer(length(x)),
xlim, ylim,
width, height, as.integer(numbins), as.integer(itype),
rx=double(2), ry=double(2), PACKAGE="Hmisc")
else
.Fortran('largrec', x, y, as.integer(length(x)),
xlim, ylim,
width, height, as.integer(numbins), as.interger(itype),
rx=double(2), ry=double(2))
x <- a$rx
if(any(x > 1e29))
{
warning('no empty rectangle was large enough')
return(list(x=NA, y=NA))
}
y <- a$ry
if(pl)
ordGridFun(grid)$polygon(x[c(1,2,2,1)],y[c(1,1,2,2)], col=1+itype)
list(x=mean(x), y=mean(y))
}
drawPlot <- function(..., xlim=c(0,1), ylim=c(0,1), xlab='', ylab='',
ticks=c('none','x','y','xy'),
key=FALSE, opts=NULL)
{
Points <- function(label=' ', type=c('p','r'), n, pch=pch.to.use[1],
cex=par('cex'), rug=c('none','x','y','xy'),
ymean=NULL)
{
type <- match.arg(type)
rug <- match.arg(rug)
cat('\nClick mouse for each point',
if(label!='')
paste(' for group ',label),
'.',
if(missing(n))
' Right click when finished.',
'\n',sep='')
pts <-
if(missing(n))
locator(type='p',pch=pch,cex=cex)
else
locator(n, type='p', pch=pch, cex=cex)
if(length(ymean))
pts$y <- pts$y - mean(pts$y) + ymean
if(type=='p')
storeTemp(pch.to.use[pch.to.use != pch],'pch.to.use')
else
{
scat1d(pts$x, side=1)
pch <- NA
}
switch(rug,
x = scat1d(pts$x, side=1),
y = scat1d(pts$y, side=2),
xy = {scat1d(pts$x, side=1); scat1d(pts$y, side=2)},
none = )
structure(list(points=pts, label=label, type=type,
pch=pch, cex=cex, rug=rug), class='Points')
}
Curve <- function(label=' ',
type=c('bezier','polygon','linear','pol','step','gauss'),
n=NULL, lty=1, lwd=par('lwd'), degree=2,
evaluation=100, ask=FALSE)
{
isfun <- is.function(type)
if(!isfun)
type <- match.arg(type)
if(!isfun && !length(n) && type=='linear')
n <- 2
if(!isfun && type=='gauss')
n <- 3
xlim <- par('usr')[1:2]
redraw <- TRUE
if(isfun)
{
x <- seq(xlim[1], xlim[2], length=evaluation)
pts <- list(x=as.single(x), y=as.single(type(x)))
lines(pts, lty=lty, lwd=lwd)
}
else repeat
{
cat('\nClick mouse for each point',
if(label!='')
paste(' for group ',label),
'.',
if(!length(n))
' Right click when finished.',
'\n', sep='')
pts <-
if(!length(n))
locator(type='l', lty=lty, lwd=lwd)
else
locator(n, type='l', lty=lty, lwd=lwd)
n <- length(pts$x)
if(n < 2) stop('must click at least 2 points')
if(n==2) type <- 'linear'
if(type=='pol')
{
x <- matrix(NA, nrow=n, ncol=degree)
for(i in 1:degree) x[,i] <- pts$x^i
f <- lm.fit.qr.bare(x, pts$y)
x <- matrix(NA, nrow=evaluation, ncol=degree)
x[,1] <- seq(min(pts$x),max(pts$x), length=evaluation)
if(degree > 1)
for(i in 1:degree)
x[,i] <- x[,1]^i
cof <- f$coefficients
y <- cof[1] + x %*% cof[-1]
pts <- list(x=as.single(x[,1]), y=as.single(y))
if(redraw)
lines(pts, lty=lty, lwd=lwd)
}
if(type=='bezier')
{
pts <- bezier(pts, xlim=range(pts$x), evaluation=evaluation)
if(redraw)
lines(pts, lty=lty, lwd=lwd)
}
if(type=='gauss')
{
mu <- pts$x[2]
delta <- diff(pts$x[-2])/2
htavg <- sum(pts$y[-2])/2
htmax <- pts$y[2]
x <- seq(xlim[1], xlim[2], length=evaluation)
b2 <- delta^2 / log(htmax/htavg)
y <- htmax * exp(-(x-mu)^2/b2)
i <- y > 1e-4
pts <- list(x=as.single(x[i]), y=as.single(y[i]))
lines(pts, lty=lty, lwd=lwd)
}
if(type=='step' && redraw)
lines(pts, type='s', lty=lty, lwd=lwd)
if(!ask) break
if(readline('\nType y to accept, n to re-draw:')=='y')
break
}
structure(list(points=pts, label=label, type=type, lty=lty,
lwd=lwd), class='Curve')
}
Abline <- function(...)
{
abline(...)
structure(list(...), class='Abline')
}
storeTemp(Points)
storeTemp(Curve)
storeTemp(Abline)
storeTemp(c(1,2,3,4,16,17,5,6,15,18,19),'pch.to.use')
ticks <- match.arg(ticks)
if(missing(ticks))
{
if(!missing(xlim))
ticks <- 'x'
if(!missing(ylim))
ticks <- 'y'
if(!missing(xlim) && !missing(ylim))
ticks <- 'xy'
}
plot(xlim, ylim, xlim=xlim, ylim=ylim, xlab=xlab, ylab=ylab,
type='n', axes=ticks=='xy')
switch(ticks,
none={
axis(1, at=xlim, labels=FALSE)
axis(2, at=ylim, labels=FALSE)
},
x={
axis(1)
axis(2, at=ylim, labels=FALSE)
},
y={
axis(1, at=xlim, labels=FALSE)
axis(2)
},
xy = )
W <- list(...)
m <- length(W)
type <- label <- rep('', m)
lty <- lwd <- pch <- cex <- rep(NA, m)
curves <- vector('list', m)
i <- 0
for(j in 1:m)
{
w <- W[[j]]
if(attr(w,'class')=='Abline')
next
i <- i + 1
isfun <- is.function(w$type)
curves[[i]] <-
if(!key || isfun)
w$points
else switch(w$type,
step = approx(w$points,
xout=seq(min(w$points$x),max(w$points$x),length=50),
method='constant', f=0),
linear = approx(w$points,
xout=seq(min(w$points$x),max(w$points$x),length=50)),
w$points)
label[i] <- w$label
type[i] <-
if(isfun)
'l'
else switch(w$type,
p='p',
r='r',
step='s',
'l')
if(type[i]=='p')
{
pch[i] <- w$pch
cex[i] <- w$cex
}
else if(type[i] != 'r')
{
lty[i] <- w$lty
lwd[i] <- w$lwd
}
}
if(i < m)
{
curves <- curves[1:i]
label <- label[1:i]
type <- type[1:i]
lty <- lty[1:i]
lwd <- lwd[1:i]
pch <- pch[1:i]
cex <- cex[1:i]
}
keyloc <- NULL
j <- type!='r'
if(any(j))
{
if(!key)
labcurve(curves[j], labels=label[j], type=type[j],
lty=lty[j], lwd=lwd[j], opts=opts)
else
{
x <- unlist(lapply(curves, function(z)z$x))
y <- unlist(lapply(curves, function(z)z$y))
keyloc <- putKeyEmpty(x, y, labels=label[j], type=type[j],
pch=pch[j], lty=lty[j],
lwd=lwd[j], cex=cex[j])
}
}
structure(list(W, xlab=xlab, ylab=ylab, xlim=xlim, ylim=ylim,
ticks=ticks, key=key, keyloc=keyloc, opts=opts),
class='drawPlot')
}
bezier <- function(x, y, xlim, evaluation=100)
{
if(missing(y))
{
y <- x[[2]]
x <- x[[1]]
}
n <- length(x)
X <- Y <- single(evaluation)
Z <- seq(0, 1, length=evaluation)
X[1] <- x[1];
X[evaluation] <- x[n]
Y[1] <- y[1];
Y[evaluation] <- y[n]
for(i in 2:(evaluation-1))
{
z <- Z[i]
xz <- yz <- 0
const <- (1 - z)^(n-1)
for(j in 0:(n-1)) {
xz <- xz + const*x[j+1]
yz <- yz + const*y[j+1]
const <- const* (n-1-j)/(j+1) * z/(1-z)
if(is.na(const))
prn(c(i,j,z))
}
X[i] <- xz; Y[i] <- yz
}
list(x=as.single(X), y=as.single(Y))
}
plot.drawPlot <- function(x, file, xlab, ylab, ticks,
key=x$key, keyloc=x$keyloc, ...)
{
if(missing(xlab))
xlab <- x$xlab
if(missing(ylab))
ylab <- x$ylab
xlim <- x$xlim
ylim <- x$ylim
if(missing(ticks))
ticks <- x$ticks
if(!missing(file))
setps(file, type='char', ...)
plot(xlim, ylim, xlim=xlim, ylim=ylim, xlab=xlab, ylab=ylab,
type='n', axes=ticks=='xy')
switch(ticks,
none={
axis(1, at=xlim, labels=FALSE)
axis(2, at=ylim, labels=FALSE)
},
x={
axis(1)
axis(2, at=ylim, labels=FALSE)
},
y={
axis(1, at=xlim, labels=FALSE)
axis(2)
},
xy= )
data <- x[[1]]
m <- length(data)
type <- label <- rep('', m)
lty <- lwd <- pch <- cex <- rep(NA, m)
curves <- vector('list', m)
i <- 0
for(j in 1:m) {
w <- data[[j]]
if(attr(w, 'class') == 'Abline') {
do.call("abline", oldUnclass(w))
next
}
i <- i + 1
if(is.function(w$type))
w$type <- 'l'
curves[[i]] <-
if(!key)
w$points
else switch(w$type,
step = approx(w$points,
xout=seq(min(w$points$x),max(w$points$x),length=50),
method='constant', f=0),
linear = approx(w$points,
xout=seq(min(w$points$x),max(w$points$x),length=50)),
w$points)
label[i] <- w$label
switch(attr(w, 'class'),
Points = {
type[i] <- w$type
pch[i] <- w$pch
cex[i] <- w$cex
switch(w$type,
p = points(w$points, cex=w$cex, pch=w$pch),
r = scat1d(w$points$x, side=1))
switch(w$rug,
x = scat1d(w$points$x, side=1),
y = scat1d(w$points$y, side=2),
xy = {
scat1d(w$points$x, side=1)
scat1d(w$points$y, side=2)
},
none = )
},
Curve = {
type[i] <-
if(w$type=='step')
's'
else
'l'
lty[i] <- w$lty
lwd[i] <- w$lwd
lines(w$points, lty=w$lty, lwd=w$lwd, type=type[i])
})
}
if(i < m)
{
curves <- curves[1:i]
label <- label[1:i]
type <- type[1:i]
pch <- pch[1:i]
lty <- lty[1:i]
lwd <- lwd[1:i]
cex <- cex[1:i]
}
if(key && !length(keyloc))
stop('you may not specify key=T unless key=T was specified to drawPlot or keyloc is specified to plot')
if(any(label!=''))
{
j <- type!='r'
if(any(j)) {
if(key) putKey(keyloc, labels=label[j],
type=type[j], pch=pch[j],
lty=lty[j], lwd=lwd[j], cex=cex[j])
else
labcurve(curves[j], type=type[j],
lty=lty[j], lwd=lwd[j], labels=label[j], opts=x$opts)
}
}
if(!missing(file)) {
dev.off()
cat('\nCreated file ',file,'.ps\n',sep='')
}
invisible()
}