--- title: "magicaxis: Pretty Scientific Plotting" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{magicaxis: Pretty Scientific Plotting} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 6 ) set.seed(42) library(magicaxis) ``` ## Overview **magicaxis** provides functions for making pretty, publication-quality scientific plots with base R graphics. It handles minor tick marks, log-scale axes, 2-D density contours, image display, hexagonal binning, and triangle (corner) plots for MCMC chains. Since version 2.6.0, all internal argument dispatch uses [ParmOff](https://github.com/asgr/ParmOff) instead of `do.call`. This lets extra `...` arguments be forwarded flexibly to each sub-function: unrecognised arguments are silently dropped, so callers no longer need to know exactly which parameters each inner function accepts. --- ## magaxis – pretty axis labelling `magaxis()` adds nicely formatted tick marks and labels to an existing plot. It supports log axes, minor ticks, Hershey fonts, and grid lines. ```{r magaxis-basic} plot(10^(1:9), 1:9, log = "x", axes = FALSE, xlab = "", ylab = "") magaxis(side = 1:2, xlab = "x (log scale)", ylab = "y") ``` Styling arguments such as `cex.axis` and `col.axis` are forwarded via ParmOff to the underlying `axis()` call, while `cex.lab` and `col.lab` go to `mtext()`: ```{r magaxis-styled} plot(1:10, (1:10)^2, axes = FALSE, xlab = "", ylab = "") magaxis(side = 1:2, xlab = "x", ylab = expression(x^2), cex.axis = 0.8, col.axis = "steelblue", cex.lab = 1.1, col.lab = "darkred") ``` Grid lines can be toggled per axis: ```{r magaxis-grid} plot(1:20, rnorm(20), axes = FALSE, xlab = "", ylab = "") magaxis(side = 1:2, grid = TRUE, grid.col = "grey80", grid.lty = 2, xlab = "Index", ylab = "Value") ``` --- ## magplot – high-level scatter plots `magplot()` wraps `plot()` to produce pretty axes automatically. For a simple scatter the axis labelling, grid, and limits are handled internally: ```{r magplot-scatter} x <- 10^(1:9) y <- 1:9 magplot(log10(x), y, unlog = "x", xlab = "x", ylab = "y") ``` Log axes on both sides: ```{r magplot-loglog} magplot(x, y, log = "xy", xlab = "x", ylab = "y") ``` Sigma-clipping the axis limits to focus on where the data actually are: ```{r magplot-clip} temp <- cbind(rt(500, df = 1.5), rt(500, df = 1.5)) magplot(temp, xlim = 2, ylim = 2, xlab = "x", ylab = "y", main = "2-sigma clipped") ``` ### z-coloured scatter When a `z` vector is supplied, points are coloured by a colour map and an optional colour bar is added. The `magmap` and `magbar` arguments are filtered via ParmOff, so unknown args are dropped cleanly: ```{r magplot-z} n <- 300 x <- rnorm(n); y <- rnorm(n); z <- x^2 + y^2 magplot(x, y, z = z, xlab = "x", ylab = "y", zcol = hcl.colors(21, "YlOrRd"), dobar = TRUE) ``` --- ## maghist – histogram with statistics `maghist()` prints a summary and draws a styled histogram. It integrates naturally with `magplot()` because it returns a `histogram` object that `magplot()` detects: ```{r maghist-basic} maghist(rnorm(1000), xlab = "Value", verbose = FALSE) ``` Log-y histogram: ```{r maghist-logy} maghist(rnorm(1e4), log = "y", grid = TRUE, verbose = FALSE, xlab = "Value", ylab = "Count (log)") ``` Pass the returned object back through `magplot()`: ```{r maghist-replot} h <- maghist(10^runif(500, 0, 3), log = "x", verbose = FALSE) magplot(h, log = "y", xlab = "x (log)", ylab = "Count (log)") ``` --- ## magclip – automatic sigma-clipping `magclip()` iteratively removes outliers that are unlikely given a Normal distribution. It is used internally by `magplot()`, `maghist()`, and `magbin()` when a scalar `xlim`/`ylim` is supplied, but it is also useful on its own: ```{r magclip-basic} set.seed(42) x <- c(rnorm(500), runif(30, 5, 10)) # 500 Normal + 30 outliers result <- magclip(x, sigma = 3) cat("Before clipping:", length(x), "points\n") cat("After clipping:", length(result$x), "points\n") cat("Clipped range:", round(result$range, 2), "\n") ``` The `estimate` argument controls which tail is used to estimate the Normal dispersion. Use `'lo'` when contamination is on the high side only: ```{r magclip-lo} set.seed(43) x_hi <- c(rnorm(500), runif(40, 4, 8)) r_both <- magclip(x_hi) r_lo <- magclip(x_hi, estimate = 'lo') cat("Both sides kept:", length(r_both$x), "| Low-side only kept:", length(r_lo$x), "\n") ``` --- ## magrun – running statistics `magrun()` computes running medians (or means/modes) and scatter measures along a binned axis, ideal for overlaying a trend on noisy scatter data: ```{r magrun-basic} set.seed(44) n <- 1000 xx <- seq(0, 2, len = n) yy <- xx + rnorm(n) magplot(xx, yy, col = "lightgrey", pch = ".", xlab = "x", ylab = "y") run <- magrun(cbind(xx, yy), bins = 10) lines(run$x, run$y, col = "red", lwd = 2) lines(run$x, run$yquan[, 1], lty = 2, col = "red") lines(run$x, run$yquan[, 2], lty = 2, col = "red") ``` The `Nscale = TRUE` flag divides the scatter by √N, giving error-in-the-mean intervals (useful for assessing the significance of a trend): ```{r magrun-Nscale} set.seed(45) n <- 500 xx <- seq(0, 1, len = n) yy <- xx + rnorm(n) run_sd <- magrun(cbind(xx, yy), bins = 8, Nscale = FALSE, diff = TRUE) run_sem <- magrun(cbind(xx, yy), bins = 8, Nscale = TRUE, diff = TRUE) magplot(xx, yy, col = "lightgrey", pch = ".", xlab = "x", ylab = "y") magerr(run_sd$x, run_sd$y, ylo = run_sd$yquan[, 1], yhi = run_sd$yquan[, 2], col = "royalblue", length = 0, lty = 2) magerr(run_sem$x, run_sem$y, ylo = run_sem$yquan[, 1], yhi = run_sem$yquan[, 2], col = "red") legend("topleft", legend = c("scatter (1σ)", "SEM (1σ)"), col = c("royalblue", "red"), lty = c(2, 1), bty = "n") ``` --- ## magerr – error bars `magerr()` adds symmetric or asymmetric error bars in x and/or y to an existing plot. Lower errors are supplied via `xlo`/`ylo` and upper errors via `xhi`/`yhi` (defaulting to the lower value if omitted): ```{r magerr-basic} set.seed(46) n <- 12 x <- sort(runif(n)) y <- 2 * x + rnorm(n, sd = 0.15) magplot(x, y, xlab = "x", ylab = "y") magerr(x, y, xlo = 0.04, ylo = 0.1, yhi = 0.2) ``` Error ellipses are drawn when `corxy` is supplied: ```{r magerr-ellipse} set.seed(47) n <- 8 x <- runif(n, 0.1, 0.9) y <- runif(n, 0.1, 0.9) sx <- runif(n, 0.03, 0.08) sy <- runif(n, 0.05, 0.12) rho <- runif(n, -0.7, 0.7) magplot(x, y, xlab = "x", ylab = "y", pch = 16) magerr(x, y, xlo = sx, ylo = sy, corxy = rho) ``` A shaded error polygon is produced with `poly = TRUE`: ```{r magerr-poly} set.seed(48) x <- seq(0, 2 * pi, len = 30) y <- sin(x) + rnorm(30, sd = 0.1) magplot(x, y, type = "l", xlab = "x", ylab = "sin(x)") magerr(x, y, ylo = 0.15, yhi = 0.15, poly = TRUE, col = adjustcolor("steelblue", 0.3), border = NA) ``` --- ## magcurve – draw a function curve `magcurve()` evaluates and plots any R expression over a range. It is a drop-in replacement for `curve()` that uses `magplot()` for its base plot, giving pretty axes automatically: ```{r magcurve-basic} magcurve(sin(x), from = 0, to = 2 * pi, xlab = "x", ylab = "sin(x)") magcurve(cos(x), add = TRUE, col = "steelblue") legend("topright", legend = c("sin", "cos"), col = c("black", "steelblue"), lty = 1, bty = "n") ``` Log axes are supported: ```{r magcurve-log} magcurve(x^2, from = 1, to = 100, log = "xy", xlab = "x (log)", ylab = expression(x^2 ~ "(log)")) ``` --- ## magbar – colour bar legend `magbar()` adds a standalone colour bar to any existing plot. The `position` argument accepts compass-style strings (`'topright'`, `'bottomleft'`, etc.); `orient` switches between vertical (`'v'`) and horizontal (`'h'`): ```{r magbar-basic} n <- 300 set.seed(49) x <- rnorm(n); y <- rnorm(n); z <- x^2 + y^2 colramp <- hcl.colors(21, "YlOrRd") colvals <- magmap(z, range = c(1, length(colramp)))$map magplot(x, y, col = colramp[round(colvals)], pch = 16, xlab = "x", ylab = "y") magbar(position = "topright", range = range(z), col = colramp, orient = "v", title = expression(x^2 + y^2)) ``` A horizontal bar at the bottom: ```{r magbar-horizontal} magplot(x, y, col = colramp[round(colvals)], pch = 16, xlab = "x", ylab = "y") magbar(position = "bottomleft", range = range(z), col = colramp, orient = "h", title = "z") ``` --- ## magcon – 2-D density contours `magcon()` computes a 2-D kernel density estimate and draws contours at chosen probability levels (default 50%, 68%, 95%) together with an optional image and colour bar. ```{r magcon-basic} x <- rnorm(500); y <- x + rnorm(500, sd = 0.5) magcon(x, y, h = c(0.2, 0.2), xlab = "x", ylab = "y", barposition = 'topleft', bartitleshift = 0.5) ``` Contours only (no background image): ```{r magcon-noim} magcon(x, y, h = c(0.2, 0.2), doim = FALSE, dobar = FALSE, xlab = "x", ylab = "y") ``` --- ## magbin – 2-D binning `magbin()` bins 2-D data into hexagons, squares, or triangles and colours them by count or a user-supplied statistic: ```{r magbin-hex} xy <- cbind(rnorm(2000), rnorm(2000)) magbin(xy, shape = "hexagon", xlab = "x", ylab = "y") ``` Square bins with a z-statistic: ```{r magbin-square-z} magbin(xy, shape = "square", z = xy[, 1]^2 - xy[, 2]^2, colref = "zstat", sizeref = "count", xlab = "x", ylab = "y") ``` Log-log hexagonal bins: ```{r magbin-loglog} xylog <- cbind(10^rnorm(2000), 10^rnorm(2000)) magbin(xylog, log = "xy", xlab = "x (log)", ylab = "y (log)") ``` --- ## magimage – image display `magimage()` displays a matrix as an image with a colour map applied via `magmap()` and optional axes via `magaxis()`. The internal `image()` call is dispatched through ParmOff: ```{r magimage-basic} z <- outer(seq(-3, 3, len = 50), seq(-3, 3, len = 50), function(x, y) exp(-(x^2 + y^2))) magimage(z, xlab = "x", ylab = "y") ``` Asinh stretch to reveal faint structure (the default `stretch = "asinh"` is well suited to images with a wide dynamic range): ```{r magimage-stretch} z2 <- outer(seq(-3, 3, len = 50), seq(-3, 3, len = 50), function(x, y) exp(-x^2) + 0.05 * exp(-y^2 / 0.1)) magimage(z2, stretch = "asinh", xlab = "x", ylab = "y") ``` --- ## magtri – triangle (corner) plots for MCMC chains `magtri()` makes a triangle plot summarising posterior samples. The diagonal shows marginal densities, the lower triangle shows 2-D density contours (`magcon`), and the upper triangle shows scatter plots. ### Basic triangle plot ```{r magtri-basic} chains <- data.frame( alpha = rnorm(500, mean = 1, sd = 0.3), beta = rnorm(500, mean = -0.5, sd = 0.5), gamma = rnorm(500, mean = 2, sd = 0.8) ) magtri(chains, samples = 300) ``` ### Passing extra arguments via ParmOff Extra `...` arguments are now forwarded by ParmOff to `magaxis`, `magcon`, **and** `points`, so you can style each sub-function from the top-level call. Unrecognised arguments in one sub-function are silently dropped and not recycled, so there is no risk of "unused argument" errors: ```{r magtri-styled} # cex.axis → magaxis (tick-label size) # lty → magcon (overrides contour line types) # col → points (overrides the red mean-marker colour) magtri(chains, samples = 300, cex.axis = 0.7, lty = c(2, 1, 3), col = "purple") ``` ### Reference values Supply `refvals` to mark known true (or reference) parameter values with a blue vertical line: ```{r magtri-refvals} magtri(chains, samples = 300, refvals = c(1, -0.5, 2)) ``` ### Few labels per sub-panel Supply `majorn` to reduce the number of labelled ticks. ```{r magtri-majorn} magtri(chains, samples = 300, refvals = c(1, -0.5, 2), majorn = 2) ``` ### Custom axis labels Use the `lab` argument to supply expression labels for each parameter: ```{r magtri-labs} magtri(chains, samples = 300, lab = list( expression(alpha), expression(beta), expression(gamma) )) ```