--- title: "Introduction to baffle" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to baffle} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} op = par(no.readonly=TRUE) # save par to restore later knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) knitr::opts_knit$set(global.par = TRUE) library("baffle") ``` ```{r, include=FALSE} par("mar"=rep(0, 4)) ``` The package `baffle` stands for `base waffle` implements waffle charts in base R graphics. Waffles are square pie charts that visualize relative quantities using colored squares. Modern waffles charts are not limited to squares and use various dimensions and symbols. ## Basic waffles The `baffle::waffle` accepts a vector of abundancies `x` and constructs waffle chart accordingly. The vector of abundancies must be a numeric integer vector. For example, if we wish to visualize the difference between `3` apples and `8` oranges, all we need to type is: ```{r, eval=TRUE} waffle(c(3,8)) ``` By default, `waffle` tries to create a square matrices and uses the categorical `Set 1` palette, if there are 9 or less objects, and the continuous `Zissou` palette otherwise. Colors and dimensions can be specified specifying the `col`, `nrow` and `ncol` arguments. ```{r} par(mfrow=c(1, 3)) waffle(c(25, 75), col=c("red", "gray")) waffle(c(25, 75), col=c("blue", "gray"), ncol=5) waffle(c(25, 75), col=c("darkgreen", "gray"), nrow=5) ``` `baffle` fills the waffle by rows from the bottomleft corner. To change this, you can specify the `byrow` and `from` arguments: ```{r} par(mfrow=c(2,2)) waffle(c(13, 12), col=c("darkorchid", "gray")) waffle(c(13, 12), col=c("skyblue", "gray"), from="topright") waffle(c(13,12), col=c("tomato", "gray"), byrow=FALSE) waffle(c(13,12), col=c("springgreen", "gray"), from="bottomright", byrow=FALSE) ``` ### Workhorse functions Waffles in `baffle` are created by three basic functions: `waffle` allows one to plot waffles directly, as demonstrated above. It constructs the waffle matrix using the `design` function and then plots the waffle matrix with the `waffle.mat` function. In most cases, `waffle` is all one needs. `design` is a function responsible for transforming the vector of abundances into a waffle matrix. Waffle matrix is a matrix of integers, each integer corresponds to the item in the abundance vector, with empty cells having value `NA`. Design is used internally by the `waffle` function, or it can be used directly create a design matrix that can be then modified, such as to merge multiple design matrices. `waffle.mat` is the function responsible for plotting the waffle matrix. The matrix is transformed into a grid and a square corresponding to each cell of the matrix is plotted at each point of the grid. `waffle.mat` expect a matrix in the format produced by `design`, but custom matrices can be provided as well. ## Unstacked Waffles Traditional waffles are squared pie charts are used to display proportion of some whole. Some modern examples use waffles as bar charts, there the waffles are "unstacked" and abundance of each element is displayed side by side. ```{r, include=FALSE} par(mfrow=c(1,1)) ``` ```{r} par(mar=c(4,0,0,0)) cyl = table(mtcars$cyl) waffle(cyl, stacked=FALSE, gap=1) legend("top", horiz=TRUE, bty="n", inset=0.9, xpd=TRUE, legend=names(cyl), cex=2, fill=palette.colors(length(cyl), "Set 1"), border=NA, title="Number of cylinders\n in 'mtcars'") ``` ## Squares, Circles, Polygons Waffle plots are typically represented by coloured squares. Many modern examples favour different shapes to make themselves more graphically distinct. The `waffle` function is flexible and accepts a plotting function. Any function will do as long as it accepts `x` and `y` coordinates and the diameter of plotted object is slightly smaller than one. `baffle` comes with several of such functions, the default `square`, `rectangle`, `circle`, `ellipse`, regular convex polygon `rcpoly`. See `?Shapes` for more information. These functions are similar to the `graphics::symbols()`. These functions are vectorized, in fact even the `f` argument in the `waffle` is vectorized, so multiple polygons can be plotted effortesly. For example, we can represent the number of cylinders in the above example with regular n-sided polygons. ```{r} par(mar=c(4,0,0,0)) cyl = table(mtcars$cyl) waffle(cyl, stacked=FALSE, gap=1, f=rcpoly, n=as.numeric(names(cyl))) legend("top", horiz=TRUE, bty="n", inset=0.9, xpd=TRUE, legend=names(cyl), cex=2, fill=palette.colors(length(cyl), "Set 1"), border=NA, title="Number of cylinders\n in 'mtcars'") ``` Shame that the `legend` does not allow for such flexibility! ## Text and Points As noted above, any function that prints to `x` and `y` coordinates can be used! The `base` functions `points` and `text` are already conforming and can be readily used. Only setting their size can be a bit problematic. `1/strheight(s)` and `1/strwidth(s)` is the most answer, but these can be called only *after* the plotting window is defined. So one has to either check multiple values and select the most fitting one, which would be `4` in this example, or write a wrapper around `text` that calculates the label dimensions and selects the appropriate value. Since the plotting functions are called after the dimensions are set, one can now use `strheight` and `strwidth` to establish the text dimensions. ```{r} autotext = function(x, y, labels, d=0.9, ...){ cex = min(1/strheight(labels), 1/strwidth(labels))*d text(x, y, labels, cex=cex, ...) } par(mar=c(4,0,0,0)) cyl = table(mtcars$cyl) waffle(cyl, stacked=FALSE, gap=1, f=autotext, labels=names(cyl)) legend("top", horiz=TRUE, bty="n", inset=0.9, xpd=TRUE, legend=names(cyl), cex=2, fill=palette.colors(length(cyl), "Set 1"), border=NA, title="Number of cylinders\n in 'mtcars'") ``` This demonstrates how easy is to improve or convert existing functions to be perfectly useable by baffle. ## Bitmaps To make things easier, `baffle` provides a wrapper around the base R's `rasterImage` (in the `graphics` namespace) with the same interface as other shape functions. To read raster images (such as `png`) use the lightweight `png` package. We then plot the original as well as a single colour representation of it using `waffle` and the `rasters` function. ```{r, include=FALSE} par(mar=rep(0,4)) ``` ```{r} library("png") path = system.file("img/Rlogo.png", package="png") imgR = imgG = imgB = img = readPNG(path) imgR[,,c(2,3)] = 0 imgG[,,c(1,3)] = 0 imgB[,,c(1,2)] = 0 waffle(c(1,1,1,1), f=rasters, image=list(img, imgR, imgG, imgB), rotate=c(0,30,60,90)) ``` ```{r, include=FALSE} # Restore old par par(op) ```