using CairoMakie # no need to import Makie itself
Makie
A data visualization ecosystem for Julia
There are several popular data visualization libraries for the Julia programming language (e.g. Plots, Gadfly, VegaLite, Makie). They vary in their precompilation time, time to first plot, layout capabilities, ability to handle 3D data, ease of use, and syntax style. In this landscape, Makie focuses on high performance, fancy layouts, and extensibility.
Makie comes with multiple backends. In this webinar, we will cover:
- GLMakie (ideal for interactive 2D and 3D plotting)
- WGLMakie (an equivalent that runs within browsers)
- CairoMakie (best for high-quality vector graphics)
We will also see how to run Makie in the Alliance clusters.
Slides (Click and wait: this reveal.js presentation is heavy and takes some time to load.)
Plotting in Julia
There are many options to create plots in Julia. Some of the most popular ones are:
- Plots.jl: high-level API for working with different back-ends (GR, Pyplot, Plotly…),
- PyPlot.jl: Julia interface to Matplotlib’s
matplotlib.pyplot
, - PlotlyJS.jl: Julia interface to plotly.js,
- PlotlyLight.jl: the fastest plotting option in Julia by far, but limited features,
- Gadfly.jl: following the grammar of graphics popularized by Hadley Wickham in R,
- VegaLite.jl: grammar of interactive graphics,
- PGFPlotsX.jl: Julia interface to the PGFPlots LaTeX package,
- UnicodePlots.jl: plots in the terminal 🙂,
- Makie.jl: powerful plotting ecosystem: animation, 3D, GPU optimization.
This webinar focuses on Makie.jl.
The Makie ecosystem
Makie consists of a core package (Makie
), with the plots functionalities.
In addition to this, a backend is needed to render plots into images or vector graphics. Three backends are available:
CairoMakie
: vector graphics or high-quality 2D plots. Creates, but does not display plots (you need an IDE that does or you can use ElectronDisplay.jl),GLMakie
: based on OpenGL; 3D rendering and interactivity in GLFW window (no vector graphics),WGLMakie
: web version ofGLMakie
(plots rendered in a browser instead of a window).
Resources
Here are some links and resources useful to get started with the Makie ecosystem:
- the official Makie documentation,
- Julia Data Science book, chapter 5,
- the project Beautiful Makie contains many great plot examples,
- cheatsheets:
for 2D plotting:
for 3D plotting:
Troubleshooting
CairoMakie and WGLMakie should install without issues. Installing GLMakie however can be challenging. This page may lead you towards a solution.
Extensions
A number of extensions have been built on top of Makie:
- GeoMakie.jl add geographical plotting utilities to Makie,
- AlgebraOfGraphics.jl turns plotting into a simple algebra of building blocks,
- GraphMakie.jl to create network graphs.
Fundamental functioning
Figure
Load the package (here, we are using CairoMakie):
Create a Figure
(container object):
= Figure() fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
typeof(fig)
Figure
You can customize a Figure
:
= Figure(backgroundcolor=:grey22, resolution=(300, 300)) fig2
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Makie uses the Colors.jl package as a dependency. You can find a list of all named colours here.
To use CSS specification (e.g. hex), you need to install Colors explicitly and use its color parsing capabilities:
using Colors
= Figure(backgroundcolor=colorant"#adc2eb") fig3
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Axis
Then, you can create an Axis
:
= Axis(Figure()[1, 1]) ax
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Axis with 0 plots:
typeof(ax)
Axis
Axis(fig3[1, 1]) # fig3[1, 1] sets the subplot layout: fig[row, col]
fig3
Axis(fig[2, 3]) # This is what happens if we change the layout
fig
Axis(fig3[2, 3]) # We can add another axis on fig3
fig3
Axis are customizable:
= Figure()
fig4 Axis(fig4[1, 1],
="x label",
xlabel="y label",
ylabel="Title of the plot")
title fig4
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Plot
Finally, you can add a plot:
= Figure()
fig = Axis(fig[1, 1])
ax = LinRange(-10, 10, 20)
x = x
y scatter!(ax, x, y) # Functions with ! transform their arguments
fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Of course, there are many plotting functions, e.g. scatterlines!
:
= Figure()
fig = Axis(fig[1, 1])
ax = LinRange(-10, 10, 20)
x = x
y scatterlines!(ax, x, y) # Functions with ! transform their arguments
fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
We can also use lines!
:
= Figure()
fig = Axis(fig[1, 1])
ax = LinRange(-10, 10, 20)
x = sin.(x) # The . means that the function is broadcast to each element of x
y lines!(ax, x, y)
fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Let’s add points to get a smoother line:
= Figure()
fig = Axis(fig[1, 1])
ax = LinRange(-10, 10, 1000)
x = sin.(x) # The . means that the function is broadcast to each element of x
y lines!(ax, x, y)
fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Now, you don’t have to create the Figure
, Axis
, and plot one at a time. You can create them at the same time with, for instance lines
:
= LinRange(-10, 10, 1000)
x = sin.(x)
y lines(x, y) # Note the use of lines instead of lines!
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Or even more simply:
= LinRange(-10, 10, 1000)
x lines(x, sin)
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
This is a lot simpler, but it is important to understand the concepts of the Figure
and Axis
objects as you will need it to customize them:
= LinRange(-10, 10, 1000)
x = cos.(x)
y lines(x, y;
=(; backgroundcolor=:green),
figure=(; title="Cosinus function", xlabel="x label", ylabel="y label")) axis
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
When you create the Figure
, Axis
, and plot at the same time, you create a FigureAxisPlot
object:
= LinRange(-10, 10, 1000)
x = cos.(x)
y = lines(x, y;
obj =(; backgroundcolor=:green),
figure=(; title="Cosinus function",
axis="x label",
xlabel="y label"));
ylabeltypeof(obj)
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Makie.FigureAxisPlot
Note the ;
in the figure
and axis
value. This is because these are one-element NamedTuples.
The mutating functions (with !
) can be used to add plots to an existing figure, but first, you need to decompose the FigureAxisPlot
object:
= lines(x, sin)
fig, ax, plot lines!(ax, x, cos) # Remember that we are transforming the Axis object
# Now we can plot the transformed Figure fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Or we can add several plots on different Axis
in the same Figure
:
= lines(x, sin)
fig, ax1, plot = Axis(fig[1, 2])
ax2 lines!(ax2, x, cos)
fig
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
Examples
2D
using CairoMakie
using StatsBase, LinearAlgebra
using Interpolations, OnlineStats
using Distributions
activate!(type = "png")
CairoMakie.
function eq_hist(matrix; nbins = 256 * 256)
= fit(Histogram, vec(matrix), nbins = nbins)
h_eq = normalize(h_eq, mode = :density)
h_eq = cumsum(h_eq.weights)
cdf = cdf / cdf[end]
cdf = h_eq.edges[1]
edg = LinearInterpolation(edg, [cdf..., cdf[end]])
interp_linear = reshape(interp_linear(vec(matrix)), size(matrix))
out return out
end
function getcounts!(h, fn; n = 100)
for _ in 1:n
= eigvals(fn())
vals = real.(vals)
x0 = imag.(vals)
y0 fit!(h, zip(x0,y0))
end
end
m(;a=10rand()-5, b=10rand()-5) = [0 0 0 a; -1 -1 1 0; b 0 0 0; -1 -1 -1 -1]
= HeatMap(range(-3.5,3.5,length=1200), range(-3.5,3.5, length=1200))
h getcounts!(h, m; n=2_000_000)
with_theme(theme_black()) do
= Figure(figure_padding=0,resolution=(600,600))
fig = Axis(fig[1,1]; aspect = DataAspect())
ax heatmap!(ax,-3.5..3.5, -3.5..3.5, eq_hist(h.counts); colormap = :bone_1)
hidedecorations!(ax)
hidespines!(ax)
figend
Precompiling OnlineStats
✓ OnlineStatsBase
✓ OnlineStats
2 dependencies successfully precompiled in 3 seconds. 48 already precompiled.
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/QGPt0/src/scenes.jl:220
3D
using GLMakie, Random
activate!()
GLMakie.
Random.seed!(13)
= -6:0.5:6
x = -6:0.5:6
y = 6exp.( -(x.^2 .+ y' .^ 2)./4)
z
= Rect3(Point3f(-0.5), Vec3f(1))
box = 100
n g(x) = x^(1/10)
= [g(x) for x in range(0,1,length=n)]
alphas = resample_cmap(:linear_worb_100_25_c53_n256, n, alpha = alphas)
cmap_alpha
with_theme(theme_dark()) do
= meshscatter(x, y, z;
fig, ax, =box,
marker= 0.5,
markersize = vec(z),
color = cmap_alpha,
colormap = (0,6),
colorrange = (;
axis type = Axis3,
= :data,
aspect = 7.3,
azimuth = 0.189,
elevation = 0.5),
perspectiveness = (;
figure =(1200,800)))
resolution meshscatter!(ax, x .+ 7, y, z./2;
= 0.25,
markersize = vec(z./2),
color = cmap_alpha,
colormap = (0, 6),
colorrange = Vec3f(0.85, 0.85, 0.85),
ambient = 1.5f0)
backlight xlims!(-5.5,10)
ylims!(-5.5,5.5)
hidedecorations!(ax; grid = false)
hidespines!(ax)
figend
For more examples, have a look at Beautiful Makie.
Compiling sysimages
While Makie is extremely powerful, its compilation time and its time to first plot are extremely long. For this reason, it might save you a lot of time to create a sysimage (a file containing information from a Julia session such as loaded packages, global variables, compiled code, etc.) with PackageCompiler.jl.
The upcoming Julia 1.9 will do this automatically.
Using the Alliance clusters
CairoMakie
CairoMakie will run without problem on the Alliance clusters. It is not designed for interactivity, so saving to file is what makes the most sense.
Example:
save("graph.png", fig)
Remember however that CairoMakie is 2D only (for now).
GLMakie
GLMakie relies on GLFW to create windows with OpenGL. GLFW doesn’t support creating contexts without an associated window. The dependency GLFW.jl will thus not install in the clusters—even with X11 forwarding—unless you use VDI nodes, VNC, or Virtual GL.
WGLMakie
You can setup a server with JSServe.jl as per the documentation. However, this method is intended for the creation of interactive widgets, e.g. for a website. While this is really cool, it isn’t optimized for performance. There might also be a way to create an SSH tunnel to your local browser, although there is no documentation on this.
Best probably is to save to file.
Conclusion about the Makie ecosystem on production clusters:
- 2D plots: use CairoMakie and save to file,
- 3D plots: use WGLMakie and save to file.