Text: geom_text
Mix.install([
{:giggly, path: Path.join(__DIR__, "..")},
{:kino, "~> 0.19"}
])
alias Giggly.Plot
mtcars = Giggly.Datasets.mtcars()
render = fn plot ->
plot
|> Giggly.to_svg()
|> IO.iodata_to_binary()
|> Kino.Image.new(:svg)
end
About this notebook
This notebook follows the examples on ggplot2's reference page for
geom_text(), which covers geom_label()
too, in the order the page gives them. Each example shows ggplot2's code first and then Giggly's. An
example Giggly cannot draw yet keeps its ggplot2 code and names what it is waiting for, so the page
is also a list of what remains.
geom_text() draws each row's label as text at its x and y, and geom_label() draws it on
a rectangle, which makes it easier to read. They label points or bars, or make a scatter plot of
words. Text has no size in the data's units, so the axes do not grow to fit it, and it keeps its
size when the plot is resized. Giggly draws text with Giggly.Plot.geom_text/3; it has no
geom_label(). A plot is described with Giggly.Plot and drawn with Giggly.to_svg/2, which
returns an SVG document. The setup cell aliases Giggly.Plot, so the cells write Plot.new/2, and
defines render, which draws a plot and shows it below the cell with Kino.Image. The data in
most examples is Giggly.Datasets.mtcars/0, R's mtcars: 32 cars from the 1974 Motor Trend US
magazine, which the setup cell binds to mtcars, R's name for it.
Text at each point
R's rownames(mtcars) are the cars' names, which Giggly.Datasets.mtcars/0 keeps as its first
column, model.
p <- ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars)))
p + geom_text()
Each label is centred on its car's weight and mileage.
p = Plot.new(mtcars, %{x: :wt, y: :mpg, label: :model})
p
|> Plot.geom_text()
|> render.()
Avoiding overlaps
# Avoid overlaps
p + geom_text(check_overlap = TRUE)
check_overlap = TRUE leaves out each label that would overlap one already drawn, the rows taken
in order.
p
|> Plot.geom_text(check_overlap: true)
|> render.()
Labels with a background
# Labels with background
p + geom_label()
Not in Giggly yet: geom_label().
Larger text
# Change size of the label
p + geom_text(size = 10)
A text's size is in millimetres, so 10 draws labels about 28 points tall.
p
|> Plot.geom_text(size: 10)
|> render.()
Justifying, nudging and turning text
# Set aesthetics to fixed value
p +
geom_point() +
geom_text(hjust = 0, nudge_x = 0.05)
p +
geom_point() +
geom_text(vjust = 0, nudge_y = 0.5)
p +
geom_point() +
geom_text(angle = 45)
# Doesn't work on all systems
p +
geom_text(family = "Times New Roman")
hjust = 0 starts each label at its point, and nudge_x moves it a little to the right;
vjust = 0 and nudge_y do the same upwards. The axes reach the labels where they are drawn.
p
|> Plot.geom_point()
|> Plot.geom_text(hjust: 0, nudge_x: 0.05)
|> render.()
p
|> Plot.geom_point()
|> Plot.geom_text(vjust: 0, nudge_y: 0.5)
|> render.()
angle turns each label anticlockwise about its place, in degrees.
p
|> Plot.geom_point()
|> Plot.geom_text(angle: 45)
|> render.()
family names the font. Giggly measures a label as its default font does, and draws it at that
width in the font named, or in the default sans-serif font where the browser lacks it.
p
|> Plot.geom_text(family: "Times New Roman")
|> render.()
Mapping colour and fill
# Add aesthetic mappings
p + geom_text(aes(colour = factor(cyl)))
p + geom_text(aes(colour = factor(cyl))) +
scale_colour_hue(l = 40)
p + geom_label(aes(fill = factor(cyl)), colour = "white", fontface = "bold")
Giggly draws the first two plots, writing factor(cyl) as {:factor, [:cyl]}.
scale_colour_hue(l = 40) darkens the colours by lowering their luminance, which Giggly spells
out, luminance:.
p
|> Plot.geom_text(%{colour: {:factor, [:cyl]}})
|> render.()
p
|> Plot.geom_text(%{colour: {:factor, [:cyl]}})
|> Plot.scale_color_hue(luminance: 40)
|> render.()
Not in Giggly yet: geom_label() and a fixed fontface.
Mapping size
# Scale size of text, and change legend key glyph from a to point
p + geom_text(aes(size = wt), key_glyph = "point")
# Scale height of text, rather than sqrt(height)
p +
geom_text(aes(size = wt), key_glyph = "point") +
scale_radius(range = c(3,6))
Not in Giggly yet: mapping a text's size and geom_text()'s key_glyph option.
Labels as expressions
# You can display expressions by setting parse = TRUE. The
# details of the display are described in ?plotmath, but note that
# geom_text uses strings, not expressions.
p +
geom_text(
aes(label = paste(wt, "^(", cyl, ")", sep = "")),
parse = TRUE
)
Each label is written as, say, 2.62^(6), which R's plotmath draws as 2.62 with a superscript 6.
Not in Giggly yet: geom_text()'s parse option and R's plotmath expressions.
A text annotation
# Add a text annotation
p +
geom_text() +
annotate(
"text", label = "plot mpg vs. wt",
x = 2, y = 15, size = 8, colour = "red"
)
annotate() adds a layer with a single label, its position and look given as values rather than
mapped from columns. R's "red" is CSS's too, #FF0000.
p
|> Plot.geom_text()
|> Plot.annotate(:text, label: "plot mpg vs. wt", x: 2, y: 15, size: 8, colour: "red")
|> render.()
Labels on dodged columns
# Aligning labels and bars --------------------------------------------------
df <- data.frame(
x = factor(c(1, 1, 2, 2)),
y = c(1, 3, 2, 1),
grp = c("a", "b", "a", "b")
)
# ggplot2 doesn't know you want to give the labels the same virtual width
# as the bars:
ggplot(data = df, aes(x, y, group = grp)) +
geom_col(aes(fill = grp), position = "dodge") +
geom_text(aes(label = y), position = "dodge")
Not in Giggly yet: geom_text()'s position option, here position = "dodge".
Dodging labels by the columns' width
# So tell it:
ggplot(data = df, aes(x, y, group = grp)) +
geom_col(aes(fill = grp), position = "dodge") +
geom_text(aes(label = y), position = position_dodge(0.9))
Columns are 0.9 wide by default, so dodging the labels by 0.9 puts each over its column.
Not in Giggly yet: geom_text()'s position option and position_dodge()'s options. Giggly names
a position by an atom, position: :dodge, which takes no options yet.
Labels just above the columns
# You can't nudge and dodge text, so instead adjust the y position
ggplot(data = df, aes(x, y, group = grp)) +
geom_col(aes(fill = grp), position = "dodge") +
geom_text(
aes(label = y, y = y + 0.05),
position = position_dodge(0.9),
vjust = 0
)
Not in Giggly yet: geom_text()'s position option and position_dodge()'s options.
Labels in the middle of stacked columns
# To place text in the middle of each bar in a stacked barplot, you
# need to set the vjust parameter of position_stack()
ggplot(data = df, aes(x, y, group = grp)) +
geom_col(aes(fill = grp)) +
geom_text(aes(label = y), position = position_stack(vjust = 0.5))
Not in Giggly yet: geom_text()'s position option, and position_stack() and its vjust
option. Giggly names a position by an atom, position: :stack, which takes no options yet.
Justification
# Justification -------------------------------------------------------------
df <- data.frame(
x = c(1, 1, 2, 2, 1.5),
y = c(1, 2, 1, 2, 1.5),
text = c("bottom-left", "top-left", "bottom-right", "top-right", "center")
)
ggplot(df, aes(x, y)) +
geom_text(aes(label = text))
ggplot(df, aes(x, y)) +
geom_text(aes(label = text), vjust = "inward", hjust = "inward")
"inward" justifies each label towards the centre of the plot, so that the labels at its edges
stay inside it.
df = %{
"x" => [1.0, 1.0, 2.0, 2.0, 1.5],
"y" => [1.0, 2.0, 1.0, 2.0, 1.5],
"text" => ["bottom-left", "top-left", "bottom-right", "top-right", "center"]
}
df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_text(%{label: :text})
|> render.()
df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_text(%{label: :text}, vjust: :inward, hjust: :inward)
|> render.()