Define a "plotting package agnostic" algebra of graphics based on a few simple building blocks that can be combined using *
and +
. Highly experimental proof of concept. Requires the branch StatsMakie/pv/aog to work in conjunction with Makie.
using RDatasets: dataset
using StatsMakie: linear
using AbstractPlotting, GLMakie
using AlgebraOfGraphics: Data, Select, Group
mpg = dataset("ggplot2", "mpg");
cols = Data(mpg) * Select(:Displ, :Hwy);
grp = Group(color = :Cyl);
plot(Scatter * cols * Attributes(markersize=10px))
# Now I can simply add `grp` to do the grouping
plot(Scatter * cols * grp * Attributes(markersize=10px))
# This is almost a recipe with scatter and linear regression :)
# It can be applied to the arguments just by multiplying them
plots = Scatter * Attributes(markersize = 10px) + linear * Attributes(linewidth = 5)
plot(plots * cols)
# Again, if I multiply by the grouping, I add it to the scene
plot(plots * cols * grp)
# This is a more complex example, where I want to split the scatter,
# but do the linear regression with all the data
different_grouping = grp * Scatter * Attributes(markersize = 10px) + linear * Attributes(linewidth = 5)
plot(cols * different_grouping)
Under the hood, Group
and Select
are combined into a Traces
object, which contains a list of "traces" to be plotted, indexed by some primary keys, that will be used also to style the plot. The Traces
object can also be passed by hand. It needs to iterate Pair{NamedTuple, Select}
, where the named tuple acts as a "primary key". If there are multiple Traces
objects, all the "consistent options" are kept (i.e. where the shared primary keys match). If the primary keys are "disjoint", all combinations are allowed. counter
simply gives values from 1
to n
to all the attributes that are passed to it as symbols.
julia> using AlgebraOfGraphics: Select, Traces
julia> ts1 = Select(rand(10));
julia> ts2 = Traces(counter(:color), eachcol(rand(10, 3)));
julia> plot(Scatter * ts1 * ts2, markersize = 10px)
julia> ts1 = Traces(counter(:marker), eachcol(rand(10, 3)));
julia> ts2 = Traces(counter(:color), eachcol(rand(10, 3)));
julia> plot(Scatter * ts1 * ts2, markersize = 10px)
The framework does not requires that the objects listed in Traces
are columns. They could be anything that the plotting package can deal with.
julia> ts1 = Traces(counter(:color), [-pi..0, 0..pi]);
julia> ts2 = Traces(counter(:linestyle), [sin, cos]);
julia> plot(ts1 * ts2, linewidth = 10)