Skip to contents

Many journals print in black, and colour figures are converted. A figure that does not depend on colour is safer. The screen aesthetic maps a discrete variable to a screen: a lattice angle, a dot shape, and a hatch angle for line screens. Angle alone distinguishes three groups. The default recipe also varies the shape.

Densities

ggplot(iris, aes(Sepal.Length, screen = Species, group = Species)) +
  with_halftone(geom_density(fill = "black", linewidth = 0.25), shape = "line") +
  scale_screen_discrete(name = NULL) +
  labs(x = "Sepal length (cm)", y = "Density") +
  theme_classic(base_size = 8) + theme_halftone() + theme(legend.position = "inside", legend.position.inside = c(0.82, 0.85), legend.key.size = unit(5, "mm"))

Where two densities overlap, the hatches cross. With dots instead of lines, overlapping groups share one lattice.

ggplot(iris, aes(Sepal.Length, fill = Species, group = Species)) +
  with_halftone(geom_density(linewidth = 0.25)) +
  labs(x = "Sepal length (cm)", y = "Density", fill = NULL) +
  theme_classic(base_size = 8) + theme_halftone() + theme(legend.position = "inside", legend.position.inside = c(0.82, 0.85))

Three intervals in one ink

The data are the lung cancer trial of Loprinzi et al. (1994). Hatched intervals are drawn as a hairline at the printable minimum.

Three overlapping hatches make a dense mesh, and a thin line inside it is hard to follow. Four settings fix that together: a coarser pitch (0.7 mm) so the mesh is open, a wider halo (0.2 mm) so the line gets its own channel, a heavier line (0.5), and long dashes, because a short dash disappears into the hatch.

library(survival)
fit <- survfit(Surv(time, status) ~ ph.ecog, data = subset(lung, ph.ecog < 3))
s <- km_steps(fit); levels(s$strata) <- paste("ECOG", levels(s$strata))

ggplot(s, aes(time, group = strata, screen = strata)) +
  with_halftone(geom_ribbon(aes(ymin = lo, ymax = hi), fill = "black"), shape = "line", pitch = 0.7) +
  with_halo(geom_step(aes(y = surv, linetype = strata), linewidth = 0.5), width = 0.2) +
  scale_screen_manual(values = c(45, 135, 0), name = NULL) +
  scale_linetype_manual(values = c("solid", "62", "22"), name = NULL) +
  scale_y_continuous(labels = scales::percent) +
  labs(x = "Days since diagnosis", y = "Overall survival") +
  theme_classic(base_size = 8) + theme_halftone() + theme(legend.position = "inside", legend.position.inside = c(0.82, 0.84), legend.key.size = unit(5, "mm"))

Screen specifications

A screen specification is a string of the form "angle|shape|tone|line_angle". A bare number is an angle. scale_screen_manual() accepts your own specifications. You can mix hatching and dots in one layer.

Legend keys are a sample of the screen at its own pitch and strip width, so a key reads at the same density as the fill it stands for.

d <- data.frame(g = c("a", "b", "c"), n = c(3, 2, 1))
ggplot(d, aes(g, n, screen = g)) +
  with_halftone(geom_col(fill = "black", colour = "black", linewidth = 0.25)) +
  scale_screen_manual(values = c("45|line", "15|circle", "135|line"), name = NULL) +
  labs(x = NULL, y = NULL) + theme_classic(base_size = 8) + theme_halftone()

Dot plots in one ink

geom_spot() draws one disc per point. The ink density inside the disc is the value. With shape = "line" the discs are hatched, and screen applies per group.

dp <- expand.grid(gene = c("CD3E", "CD8A", "NKG7", "MS4A1"), cluster = paste0("C", 1:5))
set.seed(1); dp$expr <- runif(nrow(dp)); dp$pct <- runif(nrow(dp), 0.3, 1)
ggplot(dp, aes(cluster, gene, tone = expr, size = pct)) +
  geom_spot(shape = "line", colour = "black") +
  scale_tone_continuous(name = "Expression") + scale_radius(range = c(1, 2.2), name = "Expressing") +
  labs(x = NULL, y = NULL) + theme_minimal() + theme_classic(base_size = 8) + theme_halftone() + theme(axis.ticks = element_blank()) + theme(axis.text.y = element_text(face = "italic"))

References

  • Loprinzi, C. L., Laurie, J. A., Wieand, H. S., et al. (1994). Prospective evaluation of prognostic variables from patient-completed questionnaires. North Central Cancer Treatment Group. Journal of Clinical Oncology, 12(3), 601-607. https://doi.org/10.1200/JCO.1994.12.3.601. Distributed as survival::lung.