Transformed coordinates: coord_transform
Mix.install([
{:giggly, path: Path.join(__DIR__, "..")},
{:kino, "~> 0.19"}
])
alias Giggly.Plot
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
coord_transform(), which covers
coord_trans() 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.
coord_transform() transforms the coordinate system, such as putting an axis on a log scale. Unlike
transforming a scale, it happens after the statistics are computed, and it changes the shapes geoms
are drawn in, so a straight line need not stay straight. It transforms continuous positions only.
coord_trans() is its old name, now deprecated. Giggly has neither yet, but transforms a scale, as
Giggly.Plot.scale_x_log10/2 does, and the data, with an expression in the mapping. 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. It binds Giggly.Datasets.diamonds/0,
ggplot2's diamonds, to diamonds, ggplot2's name for it.
Transforming the data
# See ?geom_boxplot for other examples
# Three ways of doing transformation in ggplot:
# * by transforming the data
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_point()
Giggly names the base-10 logarithm as R does, {:log10, [:carat]}, and titles the axes as R
does, log10(carat) and log10(price).
diamonds
|> Plot.new(%{x: {:log10, [:carat]}, y: {:log10, [:price]}})
|> Plot.geom_point()
|> render.()
Transforming the scales
# * by transforming the scales
ggplot(diamonds, aes(carat, price)) +
geom_point() +
scale_x_log10() +
scale_y_log10()
Each axis is placed on a log scale and labelled in the data's own units: 0.3, 1 and 3 carats, $300 to $10,000.
diamonds
|> Plot.new(%{x: :carat, y: :price})
|> Plot.geom_point()
|> Plot.scale_x_log10()
|> Plot.scale_y_log10()
|> render.()
Transforming the coordinate system
# * by transforming the coordinate system:
ggplot(diamonds, aes(carat, price)) +
geom_point() +
coord_transform(x = "log10", y = "log10")
Not in Giggly yet: coord_transform().
A fitted line on transformed scales
R's subset() keeps the diamonds over half a carat.
# The difference between transforming the scales and
# transforming the coordinate system is that scale
# transformation occurs BEFORE statistics, and coordinate
# transformation afterwards. Coordinate transformation also
# changes the shape of geoms:
d <- subset(diamonds, carat > 0.5)
ggplot(d, aes(carat, price)) +
geom_point() +
geom_smooth(method = "lm") +
scale_x_log10() +
scale_y_log10()
The cell keeps the rows subset() keeps, each column's values where carat is over 0.5. The
scales transform the data before the stat, so the line is fitted to the logarithms and is straight
on these axes.
kept = Enum.map(Keyword.fetch!(diamonds, :carat), fn carat -> carat > 0.5 end)
d =
Enum.map(diamonds, fn {name, values} ->
rows =
values
|> Enum.zip(kept)
|> Enum.filter(fn {_value, keep} -> keep end)
|> Enum.map(fn {value, _keep} -> value end)
{name, rows}
end)
d
|> Plot.new(%{x: :carat, y: :price})
|> Plot.geom_point()
|> Plot.geom_smooth(method: :lm)
|> Plot.scale_x_log10()
|> Plot.scale_y_log10()
|> render.()
A fitted line in transformed coordinates
ggplot(d, aes(carat, price)) +
geom_point() +
geom_smooth(method = "lm") +
coord_transform(x = "log10", y = "log10")
# Here I used a subset of diamonds so that the smoothed line didn't
# drop below zero, which obviously causes problems on the log-transformed
# scale
Not in Giggly yet: coord_transform().
Transforming back
# With a combination of scale and coordinate transformation, it's
# possible to do back-transformations:
ggplot(diamonds, aes(carat, price)) +
geom_point() +
geom_smooth(method = "lm") +
scale_x_log10() +
scale_y_log10() +
coord_transform(x = scales::transform_exp(10), y = scales::transform_exp(10))
Not in Giggly yet: coord_transform() with transformations from the scales package.
No transformation
# cf.
ggplot(diamonds, aes(carat, price)) +
geom_point() +
geom_smooth(method = "lm")
diamonds
|> Plot.new(%{x: :carat, y: :price})
|> Plot.geom_point()
|> Plot.geom_smooth(method: :lm)
|> render.()
A discrete axis
R's letters is the 26 lowercase letters, and abs(rnorm(26)) gives each one a random positive
number, so a is continuous and letters discrete. Only the continuous x axis is transformed.
# Also works with discrete scales
set.seed(1)
df <- data.frame(a = abs(rnorm(26)),letters)
plot <- ggplot(df,aes(a,letters)) + geom_point()
plot + coord_transform(x = "log10")
plot + coord_transform(x = "sqrt")
Not in Giggly yet: coord_transform().