Points: geom_point
Mix.install([
{:giggly, path: Path.join(__DIR__, "..")},
{:kino, "~> 0.19"}
])
alias Giggly.Plot
mtcars = Giggly.Datasets.mtcars()
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_point(), 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.
Points draw a scatter plot: one point per row of the data, at its x and y. 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; the large dataset is Giggly.Datasets.diamonds/0, ggplot2's diamonds:
53,940 diamonds, bound to diamonds.
A scatter plot
p <- ggplot(mtcars, aes(wt, mpg))
p + geom_point()
p = Plot.new(mtcars, %{x: :wt, y: :mpg})
p
|> Plot.geom_point()
|> render.()
Mapping colour, shape and size
# Add aesthetic mappings
p + geom_point(aes(colour = factor(cyl)))
p + geom_point(aes(shape = factor(cyl)))
# A "bubblechart":
p + geom_point(aes(size = qsec))
Giggly writes factor(cyl) as {:factor, [:cyl]}.
p
|> Plot.geom_point(%{colour: {:factor, [:cyl]}})
|> render.()
p
|> Plot.geom_point(%{shape: {:factor, [:cyl]}})
|> render.()
p
|> Plot.geom_point(%{size: :qsec})
|> render.()
Setting an aesthetic to a fixed value
# Set aesthetics to fixed value
ggplot(mtcars, aes(wt, mpg)) + geom_point(colour = "red", size = 3)
mtcars
|> Plot.new(%{x: :wt, y: :mpg})
|> Plot.geom_point(colour: "red", size: 3)
|> render.()
Transparency for large datasets
# Varying alpha is useful for large datasets
d <- ggplot(diamonds, aes(carat, price))
d + geom_point(alpha = 1/10)
d + geom_point(alpha = 1/20)
d + geom_point(alpha = 1/100)
Each plot draws all 53,940 diamonds, so the fainter the points, the more only the dense regions show.
plot = Plot.new(diamonds, %{x: :carat, y: :price})
plot
|> Plot.geom_point(alpha: 1 / 10)
|> render.()
plot
|> Plot.geom_point(alpha: 1 / 20)
|> render.()
plot
|> Plot.geom_point(alpha: 1 / 100)
|> render.()
A point's fill and border
# For shapes that have a border (like 21), you can colour the inside and
# outside separately. Use the stroke aesthetic to modify the width of the
# border
ggplot(mtcars, aes(wt, mpg)) +
geom_point(shape = 21, colour = "black", fill = "white", size = 5, stroke = 5)
mtcars
|> Plot.new(%{x: :wt, y: :mpg})
|> Plot.geom_point(shape: 21, colour: "black", fill: "white", size: 5, stroke: 5)
|> render.()
A legend that shows the fill
# The default shape in legends is not filled, but you can override the shape
# in the guide to reflect the fill in the legend
ggplot(mtcars, aes(wt, mpg, fill = factor(carb), shape = factor(cyl))) +
geom_point(size = 5, stroke = 1) +
scale_shape_manual(values = 21:25) +
scale_fill_ordinal(guide = guide_legend(override.aes = list(shape = 21)))
Not in Giggly yet: scale_fill_ordinal() and guide_legend()'s override.aes option.
Layering points of different sizes
# You can create interesting shapes by layering multiple points of
# different sizes
p <- ggplot(mtcars, aes(mpg, wt, shape = factor(cyl)))
p +
geom_point(aes(colour = factor(cyl)), size = 4) +
geom_point(colour = "grey90", size = 1.5)
p +
geom_point(colour = "black", size = 4.5) +
geom_point(colour = "pink", size = 4) +
geom_point(aes(shape = factor(cyl)))
R's "grey90" is a name CSS lacks, so the cell writes its hex, "#E5E5E5".
p = Plot.new(mtcars, %{x: :mpg, y: :wt, shape: {:factor, [:cyl]}})
p
|> Plot.geom_point(%{colour: {:factor, [:cyl]}}, size: 4)
|> Plot.geom_point(colour: "#E5E5E5", size: 1.5)
|> render.()
p
|> Plot.geom_point(colour: "black", size: 4.5)
|> Plot.geom_point(colour: "pink", size: 4)
|> Plot.geom_point(%{shape: {:factor, [:cyl]}})
|> render.()
Missing values
ggplot2's example blanks the mpg of about a fifth of the cars at random, with R's
set.seed(1), and draws the rest: a row without a y has no point.
# geom_point warns when missing values have been dropped from the data set
# and not plotted, you can turn this off by setting na.rm = TRUE
set.seed(1)
mtcars2 <- transform(mtcars, mpg = ifelse(runif(32) < 0.2, NA, mpg))
ggplot(mtcars2, aes(wt, mpg)) +
geom_point()
R blanks four cars there; Giggly's version blanks the same four by name. ggplot2 also warns that it removed them. Giggly drops them without a warning, since drawing a plot has no side effects.
blanked = ["Merc 280", "Merc 450SE", "Camaro Z28", "Porsche 914-2"]
mpg =
Enum.zip_with(Keyword.fetch!(mtcars, :model), Keyword.fetch!(mtcars, :mpg), fn model, value ->
if model in blanked, do: nil, else: value
end)
mtcars2 = List.keyreplace(mtcars, :mpg, 0, {:mpg, mpg})
mtcars2
|> Plot.new(%{x: :wt, y: :mpg})
|> Plot.geom_point()
|> render.()
ggplot(mtcars2, aes(wt, mpg)) +
geom_point(na.rm = TRUE)
Not in Giggly yet: the na.rm option. Giggly never warns about a dropped row, so the drawing
above is already what it would draw.