I've tried Gadfly
and enjoyed the grammar of graphics style of plotting, but I've heard lots of love for Makie
as another library worth checking out. I'll use the CairoMakie
backend for now, but it looks like the really fun stuff comes with the other backends like GLMakie in terms of interactivity and even making little interact apps.
BeautifulMakie
site for some great examplesLaTeXStrings
packagelines
)f = Figure(resolution = (600, 400))
Layoutables
like axes will go Layoutables
Layoutables
- what they call stuff that you can add to the Layout. lines
, scatter
, series
, etc. Makie
so verbose is the fact that there are so many possible attributes that you can modify and set to customize the heck out of your chart. Observable
is an object that a Listener
(usually a function) will react to whenever the Observable
changesOverall, the workflow feels like matplotlib
or base plotly
in that you're creating a canvas with your Figure
and then adding components with Plotting Functions
or stuff like Axes
and Legend
s to that Figure
incrementally. Each component has a bunch of attributes that you can fiddle around with and tweak to get the customized visualization that you want.
using CairoMakie
using DataFramesMeta
using AlgebraOfGraphics, PalmerPenguins
ENV["COLUMNS"] = 1000;
x = range(0, 10, length=100)
y = sin.(x)
lines(x, y)
The lines
call can be unpacked into a figure, axis, and pltobject separately if you want to attach attributes:
f, ax, pltobject = lines(x, y)
pltobject.attributes
Mutating plot functions allow you to add to an existing figure:
x = range(0, 10, length=100)
y1 = sin.(x)
y2 = cos.(x)
scatter(x, y1, color = :red, markersize = 5, figure=(; resolution=(600, 400)))
scatter!(x, y2, color = :blue, markersize = 10)
current_figure()
In fact, you can specify pretty much anything within the plot creation function:
lines(1:10, (1:10).^2; color=:black, linewidth=2, linestyle=:dash,
figure=(; figure_padding=5, resolution=(600, 400), font="Arial",
backgroundcolor=:grey90, fontsize=16),
axis=(; xlabel=L"x", ylabel=L"x^{2}", title="Chart Title",
xgridstyle=:dash, ygridstyle=:dash))
current_figure()
Makie
can take a matrix object as well, and there's a series
plotting function that can do multiple lines at once:
dat2 = cumsum(randn(4, 101), dims = 2)
fig, ax, sp = series(dat2, labels=["label $i" for i in 1:4], figure=(; resolution=(640,400)))
axislegend(ax)
current_figure()
We can see that Makie
works just fine for vectors and matrices, but what about DataFrame
s? Well, each column of a dataframe is a vector so you could get by with loops, but we have to write a lot of extra throwaway code which can get annoying fast.
While Makie
is powerful, it does seem a little verbose if you work with tabular data in DataFrames
. It's recommended if you want to build complex or 'publication quality' plots or you want interactivity, but it could use a wrapper to make things faster for one-off plots.
ggplot2
is still the go-to plotting library for R, and using it within the tidyverse feels like a well thought out, integrated system that is both easy to use and easy to layer complexity on top of it.
Gadfly
follows very closely in the ggplot2
Grammar of Graphics style, so it already feels familiar (dare I say pleasing) to use with DataFramesMeta
and comes with plenty of customization right out of the bat.
What makes the GoG-style plotting libraries feel so powerful is that you can add or remove layers of complexity on top of the plot without having to rewrite a lot of code. It's flexible in that the core building blocks work together seamlessly once you learn the grammar.
Enter AlgebraOfGraphics
. AlgebraOfGraphics
uses Makie
in the background so that you still have all of the options with layouts and attributes, but it allows you to write similarly expressive statements that give you some of the benefits of GoG. However, it's just different enough that it takes some getting used to.
We can start with an example and you can see the code in action:
# on the first run, if it asks for stdin type in 'y' and hit enter to download
penguins = dropmissing(DataFrame(PalmerPenguins.load()))
first(penguins, 6)
set_aog_theme!() # just like seaborn's sns.set()
axis = (width = 225, height = 225)
penguin_frequency = data(penguins) * frequency() * mapping(:species)
draw(penguin_frequency; axis)
The line:
penguin_frequency = data(penguins) * frequency() * mapping(:species)
is an example of the Algebra of Layers, where Layers are the key building block or abstraction. Each layer is a product of a combination of elementary objects:
And finally, once you've specified the Layers you use the draw
function to render it:
draw(penguin_frequency) # renders the Layers
Let's revisit our first lines
plot from Makie
using this AoG language:
df = DataFrame(x = range(0, 10, length=100), y = sin.(x))
plt = data(df) * mapping(:x, :y)
draw(plt * visual(Lines); axis)
The multiplication operator (*) is being used to add objects within the same layer, while the addition operator (+) is used to combine more than one Layer
into a list of Layers
.
The draw
function operates on either Layer
or Layers
and assigns it to the specified figure or axis
df = DataFrame(dat, :auto)
x2 = data(df) * mapping(:x1, :x2) * visual(Lines, color="orange")
x3 = data(df) * mapping(:x1, :x3) * visual(Lines, color="green", linewidth=3)
# Use the addition operator to combine our two Layer(s)
draw(x2 + x3; axis)
It's still a little early for AlgebraOfGraphics
but it's nice to see the GoG style on top of the power of Makie
. I'd still prefer to use Makie
itself or Gadfly
for one-off plots - and knowing a lower level library is always useful.