Makie.jl
Backends
Four backends:
CairoMakie
- SVGGLMakie
- 2D/3D/fast interactivityWGLMakie
- Same as GLMakie, but in browserRPRMakie
- experimental raytracing
I will use GLMakie
or CairoMakie
. To switch use CairoMakie.activate!()
Standard plotting
= Figure()
f = rand(100)
x = rand(100)
y
scatter(f[1,1],x,y)
lines(f[1,2],x,y)
hist(f[2,1],x)
density!(f[2,1],x) # inplace -> add to current plot
stem(f[2,2],x)
Layouts for scientific figures
Makie has the best layouting tool I have ever used. full tutorial here
= Figure()
f
# we plan to generate two subfigures (with subplots each) - better to generate two "separate" layouts
= f[1, 1] = GridLayout()
ga = f[2, 1] = GridLayout()
gb
= Axis(ga[1,1])
axtop = Axis(ga[2, 1], xlabel = "before", ylabel = "after")
axmain = Axis(ga[2, 2])
axright
= ["treatment", "placebo", "control"]
labels = randn(3, 100, 2) .+ [1, 3, 5]
d
for (label, col) in zip(labels, eachslice(d, dims = 1))
scatter!(axmain, col, label = label)
density!(axtop, col[:, 1])
density!(axright, col[:, 2], direction = :y)
end
linkyaxes!(axmain, axright)
linkxaxes!(axmain, axtop)
hidedecorations!(axtop, grid = false)
hidedecorations!(axright, grid = false)
#--- add a legend
= Legend(ga[1, 2], axmain)
leg
# absolute size for now :shrug:
=100
leg.width =100
leg.height
= true
leg.tellwidth = true
leg.tellheight
#----
# second plot
= heatmap(gb[1,1],rand(100,10),colorrange = [0,1])
ax,h = heatmap(gb[1,2],rand(100,10),colorrange = [0,1])
ax2,h2 = Colorbar(gb[1,3],h)
cb = Mixed(right=0)
cb.alignmode
#----
# Labels
Label(ga[1, 1, TopLeft()], "A1", font = :bold, padding = (0, 0, 5, 0))
Label(ga[2, 1, TopLeft()], "A2", font = :bold, padding = (0, 0, 5, 0))
Label(ga[2, 2, TopLeft()], "A3", font = :bold, padding = (0, 0, 5, 0))
Label(gb[1, 1, TopLeft()], "B", font = :bold, padding = (0, 0, 5, 0))
#---
# top plot needs more space
rowsize!(f.layout,2,Relative(0.3))
#---
f
Interactivity
With Makie.jl, two ways of interactivity:
Observables - very general way, a little bit more verbose
Pluto.jl Sliders - very simple, need to redraw plot everytime1
Pluto.jl
Installation / Start
]add Plutorun() Pluto.
If you need remote access, run it via Pluto.run(host="0.0.0.0")
Sliders
A slider is defined like this:
@bind yourVarName PlutoUI.Slider(from:to) # from:step:to is optional, step by def 1
if you move the slider, yourVarName
+ all cells that depend on that variable are automatically recalculated. Quick & dirty way to generate an interactive plot
Bonus: Makie Interactivity
There is another way to get to interactivity. Using Observables.jl
To provide a simple example of the logic:
using GLMakie
= rand(10_000)
x = Observable(1) # index to plot until
obs_ix scatter(@lift(x[1:obs_ix])) # non-interactive example
= Figure()
f = GLMakie.Slider(f[2,1],range=1:length(x))
obs_sl = @lift(x[1:$(obs_sl.value)])
y = scatter(f[1,1],y)
ax,s xlims!(ax,0,length(x))
- 1
-
@lift
does the heavy lifting (hrhr) here. It adds a listener toobs_ix
, whenever that value is changed, the value of the output of@lift
is changed as well ## Task 2: Interactivity Click here for the next task
Grammar of Graphics
The grammar of graphics is a convenient way to build common explorative plots.
For example:
For ggplot enthusiasts
You could use TidierPlots.jl - a ggplot clone
Check out the AoG/GGplot cheatsheet:
AlgebraOfGraphics.jl
Checkout this awesome AOG tutorial Really beautifully made!
Loading data
using GLMakie # backend
using AlgebraOfGraphics
using PalmerPenguins, DataFrames # example dataset
= dropmissing(DataFrame(PalmerPenguins.load()))
penguins first(penguins, 6)
A tidy dataframe
is a dataframe that follows these three rules:
- Every column is a variable.
- Every row is an observation.
- Every cell is a single value.
Tidy data make your visualization life much easier as you will see!
AoG basics
data * mapping * visual
= data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * visual(Scatter)
vis_pen draw(vis_pen)
Adding color
= data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color = :species) * visual(Scatter)
vis_pencolor draw(vis_pencolor)
But that is a bit redundant, you can shortcut this, by reusing existing mappings / inputs:
= vis_pen * mapping(color=:species)
vis_pencolor2 draw(vis_pencolor2)
Why Algebra
OfGraphics?
Follows some algebraic rules of multiplying out sums
data * mapping * (visual(Scatter)+visual(Lines))
data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * (visual(Scatter)+visual(Lines)) |> draw
Faceting
data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * mapping(color = :species, col = :sex) |> draw
data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * mapping(color = :species, col = :sex,row=:body_mass_g => x-> x>3500) |> draw
Linear & Non-linear summaries
data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (linear() + visual(Scatter)) |> draw
data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (smooth() + visual(Scatter)) |> draw
Advanced
= data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (smooth() + visual(Scatter)) |> draw
h
h.grid= h.grid[1,1].axis
ax + tab -> ax.xticks
ax h
Task 3
Footnotes
it is technically possible to combine Pluto with Observables, but it is a bit buggyâŠī¸