Box plots: geom_boxplot
Mix.install([
{:giggly, path: Path.join(__DIR__, "..")},
{:kino, "~> 0.19"}
])
alias Giggly.Plot
mpg = Giggly.Datasets.mpg()
diamonds = Giggly.Datasets.diamonds()
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_boxplot(), which covers
stat_boxplot() 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 box plot shows the distribution of a continuous column in five numbers: the median, two hinges
at the first and third quartiles, and two whiskers, each reaching to the furthest value within 1.5
times the inter-quartile range of its hinge. Every value beyond a whisker is drawn as a point of
its own, an outlier. stat_boxplot() computes the five numbers for each group, and
geom_boxplot() draws them, or draws numbers computed beforehand. Giggly has
Giggly.Plot.geom_boxplot/3, which computes the five numbers for each group itself, takes ggplot2's
options as keyword options (notch:, varwidth:, outlier_colour: and the look of each part),
and sets boxes that share a position side by side as position_dodge2() does. It has no
stat_boxplot() of its own, no geom_jitter() and no cut_width(), maps no lower, middle or
upper, and cannot choose a layer's stat. 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. Most examples use Giggly.Datasets.mpg/0, ggplot2's mpg: fuel economy for 234
cars of 38 popular models from 1999 to 2008; the rest use ggplot2's diamonds,
Giggly.Datasets.diamonds/0. The setup cell binds each to ggplot2's name for it.
A box for each class
p <- ggplot(mpg, aes(class, hwy))
p + geom_boxplot()
p = Plot.new(mpg, %{x: :class, y: :hwy})
p
|> Plot.geom_boxplot()
|> render.()
Horizontal boxes
# Orientation follows the discrete axis
ggplot(mpg, aes(hwy, class)) + geom_boxplot()
Giggly's boxes stand along the discrete axis too, here y.
Plot.new(mpg, %{x: :hwy, y: :class})
|> Plot.geom_boxplot()
|> render.()
Notches
p + geom_boxplot(notch = TRUE)
A notch around each median shows a rough 95% confidence interval for comparing medians. Where a notch reaches past its box's hinges, as two do here, R warns that the notch went outside the hinges; Giggly draws the same notches without a warning.
p
|> Plot.geom_boxplot(notch: true)
|> render.()
Widths that follow the group's size
p + geom_boxplot(varwidth = TRUE)
Each box's width is in proportion to the square root of its group's number of rows.
p
|> Plot.geom_boxplot(varwidth: true)
|> render.()
A fixed fill and colour
p + geom_boxplot(fill = "white", colour = "#3366FF")
p
|> Plot.geom_boxplot(fill: "white", colour: "#3366FF")
|> render.()
Outliers of their own colour
# By default, outlier points match the colour of the box. Use
# outlier.colour to override
p + geom_boxplot(outlier.colour = "red", outlier.shape = 1)
Shape 1 is a hollow circle. Giggly writes ggplot2's dotted option names with underscores.
p
|> Plot.geom_boxplot(outlier_colour: "red", outlier_shape: 1)
|> render.()
Boxes over the data's points
# Remove outliers when overlaying boxplot with original data points
p + geom_boxplot(outlier.shape = NA) + geom_jitter(width = 0.2)
Not in Giggly yet: geom_jitter(). Giggly hides the outliers with outlier_shape: :none, as
outlier.shape = NA does.
Dodged boxes
# Boxplots are automatically dodged when any aesthetic is a factor
p + geom_boxplot(aes(colour = drv))
Mapping colour to drv splits each class into a box for each drive train, which Giggly sets side
by side as position_dodge2() does, its default for boxes too.
p
|> Plot.geom_boxplot(%{colour: :drv})
|> render.()
A continuous x
# You can also use boxplots with continuous x, as long as you supply
# a grouping variable. cut_width is particularly useful
ggplot(diamonds, aes(carat, price)) +
geom_boxplot()
Without a group, the whole of carat makes one box, nine tenths as wide as its range. R warns that
a continuous x wants a group; Giggly draws the same box without a warning.
Plot.new(diamonds, %{x: :carat, y: :price})
|> Plot.geom_boxplot()
|> render.()
ggplot(diamonds, aes(carat, price)) +
geom_boxplot(aes(group = cut_width(carat, 0.25)))
Not in Giggly yet: cut_width(), and a whole-column function in a mapping, such as
cut_width(carat, 0.25).
Transparent outliers
# Adjust the transparency of outliers using outlier.alpha
ggplot(diamonds, aes(carat, price)) +
geom_boxplot(aes(group = cut_width(carat, 0.25)), outlier.alpha = 0.1)
Not in Giggly yet: cut_width(), and a whole-column function in a mapping, such as
cut_width(carat, 0.25). Giggly's outlier_alpha: sets the outliers' transparency.
Numbers computed beforehand
# It's possible to draw a boxplot with your own computations if you
# use stat = "identity":
set.seed(1)
y <- rnorm(100)
df <- data.frame(
x = 1,
y0 = min(y),
y25 = quantile(y, 0.25),
y50 = median(y),
y75 = quantile(y, 0.75),
y100 = max(y)
)
ggplot(df, aes(x)) +
geom_boxplot(
aes(ymin = y0, lower = y25, middle = y50, upper = y75, ymax = y100),
stat = "identity"
)
R draws 100 random numbers and puts their five numbers in a table of one row, which stat = "identity" draws as they are. Not in Giggly yet: mapping lower, middle and upper, and
choosing a layer's stat, here stat = "identity".