labeling.R (22036B)
1 #' Functions for positioning tick labels on axes 2 #' 3 #' \tabular{ll}{ 4 #' Package: \tab labeling\cr 5 #' Type: \tab Package\cr 6 #' Version: \tab 0.2\cr 7 #' Date: \tab 2011-04-01\cr 8 #' License: \tab Unlimited\cr 9 #' LazyLoad: \tab yes\cr 10 #' } 11 #' 12 #' Implements a number of axis labeling schemes, including those 13 #' compared in An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes 14 #' by Talbot, Lin, and Hanrahan, InfoVis 2010. 15 #' 16 #' @name labeling-package 17 #' @aliases labeling 18 #' @docType package 19 #' @title Axis labeling 20 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 21 #' @references 22 #' Heckbert, P. S. (1990) Nice numbers for graph labels, Graphics Gems I, Academic Press Professional, Inc. 23 #' Wilkinson, L. (2005) The Grammar of Graphics, Springer-Verlag New York, Inc. 24 #' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010. 25 #' @keywords dplot 26 #' @seealso \code{\link{extended}}, \code{\link{wilkinson}}, \code{\link{heckbert}}, \code{\link{rpretty}}, \code{\link{gnuplot}}, \code{\link{matplotlib}}, \code{\link{nelder}}, \code{\link{sparks}}, \code{\link{thayer}}, \code{\link{pretty}} 27 #' @examples 28 #' heckbert(8.1, 14.1, 4) # 5 10 15 29 #' wilkinson(8.1, 14.1, 4) # 8 9 10 11 12 13 14 15 30 #' extended(8.1, 14.1, 4) # 8 10 12 14 31 32 #' # When plotting, extend the plot range to include the labeling 33 #' # Should probably have a helper function to make this easier 34 #' data(iris) 35 #' x <- iris$Sepal.Width 36 #' y <- iris$Sepal.Length 37 #' xl <- extended(min(x), max(x), 6) 38 #' yl <- extended(min(y), max(y), 6) 39 #' plot(x, y, 40 #' xlim=c(min(x,xl),max(x,xl)), 41 #' ylim=c(min(y,yl),max(y,yl)), 42 #' axes=FALSE, main="Extended labeling") 43 #' axis(1, at=xl) 44 #' axis(2, at=yl) 45 c() 46 47 48 49 #' Heckbert's labeling algorithm 50 #' 51 #' @param dmin minimum of the data range 52 #' @param dmax maximum of the data range 53 #' @param m number of axis labels 54 #' @return vector of axis label locations 55 #' @references 56 #' Heckbert, P. S. (1990) Nice numbers for graph labels, Graphics Gems I, Academic Press Professional, Inc. 57 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 58 #' @export 59 heckbert <- function(dmin, dmax, m) 60 { 61 range <- .heckbert.nicenum((dmax-dmin), FALSE) 62 lstep <- .heckbert.nicenum(range/(m-1), TRUE) 63 lmin <- floor(dmin/lstep)*lstep 64 lmax <- ceiling(dmax/lstep)*lstep 65 seq(lmin, lmax, by=lstep) 66 } 67 68 .heckbert.nicenum <- function(x, round) 69 { 70 e <- floor(log10(x)) 71 f <- x / (10^e) 72 if(round) 73 { 74 if(f < 1.5) nf <- 1 75 else if(f < 3) nf <- 2 76 else if(f < 7) nf <- 5 77 else nf <- 10 78 } 79 else 80 { 81 if(f <= 1) nf <- 1 82 else if(f <= 2) nf <- 2 83 else if(f <= 5) nf <- 5 84 else nf <- 10 85 } 86 nf * (10^e) 87 } 88 89 90 91 #' Wilkinson's labeling algorithm 92 #' 93 #' @param dmin minimum of the data range 94 #' @param dmax maximum of the data range 95 #' @param m number of axis labels 96 #' @param Q set of nice numbers 97 #' @param mincoverage minimum ratio between the the data range and the labeling range, controlling the whitespace around the labeling (default = 0.8) 98 #' @param mrange range of \code{m}, the number of tick marks, that should be considered in the optimization search 99 #' @return vector of axis label locations 100 #' @note Ported from Wilkinson's Java implementation with some changes. 101 #' Changes: 1) m (the target number of ticks) is hard coded in Wilkinson's implementation as 5. 102 #' Here we allow it to vary as a parameter. Since m is fixed, 103 #' Wilkinson only searches over a fixed range 4-13 of possible resulting ticks. 104 #' We broadened the search range to max(floor(m/2),2) to ceiling(6*m), 105 #' which is a larger range than Wilkinson considers for 5 and allows us to vary m, 106 #' including using non-integer values of m. 107 #' 2) Wilkinson's implementation assumes that the scores are non-negative. But, his revised 108 #' granularity function can be extremely negative. We tweaked the code to allow negative scores. 109 #' We found that this produced better labelings. 110 #' 3) We added 10 to Q. This seemed to be necessary to get steps of size 1. 111 #' It is possible for this algorithm to find no solution. 112 #' In Wilkinson's implementation, instead of failing, he returns the non-nice labels spaced evenly from min to max. 113 #' We want to detect this case, so we return NULL. If this happens, the search range, mrange, needs to be increased. 114 #' @references 115 #' Wilkinson, L. (2005) The Grammar of Graphics, Springer-Verlag New York, Inc. 116 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 117 #' @export 118 wilkinson <-function(dmin, dmax, m, Q = c(1,5,2,2.5,3,4,1.5,7,6,8,9), mincoverage = 0.8, mrange=max(floor(m/2),2):ceiling(6*m)) 119 { 120 best <- NULL 121 for(k in mrange) 122 { 123 result <- .wilkinson.nice.scale(dmin, dmax, k, Q, mincoverage, mrange, m) 124 if(!is.null(result) && (is.null(best) || result$score > best$score)) 125 { 126 best <- result 127 } 128 } 129 seq(best$lmin, best$lmax, by=best$lstep) 130 } 131 132 .wilkinson.nice.scale <- function(min, max, k, Q = c(1,5,2,2.5,3,4,1.5,7,6,8,9), mincoverage = 0.8, mrange=c(), m=k) 133 { 134 Q <- c(10, Q) 135 136 range <- max-min 137 intervals <- k-1 138 granularity <- 1 - abs(k-m)/m 139 140 delta <- range / intervals 141 base <- floor(log10(delta)) 142 dbase <- 10^base 143 144 best <- NULL 145 for(i in 1:length(Q)) 146 { 147 tdelta <- Q[i] * dbase 148 tmin <- floor(min/tdelta) * tdelta 149 tmax <- tmin + intervals * tdelta 150 151 if(tmin <= min && tmax >= max) 152 { 153 roundness <- 1 - ((i-1) - ifelse(tmin <= 0 && tmax >= 0, 1, 0)) / length(Q) 154 coverage <- (max-min)/(tmax-tmin) 155 if(coverage > mincoverage) 156 { 157 tnice <- granularity + roundness + coverage 158 159 ## Wilkinson's implementation contains code to favor certain ranges of labels 160 ## e.g. those balanced around or anchored at 0, etc. 161 ## We did not evaluate this type of optimization in the paper, so did not include it. 162 ## Obviously this optimization component could also be added to our function. 163 #if(tmin == -tmax || tmin == 0 || tmax == 1 || tmax == 100) 164 # tnice <- tnice + 1 165 #if(tmin == 0 && tmax == 1 || tmin == 0 && tmax == 100) 166 # tnice <- tnice + 1 167 168 if(is.null(best) || tnice > best$score) 169 { 170 best <- list(lmin=tmin, 171 lmax=tmax, 172 lstep=tdelta, 173 score=tnice 174 ) 175 } 176 } 177 } 178 } 179 best 180 } 181 182 183 184 185 ## The Extended-Wilkinson algorithm described in the paper. 186 187 ## Our scoring functions, including the approximations for limiting the search 188 .simplicity <- function(q, Q, j, lmin, lmax, lstep) 189 { 190 eps <- .Machine$double.eps * 100 191 192 n <- length(Q) 193 i <- match(q, Q)[1] 194 v <- ifelse( (lmin %% lstep < eps || lstep - (lmin %% lstep) < eps) && lmin <= 0 && lmax >=0, 1, 0) 195 196 1 - (i-1)/(n-1) - j + v 197 } 198 199 .simplicity.max <- function(q, Q, j) 200 { 201 n <- length(Q) 202 i <- match(q, Q)[1] 203 v <- 1 204 205 1 - (i-1)/(n-1) - j + v 206 } 207 208 .coverage <- function(dmin, dmax, lmin, lmax) 209 { 210 range <- dmax-dmin 211 1 - 0.5 * ((dmax-lmax)^2+(dmin-lmin)^2) / ((0.1*range)^2) 212 } 213 214 .coverage.max <- function(dmin, dmax, span) 215 { 216 range <- dmax-dmin 217 if(span > range) 218 { 219 half <- (span-range)/2 220 1 - 0.5 * (half^2 + half^2) / ((0.1 * range)^2) 221 } 222 else 223 { 224 1 225 } 226 } 227 228 .density <- function(k, m, dmin, dmax, lmin, lmax) 229 { 230 r <- (k-1) / (lmax-lmin) 231 rt <- (m-1) / (max(lmax,dmax)-min(dmin,lmin)) 232 2 - max( r/rt, rt/r ) 233 } 234 235 .density.max <- function(k, m) 236 { 237 if(k >= m) 238 2 - (k-1)/(m-1) 239 else 240 1 241 } 242 243 .legibility <- function(lmin, lmax, lstep) 244 { 245 1 ## did all the legibility tests in C#, not in R. 246 } 247 248 249 #' An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes 250 #' 251 #' \code{extended} is an enhanced version of Wilkinson's optimization-based axis labeling approach. It is described in detail in our paper. See the references. 252 #' 253 #' @param dmin minimum of the data range 254 #' @param dmax maximum of the data range 255 #' @param m number of axis labels 256 #' @param Q set of nice numbers 257 #' @param only.loose if true, the extreme labels will be outside the data range 258 #' @param w weights applied to the four optimization components (simplicity, coverage, density, and legibility) 259 #' @return vector of axis label locations 260 #' @references 261 #' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010. 262 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 263 #' @export 264 extended <- function(dmin, dmax, m, Q=c(1,5,2,2.5,4,3), only.loose=FALSE, w=c(0.25,0.2,0.5,0.05)) 265 { 266 eps <- .Machine$double.eps * 100 267 268 if(dmin > dmax) { 269 temp <- dmin 270 dmin <- dmax 271 dmax <- temp 272 } 273 274 if(dmax - dmin < eps) { 275 #if the range is near the floating point limit, 276 #let seq generate some equally spaced steps. 277 return(seq(from=dmin, to=dmax, length.out=m)) 278 } 279 280 if((dmax - dmin) > sqrt(.Machine$double.xmax)) { 281 #if the range is too large 282 #let seq generate some equally spaced steps. 283 return(seq(from=dmin, to=dmax, length.out=m)) 284 } 285 286 n <- length(Q) 287 288 best <- list() 289 best$score <- -2 290 291 j <- 1 292 while(j < Inf) 293 { 294 for(q in Q) 295 { 296 sm <- .simplicity.max(q, Q, j) 297 298 if((w[1]*sm+w[2]+w[3]+w[4]) < best$score) 299 { 300 j <- Inf 301 break 302 } 303 304 k <- 2 305 while(k < Inf) # loop over tick counts 306 { 307 dm <- .density.max(k, m) 308 309 if((w[1]*sm+w[2]+w[3]*dm+w[4]) < best$score) 310 break 311 312 delta <- (dmax-dmin)/(k+1)/j/q 313 z <- ceiling(log(delta, base=10)) 314 315 while(z < Inf) 316 { 317 step <- j*q*10^z 318 319 cm <- .coverage.max(dmin, dmax, step*(k-1)) 320 321 if((w[1]*sm+w[2]*cm+w[3]*dm+w[4]) < best$score) 322 break 323 324 min_start <- floor(dmax/(step))*j - (k - 1)*j 325 max_start <- ceiling(dmin/(step))*j 326 327 if(min_start > max_start) 328 { 329 z <- z+1 330 next 331 } 332 333 for(start in min_start:max_start) 334 { 335 lmin <- start * (step/j) 336 lmax <- lmin + step*(k-1) 337 lstep <- step 338 339 s <- .simplicity(q, Q, j, lmin, lmax, lstep) 340 c <- .coverage(dmin, dmax, lmin, lmax) 341 g <- .density(k, m, dmin, dmax, lmin, lmax) 342 l <- .legibility(lmin, lmax, lstep) 343 344 score <- w[1]*s + w[2]*c + w[3]*g + w[4]*l 345 346 if(score > best$score && (!only.loose || (lmin <= dmin && lmax >= dmax))) 347 { 348 best <- list(lmin=lmin, 349 lmax=lmax, 350 lstep=lstep, 351 score=score) 352 } 353 } 354 z <- z+1 355 } 356 k <- k+1 357 } 358 } 359 j <- j + 1 360 } 361 362 seq(from=best$lmin, to=best$lmax, by=best$lstep) 363 } 364 365 366 367 ## Quantitative evaluation plots (Figures 2 and 3 in the paper) 368 369 370 #' Generate figures from An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes 371 #' 372 #' Generates Figures 2 and 3 from our paper. 373 #' 374 #' @param samples number of samples to use (in the paper we used 10000, but that takes awhile to run). 375 #' @return produces plots as a side effect 376 #' @references 377 #' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010. 378 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 379 #' @export 380 extended.figures <- function(samples = 100) 381 { 382 oldpar <- par() 383 par(ask=TRUE) 384 385 a <- runif(samples, -100, 400) 386 b <- runif(samples, -100, 400) 387 low <- pmin(a,b) 388 high <- pmax(a,b) 389 ticks <- runif(samples, 2, 10) 390 391 generate.labelings <- function(labeler, dmin, dmax, ticks, ...) 392 { 393 mapply(labeler, dmin, dmax, ticks, SIMPLIFY=FALSE, MoreArgs=list(...)) 394 } 395 396 h1 <- generate.labelings(heckbert, low, high, ticks) 397 w1 <- generate.labelings(wilkinson, low, high, ticks, mincoverage=0.8) 398 f1 <- generate.labelings(extended, low, high, ticks, only.loose=TRUE) 399 e1 <- generate.labelings(extended, low, high, ticks) 400 401 figure2 <- function(r, names) 402 { 403 for(i in 1:length(r)) 404 { 405 d <- r[[i]] 406 407 #plot coverage 408 cover <- sapply(d, function(x) {max(x)-min(x)})/(high-low) 409 hist(cover, breaks=seq(from=-0.01,to=1000,by=0.02), xlab="", ylab=names[i], main=ifelse(i==1, "Density", ""), col="darkgray", lab=c(3,3,3), xlim=c(0.5,3.5), ylim=c(0,0.12*samples), axes=FALSE, border=FALSE) 410 #hist(cover) 411 axis(side=1, at=c(0,1,2,3,4), xlab="hello", line=-0.1, lwd=0.5) 412 413 # plot density 414 dens <- sapply(d, length) / ticks 415 hist(dens, breaks=seq(from=-0.01,to=10,by=0.02), xlab="", ylab=names[i], main=ifelse(i==1, "Density", ""), col="darkgray", lab=c(3,3,3), xlim=c(0.5,3.5), ylim=c(0,0.06*samples), axes=FALSE, border=FALSE) 416 axis(side=1, at=c(0,1,2,3,4), xlab="hello", line=-0.1, lwd=0.5) 417 } 418 } 419 420 par(mfrow=c(4, 2), mar=c(0.5,1.85,1,0), oma=c(1,0,1,0), mgp=c(0,0.5,-0.3), font.main=1, font.lab=1, cex.lab=1, cex.main=1, tcl=-0.2) 421 figure2(list(h1,w1, f1, e1), names=c("Heckbert", "Wilkinson", "Extended\n(loose)", "Extended\n(flexible)")) 422 423 figure3 <- function(r, names) 424 { 425 for(i in 1:length(r)) 426 { 427 d <- r[[i]] 428 steps <- sapply(d, function(x) round(median(diff(x)), 2)) 429 steps <- steps / (10^floor(log10(steps))) 430 tab <- table(steps) 431 barplot(rev(tab), xlim=c(0,0.4*samples), horiz=TRUE, xlab=ifelse(i==1,"Frequency",""), xaxt='n', yaxt='s', las=1, main=names[i], border=NA, col="gray") 432 } 433 } 434 435 par(mfrow=c(1,4), mar=c(0.5, 0.75, 2, 0.5), oma=c(0,2,1,1), mgp=c(0,0.75,-0.3), cex.lab=1, cex.main=1) 436 figure3(list(h1,w1, f1, e1), names=c("Heckbert", "Wilkinson", "Extended\n(loose)", "Extended\n(flexible)")) 437 par(oldpar) 438 } 439 440 441 442 #' Nelder's labeling algorithm 443 #' 444 #' @param dmin minimum of the data range 445 #' @param dmax maximum of the data range 446 #' @param m number of axis labels 447 #' @param Q set of nice numbers 448 #' @return vector of axis label locations 449 #' @references 450 #' Nelder, J. A. (1976) AS 96. A Simple Algorithm for Scaling Graphs, Journal of the Royal Statistical Society. Series C., pp. 94-96. 451 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 452 #' @export 453 nelder <- function(dmin, dmax, m, Q = c(1,1.2,1.6,2,2.5,3,4,5,6,8,10)) 454 { 455 ntick <- floor(m) 456 tol <- 5e-6 457 bias <- 1e-4 458 459 intervals <- m-1 460 x <- abs(dmax) 461 if(x == 0) x <- 1 462 if(!((dmax-dmin)/x > tol)) 463 { 464 ## special case handling for very small ranges. Not implemented yet. 465 } 466 467 step <- (dmax-dmin)/intervals 468 s <- step 469 470 while(s <= 1) 471 s <- s*10 472 while(s > 10) 473 s <- s/10 474 475 x <- s-bias 476 unit <- 1 477 for(i in 1:length(Q)) 478 { 479 if(x < Q[i]) 480 { 481 unit <- i 482 break 483 } 484 } 485 step <- step * Q[unit] / s 486 range <- step*intervals 487 488 x <- 0.5 * (1+ (dmin+dmax-range) / step) 489 j <- floor(x-bias) 490 valmin <- step * j 491 492 if(dmin > 0 && range >= dmax) 493 valmin <- 0 494 valmax <- valmin + range 495 496 if(!(dmax > 0 || range < -dmin)) 497 { 498 valmax <- 0 499 valmin <- -range 500 } 501 502 seq(from=valmin, to=valmax, by=step) 503 } 504 505 506 #' R's pretty algorithm implemented in R 507 #' 508 #' @param dmin minimum of the data range 509 #' @param dmax maximum of the data range 510 #' @param m number of axis labels 511 #' @param n number of axis intervals (specify one of \code{m} or \code{n}) 512 #' @param min.n nonnegative integer giving the \emph{minimal} number of intervals. If \code{min.n == 0}, \code{pretty(.)} may return a single value. 513 #' @param shrink.sml positive numeric by a which a default scale is shrunk in the case when \code{range(x)} is very small (usually 0). 514 #' @param high.u.bias non-negative numeric, typically \code{> 1}. The interval unit is determined as \code{\{1,2,5,10\}} times \code{b}, a power of 10. Larger \code{high.u.bias} values favor larger units. 515 #' @param u5.bias non-negative numeric multiplier favoring factor 5 over 2. Default and 'optimal': \code{u5.bias = .5 + 1.5*high.u.bias}. 516 #' @return vector of axis label locations 517 #' @references 518 #' Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) \emph{The New S Language}. Wadsworth & Brooks/Cole. 519 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 520 #' @export 521 rpretty <- function(dmin, dmax, m=6, n=floor(m)-1, min.n=n%/%3, shrink.sml = 0.75, high.u.bias=1.5, u5.bias=0.5 + 1.5*high.u.bias) 522 { 523 ndiv <- n 524 h <- high.u.bias 525 h5 <- u5.bias 526 527 dx <- dmax-dmin 528 if(dx==0 && dmax==0) 529 { 530 cell <- 1 531 i_small <- TRUE 532 U <- 1 533 } 534 else 535 { 536 cell <- max(abs(dmin), abs(dmax)) 537 U <- 1 + ifelse(h5 >= 1.5*h+0.5, 1/(1+h), 1.5/(1+h5)) 538 i_small = dx < (cell * U * max(1, ndiv) * 1e-07 * 3) 539 } 540 541 if(i_small) 542 { 543 if(cell > 10) 544 { 545 cell <- 9+cell/10 546 } 547 cell <- cell * shrink.sml 548 if(min.n > 1) cell <- cell/min.n 549 } 550 else 551 { 552 cell <- dx 553 if(ndiv > 1) cell <- cell/ndiv 554 } 555 556 if(cell < 20 * 1e-07) 557 cell <- 20 * 1e-07 558 559 base <- 10^floor(log10(cell)) 560 561 unit <- base 562 563 if((2*base)-cell < h*(cell-unit)) 564 { 565 unit <- 2*base 566 if((5*base)-cell < h5*(cell-unit)) 567 { 568 unit <- 5*base 569 if((10*base)-cell < h*(cell-unit)) 570 unit <- 10*base 571 } 572 } 573 574 # track down lattice labelings... 575 576 ## Maybe used to correct for the epsilon here?? 577 ns <- floor(dmin/unit + 1e-07) 578 nu <- ceiling(dmax/unit - 1e-07) 579 580 ## Extend the range out beyond the data. Does this ever happen?? 581 while(ns*unit > dmin+(1e-07*unit)) ns <- ns-1 582 while(nu*unit < dmax-(1e-07*unit)) nu <- nu+1 583 584 585 ## If we don't have quite enough labels, extend the range out to make more (these labels are beyond the data :( ) 586 k <- floor(0.5 + nu-ns) 587 if(k < min.n) 588 { 589 k <- min.n - k 590 if(ns >=0) 591 { 592 nu <- nu + k/2 593 ns <- ns - k/2 + k%%2 594 } 595 else 596 { 597 ns <- ns - k/2 598 nu <- nu + k/2 + k%%2 599 } 600 ndiv <- min.n 601 } 602 else 603 { 604 ndiv <- k 605 } 606 607 graphmin <- ns*unit 608 graphmax <- nu*unit 609 610 seq(from=graphmin, to=graphmax, by=unit) 611 } 612 613 #' Matplotlib's labeling algorithm 614 #' 615 #' @param dmin minimum of the data range 616 #' @param dmax maximum of the data range 617 #' @param m number of axis labels 618 #' @return vector of axis label locations 619 #' @references 620 #' \url{http://matplotlib.sourceforge.net/} 621 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 622 #' @export 623 matplotlib <- function(dmin, dmax, m) 624 { 625 steps <- c(1,2,5,10) 626 nbins <- m 627 trim <- TRUE 628 629 vmin <- dmin 630 vmax <- dmax 631 params <- .matplotlib.scale.range(vmin, vmax, nbins) 632 scale <- params[1] 633 offset <- params[2] 634 635 vmin <- vmin-offset 636 vmax <- vmax-offset 637 638 rawStep <- (vmax-vmin)/nbins 639 scaledRawStep <- rawStep/scale 640 641 bestMax <- vmax 642 bestMin <- vmin 643 644 scaledStep <- 1 645 chosenFactor <- 1 646 647 for (step in steps) 648 { 649 if (step >= scaledRawStep) 650 { 651 scaledStep <- step*scale 652 chosenFactor <- step 653 bestMin <- scaledStep * floor(vmin/scaledStep) 654 bestMax <- bestMin + scaledStep*nbins 655 if (bestMax >= vmax) 656 break 657 } 658 } 659 if (trim) 660 { 661 extraBins <- floor((bestMax-vmax)/scaledStep) 662 nbins <- nbins-extraBins 663 } 664 graphMin <- bestMin+offset 665 graphMax <- graphMin+nbins*scaledStep 666 667 seq(from=graphMin, to=graphMax, by=scaledStep) 668 } 669 670 .matplotlib.scale.range <- function(min, max, bins) 671 { 672 threshold <- 100 673 dv <- abs(max-min) 674 maxabsv<-max(abs(min), abs(max)) 675 if (maxabsv == 0 || dv/maxabsv<10^-12) 676 return(c(1, 0)) 677 678 meanv <- 0.5*(min+max) 679 680 if ((abs(meanv)/dv) < threshold) 681 offset<- 0 682 else if (meanv>0) 683 { 684 exp<-floor(log10(meanv)) 685 offset = 10.0^exp 686 } else 687 { 688 exp <- floor(log10(-1*meanv)) 689 offset <- -10.0^exp 690 } 691 exp <- floor(log10(dv/bins)) 692 scale = 10.0^exp 693 c(scale, offset) 694 } 695 696 697 698 #' gnuplot's labeling algorithm 699 #' 700 #' @param dmin minimum of the data range 701 #' @param dmax maximum of the data range 702 #' @param m number of axis labels 703 #' @return vector of axis label locations 704 #' @references 705 #' \url{http://www.gnuplot.info/} 706 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 707 #' @export 708 gnuplot <- function(dmin, dmax, m) 709 { 710 ntick <- floor(m) 711 power <- 10^floor(log10(dmax-dmin)) 712 norm_range <- (dmax-dmin)/power 713 p <- (ntick-1) / norm_range 714 715 if(p > 40) 716 t <- 0.05 717 else if(p > 20) 718 t <- 0.1 719 else if(p > 10) 720 t <- 0.2 721 else if(p > 4) 722 t <- 0.5 723 else if(p > 2) 724 t <- 1 725 else if(p > 0.5) 726 t <- 2 727 else 728 t <- ceiling(norm_range) 729 730 d <- t*power 731 graphmin <- floor(dmin/d) * d 732 graphmax <- ceiling(dmax/d) * d 733 734 seq(from=graphmin, to=graphmax, by=d) 735 } 736 737 738 739 #' Sparks' labeling algorithm 740 #' 741 #' @param dmin minimum of the data range 742 #' @param dmax maximum of the data range 743 #' @param m number of axis labels 744 #' @return vector of axis label locations 745 #' @references 746 #' Sparks, D. N. (1971) AS 44. Scatter Diagram Plotting, Journal of the Royal Statistical Society. Series C., pp. 327-331. 747 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 748 #' @export 749 sparks <- function(dmin, dmax, m) 750 { 751 fm <- m-1 752 ratio <- 0 753 key <- 1 754 kount <- 0 755 r <- dmax-dmin 756 b <- dmin 757 758 while(ratio <= 0.8) 759 { 760 while(key <= 2) 761 { 762 while(r <= 1) 763 { 764 kount <- kount + 1 765 r <- r*10 766 } 767 while(r > 10) 768 { 769 kount <- kount - 1 770 r <- r/10 771 } 772 773 b <- b*(10^kount) 774 if( b < 0 && b != trunc(b)) b <- b-1 775 b <- trunc(b)/(10^kount) 776 r <- (dmax-b)/fm 777 kount <- 0 778 key <- key+2 779 } 780 781 fstep <- trunc(r) 782 if(fstep != r) fstep <- fstep+1 783 if(r < 1.5) fstep <- fstep-0.5 784 fstep <- fstep/(10^kount) 785 ratio <- (dmax - dmin)*(fm*fstep) 786 kount <- 1 787 key <- 2 788 } 789 fmin <- b 790 c <- fstep*trunc(b/fstep) 791 if(c < 0 && c != b) c <- c-fstep 792 if((c+fm*fstep) > dmax) fmin <- c 793 794 seq(from=fmin, to=fstep*(m-1), by=fstep) 795 } 796 797 798 #' Thayer and Storer's labeling algorithm 799 #' 800 #' @param dmin minimum of the data range 801 #' @param dmax maximum of the data range 802 #' @param m number of axis labels 803 #' @return vector of axis label locations 804 #' @references 805 #' Thayer, R. P. and Storer, R. F. (1969) AS 21. Scale Selection for Computer Plots, Journal of the Royal Statistical Society. Series C., pp. 206-208. 806 #' @author Justin Talbot \email{jtalbot@@stanford.edu} 807 #' @export 808 thayer <- function(dmin, dmax, m) 809 { 810 r <- dmax-dmin 811 b <- dmin 812 kount <- 0 813 kod <- 0 814 815 while(kod < 2) 816 { 817 while(r <= 1) 818 { 819 kount <- kount+1 820 r <- r*10 821 } 822 while(r > 10) 823 { 824 kount <- kount-1 825 r <- r/10 826 } 827 b <- b*(10^kount) 828 if(b < 0) 829 b <- b-1 830 ib <- trunc(b) 831 b <- ib 832 b <- b/(10^kount) 833 r <- dmax-b 834 a <- r/(m-1) 835 kount <- 0 836 while(a <= 1) 837 { 838 kount <- kount+1 839 a <- a*10 840 } 841 while(a > 10) 842 { 843 kount <- kount-1 844 a <- a/10 845 } 846 ia <- trunc(a) 847 if(ia == 6) ia <- 7 848 if(ia == 8) ia <- 9 849 850 aa <- 0 851 if(a < 1.5) aa <- -0.5 852 a <- aa + 1 + ia 853 a <- a/(10^kount) 854 855 test <- (m-1) * a 856 test1 <- (dmax-dmin)/test 857 if(test1 > 0.8) 858 kod <- 2 859 860 if(kod < 2) 861 { 862 kount <- 1 863 r <- dmax-dmin 864 b <- dmin 865 kod <- kod + 1 866 } 867 } 868 869 iab <- trunc(b/a) 870 if(iab < 0) iab <- iab-1 871 c <- a * iab 872 d <- c + (m-1)*a 873 if(d >= dmax) 874 b <- c 875 876 valmin <- b 877 valmax <- b + a*(m-1) 878 879 seq(from=valmin, to=valmax, by=a) 880 } 881