# Makie.jl ## Backends Four backends: 1. `CairoMakie` - SVG 2. `GLMakie` - 2D/3D/fast interactivity 3. `WGLMakie` - Same as GLMakie, but in browser 4. `RPRMakie` - experimental raytracing I will use `GLMakie` or `CairoMakie`. To switch use `CairoMakie.activate!()` ## Standard plotting ```julia f = Figure() x = rand(100) y = rand(100) 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](https://docs.makie.org/stable/tutorials/layout-tutorial/) ```julia f = Figure() # we plan to generate two subfigures (with subplots each) - better to generate two "separate" layouts ga = f[1, 1] = GridLayout() gb = f[2, 1] = GridLayout() axtop = Axis(ga[1,1]) axmain = Axis(ga[2, 1], xlabel = "before", ylabel = "after") axright = Axis(ga[2, 2]) labels = ["treatment", "placebo", "control"] d = randn(3, 100, 2) .+ [1, 3, 5] 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 leg = Legend(ga[1, 2], axmain) # absolute size for now :shrug: leg.width =100 leg.height =100 leg.tellwidth = true leg.tellheight = true #---- # second plot ax,h = heatmap(gb[1,1],rand(100,10),colorrange = [0,1]) ax2,h2 = heatmap(gb[1,2],rand(100,10),colorrange = [0,1]) cb = Colorbar(gb[1,3],h) cb.alignmode = Mixed(right=0) #---- # 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 everytime^[it is technically possible to combine Pluto with Observables, but it is a bit buggy] ## Pluto.jl ### Installation / Start ```julia ]add Pluto Pluto.run() ``` ::: {.callout-tip} If you need remote access, run it via `Pluto.run(host="0.0.0.0")` ::: ### Sliders A slider is defined like this: ```julia @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: ```julia using GLMakie x = rand(10_000) obs_ix = Observable(1) # index to plot until scatter(@lift(x[1:obs_ix])) # non-interactive example # <1> f = Figure() obs_sl = GLMakie.Slider(f[2,1],range=1:length(x)) y = @lift(x[1:$(obs_sl.value)]) ax,s = scatter(f[1,1],y) xlims!(ax,0,length(x)) ``` 1. `@lift` does the heavy lifting (hrhr) here. It adds a listener to `obs_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](tasks.qmd#2) # 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](https://github.com/TidierOrg/TidierPlots.jl) Check out the [AoG/GGplot cheatsheet](../../../../cheatsheets/ggplotAOG.qmd): ## AlgebraOfGraphics.jl ::: callout-note Checkout [this awesome AOG tutorial](https://tutorials.pumas.ai/html/PlottingInJulia/01-AoG-intro.html) Really beautifully made! ::: ### Loading data ```julia using GLMakie # backend using AlgebraOfGraphics using PalmerPenguins, DataFrames # example dataset penguins = dropmissing(DataFrame(PalmerPenguins.load())) first(penguins, 6) ``` ::: callout-note A `tidy dataframe` is a dataframe that follows these three rules: 1. Every column is a variable. 2. Every row is an observation. 3. Every cell is a single value. Tidy data make your visualization life much easier as you will see! ::: ### AoG basics `data * mapping * visual` ```julia vis_pen = data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * visual(Scatter) draw(vis_pen) ``` ### Adding color ```julia vis_pencolor = data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color = :species) * visual(Scatter) draw(vis_pencolor) ``` But that is a bit redundant, you can shortcut this, by reusing existing mappings / inputs: ```julia vis_pencolor2 = vis_pen * mapping(color=:species) draw(vis_pencolor2) ``` ### Why `Algebra`OfGraphics? Follows some algebraic rules of multiplying out sums `data * mapping * (visual(Scatter)+visual(Lines))` ```julia data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * (visual(Scatter)+visual(Lines)) |> draw ``` ### Faceting ```julia data(penguins) * mapping(:bill_length_mm, :bill_depth_mm) * mapping(color = :species, col = :sex) |> draw ``` ```julia 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 ```julia data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (linear() + visual(Scatter)) |> draw ``` ```julia data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (smooth() + visual(Scatter)) |> draw ``` ### Advanced ```julia h = data(penguins) * mapping(:bill_length_mm, :bill_depth_mm, color=:species) * (smooth() + visual(Scatter)) |> draw h.grid ax = h.grid[1,1].axis ax + tab -> ax.xticks h ``` ## Task 3 [Click here for the next task](tasks.qmd#3)