Draft

NPC with ‘ggplot2’ and ‘ggpp’

Contrasts, multiple comparisons and simple labels

Plotting examples
Author

Pedro J. Aphalo

Published

2024-04-22

Modified

2024-04-22

Abstract

Example R code for plots annotations using NPC () values for the x and/or y aesthetics. The examples demonstrate how to do what used to be possible only with ‘ggpp’ with ‘ggplot2’ >=3.5.0, posibly still with a little help from ‘ggpp’.

Keywords

ggplot2 pkg, ggpp pkg, data labels, plot annotations

Warning

Version 3.5.0 of package ‘ggplot2’ and future version 0.5.7 of package ‘ggpp’ are required.

Version 0.5.7 of package ‘ggpp’ will be the first to include as_npc(), as_npcx() and as_npcy() used in some examples in this page.

Tip

In this page most code chunks are “folded” so as to decrease the clutter when searching for examples. A few code chunks that are reused across several plots are by default unfolded to make them more visible. Above each plot you will find one or more “folded” code chuncks signalled by a small triangle followed by “Code”. Clicking on the triangle “unfolds” the code chunk making visible the R code used to produce the plot.

The code in the chunks can be copied by clicking on the top right corner, where an icon appears when the mouse cursor hovers over the code listing.

The </> Code drop down menu to the right of the page title makes it possible to unfold all code chunks and to view the Quarto source of the whole web page.

Names of functions and other R objects are linked to the corresponding on-line help pages. The names of R extension packages are linked to their documentation web sites when available.

1 Introduction

NPC or “Normalised Parent Coordinates” are the default coordinate system used by package ‘grid’. Package ‘grid’ is part of R and is the basis on which ‘ggplot2’ is built. It provides the low level functions that do the actual drawing of the plots.

Normalised Parent Coordinates are relative to a viewport, and vary between zero and one. The plotting area of a ggplot is a ‘grid’ viewport. We use NPC values when we want to skip any mapping or scaling and directly specify a position on the plotting area, irrespective of whatever else is plotted in it.

There are two approaches to achieve this, skipping all scaling and mapping stages within ‘ggplot2’ code, which is possible since version 3.5.0, or what ‘ggpp’ used to do without accessing ‘ggplot2’ code, to compute the values in data units corresponding to an NPC value and pass those to ‘ggplot2’.

Obviously, the approach used by ‘ggplot2’ is simpler and more elegant. Still, this code requires the use of numeric values while ‘ggpp’ supports named positions. (Beware that this new approach still has limitations and rough edges.)

In ‘ggpp’ 0.5.7 I added simple functions that make it possible to use named positions with any geometry relying on the new approach implemented in ‘ggplot2’ 3.5.0.

Package ggpp imports and reexports all definitions from ggplot2.

Important

One needs to always check that annotations do not occlude anything significant, such as observations in the base plot. This needs special care when using annotations together with batch plotting. Either ensure that the scale limits of the base plot are expanded to avoid overlap or that the layer with the equations is the lowest one, i.e., added to the plot first.

2 Data labels and plot annotations

Data labels add textual information directly related to individual data points (shown as glyphs). Text position in this case is dependent on the scales used to represent data points. Text is usually displaced so that it does not occlude the glyph representing the data point and when the link to the data point is unclear, this link is signalled with a line segment or arrow. Data labels are distinct from annotations in that they contribute directly to the representation of data on a plot or map.

Annotations differ from data labels, in that their position is decoupled from their meaning. Insets can be thought as larger, but still self-contained annotations. In most cases the reading of inset tables and plots depends only weakly on the plot or map in which they are included.

In the case of annotations and insets the designer of a data visualization has the freedom to locate them anywhere, as long as they do not occlude features used to describe data. I will use the term annotation irrespective if the “labels” are textual or graphical.

The labelled segments used to highlight pairwise comparisons are a special case as they behave as data labels along the axis onto which an explanatory factor has been mapped, usually x, but as annotation along the axis onto which a continuous numeric variable has been mapped, usually y.

3 Load the packages

Load and attach the packages.

4 Code examples

4.1 Using ‘ggpp’

Make a simple plot to label.

Code
my.cars <- mtcars
my.cars$cyl <- factor(my.cars$cyl)

p1 <- ggplot(my.cars, aes(cyl, mpg)) +
  geom_boxplot(width = 0.33)
p1

We add an annotation in the top right corner as has been possible for some time with package ‘ggpp’.

Code
p1 +
  annotate(geom = "text_npc", 
           npcy = "top", npcx = "right", 
           label = "a", size = 6, color = "red")

Code
p1 +
  geom_text_npc(npcy = "top", npcx = "right", label = "a", size = 6, color = "red")

Code
p1 +
  geom_text_npc(npcy = "top", npcx = "right", label = "a", size = 6, color = "red") +
  facet_wrap(facets = vars(vs))

Code
tags.df <- data.frame(labs = c("a", "b"), 
                      vs = c(0, 1))
p1 +
  geom_text_npc(data = tags.df,
                mapping = aes(label = labs), 
                npcy = "top", npcx = "right",
                size = 6, color = "red") +
  facet_wrap(facets = vars(vs))

4.2 Using ‘ggpplot2’

The approach in ‘ggplot2’ is based on “protecting” the value with I() the identity function that sets the class to "AsIs", which is later detected by ‘ggplot2’ code.

Functions as_npcx() and as_npcy() map words into numerical values and pass the values to I().

Code
as_npcy("top")
[1] 0.95
Code
class(as_npcy("top"))
[1] "AsIs"
Code
as_npcx("right")
[1] 0.95
Code
class(as_npcx("right"))
[1] "AsIs"

The examples only partly work in ‘ggplot2’ 3.5.0.9000, as it seems that there is no support for factors mapped to x or y. Here I was able to use an "AsIs" only for the y aesthetic, while for x NPC is not supported.

Code
p1 +
  annotate(geom = "text",
           y = as_npcy("top"), x = 3.5, 
           label = "a", size = 6, color = "red")

This case does not yet work with ‘ggplot2’.

Code
p1 +
  geom_text(y = as_npc("top"), x = 3.5, label = "a", size = 6, color = "red")

Code
p1 +
  geom_text(y = as_npc("top"), x = 3.4, label = "a", size = 6, color = "red") +
  facet_wrap(facets = vars(vs))

Code
tags.df <- data.frame(labs = c("a", "b"), 
                      vs = c(0, 1))
p1 +
  geom_text(data = tags.df,
            mapping = aes(label = labs), 
            y = as_npc("top"), x = 3.4,
            size = 6, color = "red") +
  facet_wrap(facets = vars(vs))

Code
tags.df <- data.frame(labs = c("a", "b"), 
                      vs = c(0, 1))
p1 +
  geom_text(data = tags.df,
            mapping = aes(label = labs), 
            y = as_npc("top"), x = 3.4,
            size = 6, color = "red") +
  facet_wrap(facets = vars(vs))