knitr::opts_chunk$set(echo = TRUE) require(prepplot) require(ggplot2) require(grid) require(gridBase)
Base R graphics are very powerful, but the many different possibilities and graphical parameters can be overwhelming. Package prepplot has been developed for making it easier to customize figure regions for base R graphics. The concept is to prepare the figure region with prepplot, and to then add further graphical elements as desired; this can be done with any function that allows adding to an existing plot.
Inspired by Rahlf (2017), the default choice consists of a lightly colored background, and axis lines, tick marks and boxes are avoided. Orientation in the plot region can be supported by grid lines or background stripes with reasonable defaults regarding line type and color; customization is straightforward and, for example, allows to modify defaults such that a ggplot2-like look is obtained. Figure \@ref(fig:ggplot) shows a scatter plot on default prepplot background with grid lines and on a prepplot customization similar to the ggplot default, and compares it to a default ggplot scatter plot. The code for this figure is shown in the examples section.
par(mfrow=c(1, 3), mgp=c(2,0.2,0), mar=c(3.1,4.1,4.1,1.6)) ## prepplot grid default prepplot(swiss$Education, swiss$Fertility, ## manage grid gridx=TRUE, gridy=TRUE, ## annotation xlab="Education", ylab="Fertility", main="prepplot default grid") points(Fertility ~ Education, swiss, pch=16, col="grey20", cex=1.3) ## prepplot ggplot like prepplot(swiss$Education, swiss$Fertility, ## background color (like in version 0.7 of prepplot) bg = "grey92", ## manage grid gridx=TRUE, gridy=TRUE, col.grid="white", lty.grid="solid", ## manage axis annotation cex=0.75, cex.lab=1.35, col.axis="black", xlab="Education", ylab="Fertility", ## manage title main="prepplot ggplot like", cex.main=1.75, font.main=1, col.main="black") points(Fertility ~ Education, swiss, pch=16, cex=1.3) ## add the default ggplot plot ## following the answer in StackOverflow question # https://stackoverflow.com/questions/14124373/ # combine-base-and-ggplot-graphics-in-r-figure-window plot.new() vps <- baseViewports() pushViewport(vps$figure) ## in the third plot region vp1 <-plotViewport(c(0,0.5,1,0.25)) ## create new vp with margins p <- ggplot(swiss, aes(Education, Fertility)) + geom_point() + labs(title="ggplot default") print(p, vp = vp1)
prepplot consists of only two user-visible functions, prepplot and stripes. Function prepplot creates a plotting region for an x range and a y range, taking care of background color, and optionally specifying various further design and annotation elements of the figure region.
Function prepplot has many arguments, most of which have reasonable defaults and are thus optional. The documentation groups related arguments together in order to support locating arguments. While it is possible and convenient to specify most plot annotations within function prepplot, it is also possible to use prepplot for background color and stripes only and to do all further annotation with functions like mtext, title, axis, stripes (from prepplot) or abline.
The only compulsory arguments xlim and ylim specify the data limits for the two axes. They can either contain numeric vectors of length two like usual, or longer numeric vectors from which the length two vectors are calculated by function range. Depending on the choices of the arguments xaxs and yaxs (or their values in the graphics state, as set/queried by the par function), the plot region in the respective direction is 4% larger than specified by these limits (setting "r") or exactly matches these limits (setting "i").
prepplot requires numeric xlim and ylim specifications; it currently does not work with logarithmic axes. For a date, time or date-time axis, the user has to take care of conversion to numeric values before using prepplot (see the last example in the examples section). Axes with character-valued ticks are always based on numeric tick position specifications; see e.g. Figure \@ref(fig:barplots) for a barplot example.
The code below creates a default plot region with the default choice for xaxs and yaxs (assuming that the default has not been changed in par) and both choices modified to exact axis limits. The result is shown in Figure \@ref(fig:default); the bottom margin has been reduced, because it would produce a lot of empty space between the figure and its caption.
x <- 0:10 y <- -5:5 par(mfrow=c(1,2), mar=c(2.1, 4.1, 4.1, 2.1)) prepplot(x, y, main='default axis extension', bg="grey92", cex.axis=0.8, mgpx=c(2,0.25,0)) points(x,y, pch=16) mtext('xaxs="r"', line=0.25, family="mono") prepplot(x, y, xaxs="i", yaxs="i", main='limits match axis extent', bg="grey92", cex.axis=0.8, mgpx=c(2,0.25,0)) points(x,y, pch=16, xpd=TRUE) mtext('xaxs="i"', line=0.25, family="mono")
prepplot obeys to most general graphical parameters as set with function par (see ?par), e.g. mar. Where further graphical parameters can be modified from within prepplot (bg, xaxs, yaxs, xaxt, yaxt, cex), they default to the current par settings; this is partly also true for parameters that are set for specific components, like mgp, lwd or col; las is an exception: it is per default 1 in prepplot, regardless of par("las"). prepplot does not actively change the graphics state (as queried by par()).
For default plot regions,
las=1), i.e., easily readable from the reader's perspective (the y axis label, if specified, is parallel to the y axis),grey20),lwd.axis=0),lwd.axis to a positive value, the default border color becomes the annotation color specified with col.axis), xlab and ylab are NULL),Default plot regions are therefore very unobtrusive: they are neither highlighted by a background color nor bounded by a border line nor indicated by visible axis lines or tick marks. In most cases, one will want to activate one or more structural elements:
gridx, gridy), possibly major and minor;bg (e.g. to "grey92", which used to be its default in version~0.7 of the package): bg in prepplot affects the plot region only, which is the usr area calculated by R from the axis limits by extending them by 4% (arguments xaxs and/or yaxs equal to "r" or NULL, assuming unchanged par specifications) or not at all (arguments xaxs and/or yaxs equal to "i");stripex, stripey) as an alternative to grid lines;prepplot by setting positive lwd.axis (box and axis lines and ticks) or with subsequent axis and/or box statements.xaxs and yaxs) and the background areaIf xaxs="r", the horizontal extent of the plot region is 4% larger than the range obtained from the xlim specification; if xaxs="i", the x extent of the plot region exactly coincides with range(xlim). If xaxs=NULL, xaxs is determined from the current graphics state par("xaxs"). yaxs behaves analogously for the vertical extent of the plot region.
The background color colors the plot region, whose coordinates can also be queried by par("usr"), and the plot region is framed by the color specified with the border argument. If an axis line is not requested (default, lwd.axis=0), the default border color equals the background color; otherwise, the default border color equals the axis color.
Note that xaxs="i" or yaxs="i" should usually not be specified, if data values equal to one of the axis limits exist (see Figure \@ref(fig:default)). On the other hand, if the axis ranges have been chosen liberally, they may be a good choice, because the orientation marks often look more beautiful.
prepplot offers stripes and / or grid lines. Respective arguments are handled separately for x and y axes (e.g., gridx, stripesx).
Stripes and grid lines cannot be simultaneously requested for the same axis within function prepplot; if both are desired, stripes should be done with prepplot, and subsequently grid lines can be added with function abline. The simplest use is to activate grid lines or stripes by, e.g., gridx=TRUE or stripesy=TRUE; in that case, axis tick positions define the locations of grid lines or stripes. For stripes, swapping the colors between bg and col.stripes can make a big difference, as can be seen in Figure \@ref(fig:orient) by comparing the top and bottom chart in the middle column.
If grid lines are activated (gridx and/or gridy set to TRUE), these are dotted, whenever there are no minor grid lines and solid otherwise. Their default color is darker than the default background color but lighter than the default axis and annotation (grey75). Per default, if activated, minor grid lines are half as wide as major grid lines, whose width is governed by par("lwd") per default.
If stripes are activated without specifying specific stripes boundaries (stripesx and/or stripesy set to TRUE), the background is interrupted by stripes (default color light grey, grey90) between tick positions. Depending on the device's default background color, this default may or may not be adequate.
Apart from using stripes or grid lines for orientation w.r.t. the axis scale, they can also be used for displaying information independent of the tick positions, e.g. grid lines for time points of interest or upper limits of harmful substances, or stripes for indicating time periods of interest in time series presentations (see, e.g., the last example). Such stripes or grid lines can be requested with function prepplot, or subsequently with functions abline or stripes.
Per default, prepplot displays tick position labelings without ticks, and~\textendash~if axis labels are given~\textendash~axis labels. Axis annotation color is controlled by col.axis, which defaults to grey20, i.e. a dark grey. Tick position labels default to tick positions, but can be modified by xticklabs/yticklabs, e.g. for character or date values.
If default tick positions from R's calculations are not appropriate, xticks can be used for specifying custom tick positions for the horizontal axis. If xticks is not given (i.e. remains NULL), a vector-valued gridx or (if that is not available either) stripesx determines the x tick positions; default R tick positions are only calculated if neither of these is given. By giving both xticks and one of gridx or stripesx, stripes or lines corresponding to the horizontal axis can be specified independent of tick mark positions, as mentioned before. yticks and friends are completely analogous.
The distance of the tick position labels and axis labels from the plot region (i.e. the colored background, which coincides with the rectangle specified by par("usr") coordinates) is controlled by the mgp graphical parameter, a three-element vector (default c(3, 1, 0)), which gives the number of lines the axis label, the tick mark labels and the axis line are outside the usr rectangle (the specified line refers to the closest position, wide tick position labels will extend into the margin much more than the specified value). Options mgpx and mgpy can be used to control these positionings independent of the current par("mgp") setting and separately for the two axes; both default to par("mgp"), and mgpy defaults to mgpx, if that is specified. One should always think about mgp in relation to the margin settings (par("mar")); in the author's opinion, both mgp and mar settings are often too large, and occasionally much too narrow (e.g. for the left margin of horizontal barplots). Whether or not the margin size is appropriate also depends on whether or not non-NULL axis labels (xlab and ylab) are specified.
Per default, there is no axis line and ticks at the tick positions are not drawn. If lwd.axis is set to a positive value, a typical R axis is drawn, per default with a same color border around the plot region, and the axis observes parameter settings like par("tcl"); the ... argument of function prepplot can also be used for specifying such parameters, e.g. tcl=-0.2 specifies shorter tick marks on the outside of the usr area than the R default (effective only for positive lwd.axis).
Suppressing axes can also be useful, e.g. when subsequently using functions for which it is difficult to suppress axes; an example of this is given in Section 3.3.
The \dots argument of prepplot allows to specify a title and / or sub title (with main and / or sub), and further arguments like col.main or font.main can be used for their formatting. Alternatively and even additionally, subsequent annotation with axis, mtext or title can be used.
prepplot allows to add arrows to the plot region: option axis.arrow takes care of that. The default axes can be provided with an arrow by setting axis.arrow to TRUE; this draws axes with width equal to 1.5*lwd.grid, i.e. lwd.axis=0 does not prevent axis arrows from being drawn. With axis.arrow=TRUE, it is usually advisable to keep the respective xaxs or yaxs at its default "r" (or NULL).
A two-element numeric vector axis.arrow (instead of a logical) requests axes with arrows in positions given by the two vector elements. prepplot keeps axis annotation at the margin of the plot region (oberving mgp specification), regardless where axis arrows are placed. An example with default axes with arrows is in the prepplot help, and Section 3.5 of this vignette has an example with a differently placed axis arrow.
The examples in this section illustrate a few uses of function prepplot.
The following simple example uses major and minor grid lines on white background. The result is shown in Figure \@ref(fig:majorminor).
par(mar=par("mar")-1, mgp=c(2,0.3,0)) prepplot(c(0,10), c(25,30), yticks=25:30, bg="white", lwd.grid.minor=1, gridyminor=4, gridx=TRUE, xlab="x axis label", ylab="y axis label", main="Major/minor grid on white background")
The overview section already showed the default gridlines and customized gridlines mimicking ggplot style (Figure \@ref(fig:ggplot)). The code for that example is shown below; note that it uses package gridBase for placing the grid-based ggplot figure in the third position of the base graphics layout.
It will often be of interest to adapt some par settings before calling function prepplot, e.g. mar, mgp, tcl, xaxs, yaxs, ... The following code exemplifies such parameter settings, together with a selection of layouts; the results are shown in Figure \@ref(fig:parandmore).
par(mfrow=c(2,2), mar=c(3.1, 4.1, 3.1, 2.1), mgp=c(2,0.5,0), xaxs="i", yaxs="i") prepplot(xlim=c(0,9), ylim=c(0,12), bg="grey92", col.grid="grey98", lty.grid=1, lwd.grid=1.5, gridx=0:9, gridy=seq(0,12,2), xlab="x", ylab="y", cex = 0.7, mgpx=c(1.5,0.25,0), mgpy=c(1.75,0.5,0), main="ggplot style") prepplot(xlim=c(0,9), ylim=c(0,12), gridx=0:9, gridy=seq(0,12,2), xlab="x", ylab="y", cex = 0.7, main="default grid lines") prepplot(xlim=c(0,9), ylim=c(0,12), gridx=0:9, gridy=seq(0,12,2), xlab="x", ylab="y", cex = 0.7, lty.grid="solid", mgpx=c(2,0.5,0), main="default solid grid lines") prepplot(ylim=c(0,12), xlim=c(0,9), stripesx=0:9, xlab="x", yaxt="n", cex = 0.7, mgpx=c(2,0.5,0), main="default vertical stripes, no y axis")
The following code creates barplots with transparent bars on striped background. Function prepplot provides the plot region and the x axis annotation, while the vertical axis annotation comes from function barplot, which is called with the add=TRUE argument. Figure \@ref(fig:barplots) shows the result.
tab <- c(one=10, two=5, three=8, four=7, five=12, six=10, seven=9) par(mfrow=c(1,2), mar=c(3.1, 4.1, 3.1, 2.1)) prepplot(ylim=c(0,8.5), xlim=c(-0.2,13), yaxs="i", stripesx=seq(0,12,2), xlab="Frequency", yaxt="n", cex = 0.8, mgpx=c(2,0.5,0), main="Default colors", cex.main=1.2) barplot(tab, col=rgb(0.5, 0.5, 0.5, 0.7), border=FALSE, add=TRUE, xaxt="n",las=2, horiz=TRUE, cex.names=0.8) hks51 <- rgb(0, 152/256, 161/256) prepplot(ylim=c(0,8.5), xlim=c(-0.2,13), yaxs="i", xticks=seq(0,12,2), stripesx=TRUE, xlab="Frequency", bg=rgb(235/256, 246/256, 246/256), col.stripes = rgb(190/256, 226/256, 226/256, 0.4), col.axis=hks51, cex=0.8, col.main=hks51, yaxt="n", tcl=-0.2, mgpx=c(2,0.5,0), main="Beuth colors", cex.main=1.2, col.main=hks51) barplot(tab, col=rgb(0,152/256,161/256,0.7), border=FALSE, add=TRUE, xaxt="n",las=2, horiz=TRUE, cex.names=0.8, col.axis=rgb(0,152/256,161/256))
The following code creates scatter plots on various backgrounds, illustrating various orientation strategies, which are on offer for function prepplot. The result is shown in Figure \@ref(fig:orient). Note: The charts use the default axis extent (ranges of Education and Fertility, extended by 4\%); a custom axis with well-chosen limits and suppressing extension by 4\% could yield constant widths for all stripes, also the outer ones. Also remember the previous remark on the effect of swapping bg and col.stripes.
par(mfrow=c(2,3), oma=c(2,0,0,0), mgp=c(2.25,0.5,0), mar=c(3.1, 4.1, 3.1, 2.1)) ## default axis system with grid lines prepplot(xlim=swiss$Education, ylim=swiss$Fertility, mgpy=c(2.5,0.5,0), xlab="Education", ylab="Fertility", gridx=TRUE, gridy=TRUE, cex=0.8, main="grid lines") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") ## stripes and grid lines prepplot(xlim=swiss$Education, ylim=swiss$Fertility, mgpy=c(2.5,0.5,0), xlab="Education", ylab="Fertility", stripesx=seq(-5,60,5), gridy=seq(30,100,10), cex=0.8, xticks=seq(0,50,10), yticks = seq(40,90,10), main="stripes and grid lines") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") prepplot(xlim=range(swiss$Education), ylim=range(swiss$Fertility), mgpy=c(2.5,0.5,0), xlab="Education", ylab="Fertility", gridx=seq(0,60,10), stripesy=seq(30,100,5), cex=0.8, xticks=seq(0,50,10), yticks = seq(40,90,10), main="stripes and grid lines") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") ## stripes only prepplot(xlim=range(swiss$Education), ylim=range(swiss$Fertility), mgpy=c(2.5,0.5,0), bg="grey90", col.stripes = "white", xlab="Education", ylab="Fertility", stripesx=seq(-5,60,5), stripesy=seq(30,100,5), cex=0.8, xticks=seq(0,50,10), yticks = seq(40,90,10), main="crossing stripes") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") prepplot(xlim=range(swiss$Education), ylim=range(swiss$Fertility), mgpy=c(2.5,0.5,0), xlab="Education", ylab="Fertility", stripesx=seq(-5,60,5), cex=0.8, xticks=seq(0,50,10), yticks = seq(40,90,10), main="only vertical stripes", bg="grey90", col.stripes = "white") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") prepplot(xlim=range(swiss$Education), ylim=range(swiss$Fertility), mgpy=c(2.5,0.5,0), xlab="Education", ylab="Fertility", stripesy=seq(30,100,5), cex=0.8, yticks = seq(40,90,10), xticks=seq(0,50,10), main="only horizontal stripes") points(swiss$Education, swiss$Fertility, pch=16, col="grey20") mtext(side=1, line=0.5, 'Some strategies are better than others ...', cex=0.8, outer=TRUE, col="grey20")
This section exemplifies axis arrows (first example, Figure \@ref(fig:stripes)), simultaneous use of stripes and grid lines on the same dimension and a date-time axis (second example, Figure \@ref(fig:datetime)).
prepplot(0:10, -5:5, yticks=seq(-5,5,5), stripesy=-5:5, xlab="x", ylab="y", main="with stripes and arrow axis", cex=0.8, cex.main=1.2, axis.arrow = c(0,NA),arrow.length = 0.5,arrow.width = 0.5, lwd=2)
It sometimes make sense to combine stripes and grid lines for the same dimension, e.g. on a time axis. For example, think of some technical equipment with down times shown as stripes and days separated by grid lines. Such an application is exemplified below.
par(mar=c(1.5, 3, 3, 1), mgp=c(3,0.2,0)) xlim <- as.numeric(as.POSIXlt(c("2017-12-28 18:09:46","2018-01-02 09:15:22"))) stripelims <- as.numeric(as.POSIXlt( c("2017-12-29 10:30:00","2017-12-29 12:00:00", "2017-12-30 10:00:00","2017-12-30 12:30:00", "2017-12-31 10:00:00","2017-12-31 12:30:00", "2018-01-01 10:30:00","2018-01-01 11:30:00", "2018-01-01 16:30:00","2018-01-01 17:45:00"))) griddatts <- as.POSIXlt(c( "2017-12-29 00:00:00", "2017-12-30 00:00:00", "2017-12-31 00:00:00", "2018-01-01 00:00:00", "2018-01-02 00:00:00")) gridposs <- as.numeric(griddatts) tickposs <- gridposs + 12*3600 tickposs <- tickposs[tickposs < xlim[2]] prepplot(xlim=xlim, ylim=c(-5,10), stripesx=stripelims, xaxt="n", cex=0.7, gridy=TRUE, gridyminor=4, main="Date-time data with stripes and grid lines on same axis") abline(v=gridposs, col="grey20") axis(side=1, at=tickposs, labels=as.Date(as.POSIXlt(tickposs, origin="1970-01-01 00:00.00 UTC")), lwd=0, cex.axis=0.7)
Murrell, P. (2011). R graphics. CRC, Boca Raton. \url{https://www.stat.auckland.ac.nz/~paul/RG2e/}
Rahlf, T. (2017). Data visualisation with R. Springer, New York.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.