The ggtext package defines two new geoms,
geom_richtext()
and geom_textbox()
, which can
be used to plot with markdown text. They draw simple text labels
(without word wrap) and textboxes (with word wrap), respectively.
Markdown-formatted text labels can be placed into a plot with
geom_richtext()
. This geom is mostly a drop-in replacement
for geom_label()
(or geom_text()
), with added
capabilities.
As a first example, we will annotate a plot of linear regressions
with their r2 values. We will use the
iris
dataset for this demonstration. In our first
iteration, we will not yet use any ggtext features, and instead plot the
text with geom_text()
.
library(ggplot2)
library(dplyr)
library(glue)
<- iris %>%
iris_cor group_by(Species) %>%
summarize(r_square = cor(Sepal.Length, Sepal.Width)^2) %>%
mutate(
# location of each text label in data coordinates
Sepal.Length = 8, Sepal.Width = 4.5,
# text label containing r^2 value
label = glue("r^2 = {round(r_square, 2)}")
)
iris_cor#> # A tibble: 3 × 5
#> Species r_square Sepal.Length Sepal.Width label
#> <fct> <dbl> <dbl> <dbl> <glue>
#> 1 setosa 0.551 8 4.5 r^2 = 0.55
#> 2 versicolor 0.277 8 4.5 r^2 = 0.28
#> 3 virginica 0.209 8 4.5 r^2 = 0.21
<- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
iris_facets geom_point() +
geom_smooth(method = "lm", formula = y ~ x) +
facet_wrap(~Species) +
theme_bw()
+
iris_facets geom_text(
data = iris_cor,
aes(label = label),
hjust = 1, vjust = 1
)
This code works, but the result is not fully satisfying. First,
because r is a mathematical variable, it should be typeset in
italics. Second, it would be nicer to have a superscript 2 instead of
^2. We can achieve both results by creating a markdown label and
plotting it with geom_richtext()
.
library(ggtext)
<- iris_cor %>%
iris_cor_md mutate(
# markdown version of text label
label = glue("*r*<sup>2</sup> = {round(r_square, 2)}")
)
iris_cor_md#> # A tibble: 3 × 5
#> Species r_square Sepal.Length Sepal.Width label
#> <fct> <dbl> <dbl> <dbl> <glue>
#> 1 setosa 0.551 8 4.5 *r*<sup>2</sup> = 0.55
#> 2 versicolor 0.277 8 4.5 *r*<sup>2</sup> = 0.28
#> 3 virginica 0.209 8 4.5 *r*<sup>2</sup> = 0.21
+
iris_facets geom_richtext(
data = iris_cor_md,
aes(label = label),
hjust = 1, vjust = 1
)
By default, geom_richtext()
puts a box around the text
it draws. We can suppress the box by setting the fill and outline colors
to transparent (fill = NA, label.colour = NA
).
+
iris_facets geom_richtext(
data = iris_cor_md,
aes(label = label),
hjust = 1, vjust = 1,
# remove label background and outline
fill = NA, label.color = NA,
# remove label padding, since we have removed the label outline
label.padding = grid::unit(rep(0, 4), "pt")
)
We can separately choose the colors of label outline, label fill, and label text, and we can assign them via aesthetic mapping as well as by direct specification, as is usual in ggplot2.
+
iris_facets aes(colour = Species) +
geom_richtext(
data = iris_cor_md,
aes(
label = label,
fill = after_scale(alpha(colour, .2))
),text.colour = "black",
hjust = 1, vjust = 1
+
) theme(legend.position = "none")
Rotated labels are also possible, though in most cases it is not recommended to use them.
+
iris_facets aes(colour = Species) +
geom_richtext(
data = iris_cor_md,
aes(
x = 7.5,
label = label,
fill = after_scale(alpha(colour, .2))
),text.colour = "black",
hjust = 1, vjust = 1,
angle = 30
+
) theme(legend.position = "none")
Markdown-formatted text boxes (with word wrap) can be placed into a
plot with geom_textbox()
. It is generally necessary to
specify a width for the box. Widths are specified in grid units, and
both absolute (e.g., "cm"
, "pt"
, or
"in"
) and relative ("npc"
, Normalised Parent
Coordinates) units are possible.
<- data.frame(
df x = 0.1,
y = 0.8,
label = "*Lorem ipsum dolor sit amet,* consectetur adipiscing
elit. Quisque tincidunt eget arcu in pulvinar. Morbi varius leo
vel consectetur luctus. **Morbi facilisis justo non fringilla.**
Vivamus sagittis sem felis, vel lobortis risus mattis eget. Nam
quis imperdiet felis, in convallis elit."
)
<- ggplot() +
p geom_textbox(
data = df,
aes(x, y, label = label),
width = grid::unit(0.73, "npc"), # 73% of plot panel width
hjust = 0, vjust = 1
+
) xlim(0, 1) + ylim(0, 1)
p
If we specify a relative width, then changing the size of the plot will change the size of the textbox. The text will reflow to accommodate this change.
p
The parameters hjust
and vjust
align the
box relative to the reference point specified by x
and
y
, but they do not affect the alignment of text inside the
box. To specify how text is aligned inside the box, use
halign
and valign
. For example,
halign = 0.5
generates centered text.
ggplot() +
geom_textbox(
data = df,
aes(x, y, label = label),
width = grid::unit(0.73, "npc"), # 73% of plot panel width
hjust = 0, vjust = 1,
halign = 0.5 # centered text
+
) xlim(0, 1) + ylim(0, 1)
While text boxes cannot be rotated arbitrarily, they can be placed in
four distinct orientations, corresponding to rotations by multiples of
90 degrees. Note that hjust
and vjust
are
specified relative to this orientation.
<- data.frame(
df x = 0.5,
y = 0.5,
label = "The quick brown fox jumps over the lazy dog.",
orientation = c("upright", "left-rotated", "inverted", "right-rotated")
)
ggplot() +
geom_textbox(
data = df,
aes(x, y, label = label, orientation = orientation),
width = grid::unit(1.5, "in"),
height = grid::unit(1.5, "in"),
box.margin = grid::unit(rep(0.25, 4), "in"),
hjust = 0, vjust = 1
+
) xlim(0, 1) + ylim(0, 1) +
scale_discrete_identity(aesthetics = "orientation")
The previous example uses the box.margin
argument to
create some space between the reference point given by x
,
y
and the box itself. This margin is part of the size
calculation for the box, so that a width of 1.5 inches with 0.25 inch
margins yields an actual box of 1 inch in width.