Powered by AppSignal & Oban Pro

Complete themes: ggtheme

notebooks/ggtheme.livemd

Complete themes: ggtheme

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 theme_grey(), which covers theme_gray(), theme_bw(), theme_linedraw(), theme_light(), theme_dark(), theme_minimal(), theme_classic(), theme_void() and theme_test() 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 complete theme sets how everything in a plot that is not data is drawn: backgrounds, gridlines, axes, text, legends and strips. theme_gray(), also spelt theme_grey(), is ggplot2's default: a grey panel with white gridlines. The others vary it, from theme_bw()'s dark lines on white, through theme_classic()'s axis lines without gridlines, to theme_void(), which draws nothing but the data; theme_test() is meant for visual tests and should not change. Each takes a base font size and family, base sizes for lines and rectangles, and ink, paper and accent colours. theme() adjusts a theme rather than replacing it. Giggly has each of them but theme_test(), spelt one way, as Giggly.Plot.theme_gray/2, Giggly.Plot.theme_bw/2 and the rest, each taking base_size:, base_family:, header_family:, base_line_size: and base_rect_size:, but no ink, paper or accent colours (ink, paper, accent). A plot that chooses none is drawn in theme_gray(), and Giggly.Plot.theme/2 adjusts a theme. 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 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.

The default theme

R's within() turns vs, am, cyl and gear into factors, giving vs and am words for their values.

mtcars2 <- within(mtcars, {
  vs <- factor(vs, labels = c("V-shaped", "Straight"))
  am <- factor(am, labels = c("Automatic", "Manual"))
  cyl  <- factor(cyl)
  gear <- factor(gear)
})

p1 <- ggplot(mtcars2) +
  geom_point(aes(x = wt, y = mpg, colour = gear)) +
  labs(
    title = "Fuel economy declines as weight increases",
    subtitle = "(1973-74)",
    caption = "Data from the 1974 Motor Trend US magazine.",
    tag = "Figure 1",
    x = "Weight (1000 lbs)",
    y = "Fuel economy (mpg)",
    colour = "Gears"
  )

p1 + theme_gray() # the default

The cell makes mtcars2 as within() does. vs and am become factors of their words, each with Giggly.Factor.new/2 and its levels in R's order, so that the panels further down follow them; cyl and gear become their numbers written out, so gear is discrete and its legend lists 3, 4 and 5. Giggly.Plot.theme_gray/2 draws the plot in theme_gray(), which is Giggly's default too.

labels = %{vs: ["V-shaped", "Straight"], am: ["Automatic", "Manual"]}

mtcars2 =
  Enum.map(mtcars, fn
    {column, values} when column in [:vs, :am] ->
      levels = labels[column]
      words = Enum.map(values, fn value -> Enum.at(levels, round(value)) end)
      {column, Giggly.Factor.new(words, levels: levels)}

    {column, values} when column in [:cyl, :gear] ->
      {column, Enum.map(values, fn value -> Integer.to_string(round(value)) end)}

    unchanged ->
      unchanged
  end)

p1 =
  mtcars2
  |> Plot.new()
  |> Plot.geom_point(%{x: :wt, y: :mpg, colour: :gear})
  |> Plot.labs(
    title: "Fuel economy declines as weight increases",
    subtitle: "(1973-74)",
    caption: "Data from the 1974 Motor Trend US magazine.",
    tag: "Figure 1",
    x: "Weight (1000 lbs)",
    y: "Fuel economy (mpg)",
    colour: "Gears"
  )

p1
|> Plot.theme_gray()
|> render.()

Other complete themes

p1 + theme_bw()
p1 + theme_linedraw()
p1 + theme_light()
p1 + theme_dark()
p1 + theme_minimal()
p1 + theme_classic()
p1 + theme_void()
p1
|> Plot.theme_bw()
|> render.()
p1
|> Plot.theme_linedraw()
|> render.()
p1
|> Plot.theme_light()
|> render.()
p1
|> Plot.theme_dark()
|> render.()
p1
|> Plot.theme_minimal()
|> render.()
p1
|> Plot.theme_classic()
|> render.()
p1
|> Plot.theme_void()
|> render.()

The default theme with panels

# Theme examples with panels
p2 <- p1 + facet_grid(vs ~ am)

p2 + theme_gray() # the default

vs ~ am gives the panels a row for each value of vs and a column for each of am, which Giggly.Plot.facet_grid/2 takes as rows: :vs, cols: :am.

p2 = Plot.facet_grid(p1, rows: :vs, cols: :am)

p2
|> Plot.theme_gray()
|> render.()

Other complete themes with panels

p2 + theme_bw()
p2 + theme_linedraw()
p2 + theme_light()
p2 + theme_dark()
p2 + theme_minimal()
p2 + theme_classic()
p2 + theme_void()
p2
|> Plot.theme_bw()
|> render.()
p2
|> Plot.theme_linedraw()
|> render.()
p2
|> Plot.theme_light()
|> render.()
p2
|> Plot.theme_dark()
|> render.()
p2
|> Plot.theme_minimal()
|> render.()
p2
|> Plot.theme_classic()
|> render.()
p2
|> Plot.theme_void()
|> render.()