Powered by AppSignal & Oban Pro

Manual scales: scale_manual

notebooks/scale_manual.livemd

Manual scales: scale_manual

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 scale_colour_manual(), which covers scale_fill_manual(), scale_size_manual(), scale_shape_manual(), scale_linetype_manual(), scale_linewidth_manual(), scale_alpha_manual() and scale_discrete_manual() 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.

A manual scale maps each value of a discrete column to an aesthetic value given by hand: a colour, a fill, a size, a shape, a line type, a line width or a transparency. Each function works on the aesthetic in its name, though scale_colour_manual() and scale_fill_manual() can serve colour and fill at once through their aesthetics argument; scale_discrete_manual() works on whatever aesthetics that argument names. Giggly has Giggly.Plot.scale_color_manual/2, Giggly.Plot.scale_fill_manual/2, Giggly.Plot.scale_shape_manual/2 and Giggly.Plot.scale_linetype_manual/2, with their breaks:, labels: and limits:, but not the aesthetics option, so each serves its own aesthetic, nor scale_size_manual(), scale_linewidth_manual(), scale_alpha_manual() or scale_discrete_manual(). 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 these 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.

Colours by hand

p <- ggplot(mtcars, aes(mpg, wt)) +
  geom_point(aes(colour = factor(cyl)))
p + scale_colour_manual(values = c("red", "blue", "green"))

Unnamed values go to the column's values in order: the cars with 4, 6 and 8 cylinders red, blue and green. Giggly writes factor(cyl) as {:factor, [:cyl]}. CSS's "green" is #008000, darker than R's #00FF00, so the eight-cylinder cars are a darker green than ggplot2 draws them.

p =
  mtcars
  |> Plot.new(%{x: :mpg, y: :wt})
  |> Plot.geom_point(%{colour: {:factor, [:cyl]}})

p
|> Plot.scale_color_manual(values: ["red", "blue", "green"])
|> render.()

Colours named by value

# It's recommended to use a named vector
cols <- c("8" = "red", "4" = "blue", "6" = "darkgreen", "10" = "orange")
p + scale_colour_manual(values = cols)

Named values go to the column's values with those names, whatever order they are written in; a name the data lacks, here "10", is unused. Giggly's named vector is a map, and it names each value as the column holds it, so factor(cyl)'s levels are the numbers 8, 4, 6 and 10, where R names them as text.

cols = %{8 => "red", 4 => "blue", 6 => "darkgreen", 10 => "orange"}

p
|> Plot.scale_color_manual(values: cols)
|> render.()

Colour and fill at once

# You can set color and fill aesthetics at the same time
ggplot(
  mtcars,
  aes(mpg, wt, colour = factor(cyl), fill = factor(cyl))
) +
  geom_point(shape = 21, alpha = 0.5, size = 2) +
  scale_colour_manual(
    values = cols,
    aesthetics = c("colour", "fill")
  )

Not in Giggly yet: scale_colour_manual()'s aesthetics option.

Breaks and labels

# As with other scales you can use breaks to control the appearance
# of the legend.
p + scale_colour_manual(values = cols)
p + scale_colour_manual(
  values = cols,
  breaks = c("4", "6", "8"),
  labels = c("four", "six", "eight")
)

The first plot is the one above; the second labels the legend's keys as given, its breaks named as the column holds them.

p
|> Plot.scale_color_manual(values: cols)
|> render.()
p
|> Plot.scale_color_manual(values: cols, breaks: [4, 6, 8], labels: ["four", "six", "eight"])
|> render.()

Limits

# And limits to control the possible values of the scale
p + scale_colour_manual(values = cols, limits = c("4", "8"))
p + scale_colour_manual(values = cols, limits = c("4", "6", "8", "10"))

A value outside limits is treated as missing: in the first plot the six-cylinder cars are drawn in grey50, the missing value's colour, and the legend has keys for 4 and 8 alone. In the second, 10 has a key, which no car draws in, so it is empty.

p
|> Plot.scale_color_manual(values: cols, limits: [4, 8])
|> render.()
p
|> Plot.scale_color_manual(values: cols, limits: [4, 6, 8, 10])
|> render.()