--- title: "Highlighting cells" format: html: minimal: true embed-resources: true vignette: > %\VignetteIndexEntry{Highlighting cells} %\VignetteEngine{quarto::format} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5, dpi = 96, out.width = "100%") ``` ```{r} #| include: false library(paintr) ``` When you draw a structure to teach with it, you almost always want to point at one part of it, whether *this* row, *that* column, or the cells that pass a test. Every paintr painter takes a `highlight_area` argument for exactly this, and every picture in this article is the same drawing twice, once plain and once with a patch of colour laid over the cells that matter. ## A highlight is a logical mask `highlight_area` wants a logical structure the same shape as your data, with `TRUE` wherever a cell should light up and `FALSE` everywhere else. Nothing more. Here is a small matrix and a mask that turns on its second row by hand. ```{r} m <- matrix(1:12, nrow = 3) mask <- matrix(FALSE, nrow = 3, ncol = 4) mask[2, ] <- TRUE mask ``` ```{r} #| fig-alt: "A 3 by 4 grid of the numbers 1 through 12 with its entire second row shaded, so the #| cells holding 2, 5, 8, and 11 are highlighted while the rest stay plain." paint_matrix(m, highlight_area = mask) ``` The mask and the data share a shape, so the painter reads them cell for cell. You will rarely build one by hand, though, because paintr gives you a small family of functions that build the mask for you. ## The three builders `highlight_data()` is the general builder, and three convenience wrappers cover the common cases. Each one returns a mask of the right shape that you hand straight to `highlight_area`. `highlight_rows()` takes a row selection as integer positions, a logical vector, or names when the data has them. ```{r} #| fig-alt: "The same 3 by 4 grid of 1 through 12 with its first and third rows shaded, highlighting #| the cells 1, 4, 7, 10 and 3, 6, 9, 12 while the middle row stays plain." paint_matrix(m, highlight_area = highlight_rows(m, c(1, 3))) ``` `highlight_columns()` takes a column selection the same way. ```{r} #| fig-alt: "The same 3 by 4 grid with its second and third columns shaded, highlighting the cells #| 4, 5, 6 and 7, 8, 9." paint_matrix(m, highlight_area = highlight_columns(m, 2:3)) ``` `highlight_locations()` reaches individual cells. For a two dimensional structure it takes a two column matrix of row/column pairs, one point per row. ```{r} #| fig-alt: "The same 3 by 4 grid with three individual cells shaded along a diagonal, the values 1, #| 8, and 12 at positions [1, 1], [2, 3], and [3, 4], while every other cell stays plain." points <- rbind( c(1, 1), c(2, 3), c(3, 4) ) paint_matrix(m, highlight_area = highlight_locations(m, points)) ``` All three are thin wrappers over `highlight_data()`, so you can also ask for rows and columns at once and get their union: ```{r} #| fig-alt: "The same 3 by 4 grid with its first row and fourth column both shaded, so the #| highlighted cells form an L: 1, 4, 7, 10 across the top and 10, 11, 12 down the right edge, #| meeting at the corner value 10." paint_matrix(m, highlight_area = highlight_data(m, rows = 1, columns = 4)) ``` ## A comparison is already a mask A mask is just a logical the shape of the data, and that is exactly what an R comparison hands back. So you can skip the builders entirely and pass a test: ```{r} m > 4 ``` ```{r} #| fig-alt: "The same 3 by 4 grid with every cell greater than 4 shaded, highlighting the values 5 #| through 12 while 1, 2, 3, and 4 stay plain; the shaded pattern is exactly the TRUE entries of #| the comparison m > 4." paint_matrix(m, highlight_area = m > 4) ``` The red-and-white grid of `m > 4` and the highlighted picture are the same object, the vectorised comparison made visible. `which()`, `is.na()`, and friends all produce masks you can drop straight in. ## A different colour The default highlight is a soft `"lemonchiffon"`. Any R colour works through `highlight_color`: ```{r} #| fig-alt: "The same grid with cells greater than 4 shaded, this time in light pink rather than the #| default pale yellow, showing that any R colour can fill the highlighted cells." paint_matrix(m, highlight_area = m > 4, highlight_color = "lightpink") ``` ## Highlighting works on every structure The invariant is simple. **If a painter can draw a structure, `highlight_data()` can mask it.** The builders take the same arguments whatever you point them at, and `highlight_columns()` in particular will name a column by its name for the structures that have names. A data frame column, selected by name: ```{r} #| fig-height: 5 #| fig-alt: "The first six rows of the iris data frame drawn as a table, with the whole Sepal.Width #| column shaded to pick it out by name from among Sepal.Length, Petal.Length, Petal.Width, and #| Species." df <- head(iris, 6) paint_data_frame(df, highlight_area = highlight_columns(df, "Sepal.Width")) ``` A list is drawn as its elements side by side, so its elements *are* its columns. `highlight_columns()` names one the same way it named a data frame column: ```{r} #| fig-alt: "A list drawn as three side-by-side columns headed $counts, $tags, and $ok of #| decreasing length, so the bottom edge is ragged; the $tags column, holding a, b, c, is shaded #| to select it by name just as a data frame column would be." l <- list(counts = c(3, 1, 4, 1), tags = c("a", "b", "c"), ok = c(TRUE, FALSE)) paint_list(l, highlight_area = highlight_columns(l, "tags")) ``` Because a data frame is a list whose elements share a length, and a mask is a mask, the same call reads the same way across the whole family. See `vignette("paintr", package = "paintr")` for the full tour of the five structures. ## Base and ggplot2, the same mask Highlighting is a property of the drawing, not of the backend, so the mask you build feeds a base picture and a ggplot2 picture without change. The matrices above were base graphics, and here is the identical highlight through `gpaint_*`: ```{r} #| fig-alt: "The 3 by 4 grid of 1 through 12 with every cell greater than 4 shaded, drawn through #| the ggplot2 backend; the highlight is identical to the base-graphics version above, since the #| mask is a property of the drawing, not the backend." gpaint_matrix(m, highlight_area = m > 4) ``` The `paint_*` and `gpaint_*` families take the same `highlight_area` and `highlight_color` arguments and mean the same thing by them. For how the two backends differ underneath, see `vignette("base-vs-ggplot2", package = "paintr")`.