Choosing an appropriate set of colors or colorbar for your plot is an essential part when presenting results. Using Colors.jl is supported in Makie.jl
so that you can use named colors or pass RGB
or RGBA
values. Additionally, colormaps from ColorSchemes.jl and PerceptualColourMaps.jl can also be used. It is worth knowing that you can reverse a colormap by doing Reverse(:colormap_name)
and obtain a transparent color or colormap with color=(:red,0.5)
and colormap=(:viridis, 0.5)
.
Different use cases will be shown next. Then we will define a custom theme with new colors and a colorbar palette.
By default Makie.jl
has a predefined set of colors in order to cycle through them automatically, as shown in the previous figures, where no specific color was set. Overwriting these defaults is done by calling the keyword color
in the plotting function and specifying a new color via a Symbol
or String
. See this in the following example:
function set_colors_and_cycle()
# Epicycloid lines
x(r, k, θ) = r * (k .+ 1.0) .* cos.(θ) .- r * cos.((k .+ 1.0) .* θ)
y(r, k, θ) = r * (k .+ 1.0) .* sin.(θ) .- r * sin.((k .+ 1.0) .* θ)
θ = range(0, 6.2π, 1000)
axis = (; xlabel=L"x(\theta)", ylabel=L"y(\theta)",
title="Epicycloid", aspect=DataAspect())
figure = (; size=(600, 400), font="CMU Serif")
fig, ax, _ = lines(x(1, 1, θ), y(1, 1, θ); color="firebrick1", # string
label=L"1.0", axis=axis, figure=figure)
lines!(ax, x(4, 2, θ), y(4, 2, θ); color=:royalblue1, #symbol
label=L"2.0")
for k = 2.5:0.5:5.5
lines!(ax, x(2k, k, θ), y(2k, k, θ); label=latexstring("$(k)")) #cycle
end
Legend(fig[1, 2], ax, latexstring("k, r = 2k"), merge=true)
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig
end
JDS.set_colors_and_cycle()
Where, in the first two lines we have used the keyword color
to specify our color. The rest is using the default cycle set of colors. Later, we will learn how to do a custom cycle.
Regarding colormaps, we are already familiar with the keyword colormap
for heatmaps and scatters. Here, we show that a colormap can also be specified via a Symbol
or a String
, similar to colors. Or, even a vector of RGB
colors. Let’s do our first example by calling colormaps as a Symbol
, String
and cgrad
for categorical values. See ?cgrad
for more information.
figure = (; size=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange=(0, 1),
colormap=Reverse(:viridis), axis=axis, figure=figure)
Colorbar(fig[1, 2], pltobj, label = "Reverse sequential colormap")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig
When setting a colorrange
usually the values outside this range are colored with the first and last color from the colormap. However, sometimes is better to specify the color you want at both ends. We do that with highclip
and lowclip
:
using ColorSchemes
figure = (; size=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj = heatmap(randn(20, 20); colorrange=(-2, 2),
colormap="diverging_rainbow_bgymr_45_85_c67_n256",
highclip=:black, lowclip=:white, axis=axis, figure=figure)
Colorbar(fig[1, 2], pltobj, label = "Diverging colormap")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig
But we mentioned that also RGB
vectors are valid options.
For our next example you could pass the custom colormap perse or use cgrad
to force a categorical Colorbar
.
using Colors, ColorSchemes
figure = (; size=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
#cmap = ColorScheme(range(colorant"red", colorant"green", length=3))
# this is another way to obtain a colormap, not used here, but try it.
mycmap = ColorScheme([RGB{Float64}(i, 1.5i, 2i) for i in [0.0, 0.25, 0.35, 0.5]])
fig, ax, pltobj = heatmap(rand(-1:1, 20, 20);
colormap=cgrad(mycmap, 3, categorical=true, rev=true), # cgrad and Symbol, mycmap
axis=axis, figure=figure)
cbar = Colorbar(fig[1, 2], pltobj, label="Categories")
cbar.ticks = ([-0.66, 0, 0.66], ["negative", "neutral", "positive"])
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig
Lastly, the ticks in the colorbar for the categorial case are not centered by default in each color. This is fixed by passing custom ticks, as in cbar.ticks = (positions, ticks)
.
The last case is when passing multiple colors to colormap
. You will get an interpolated colormap between those colors. Also, hexadecimal coded colors are accepted. So, on top of our heatmap let’s put one semi-transparent point using this.
figure = (; size=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange=(0, 1),
colormap=["red", "black"], axis=axis, figure=figure)
scatter!(ax, [11], [11]; color=("#C0C0C0", 0.5), markersize=150)
Colorbar(fig[1, 2], pltobj, label="2 colors")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig
Here, we could define a global Theme
with a new cycle for colors, however that is not the recommend way to do it. It’s better to define a new theme and use as shown before. Let’s define a new one with a cycle
for :color
, :linestyle
, :marker
and a new colormap
default. And add these new attributes to our previous publication_theme
.
function new_cycle_theme()
# https://nanx.me/ggsci/reference/pal_locuszoom.html
my_colors = ["#D43F3AFF", "#EEA236FF", "#5CB85CFF", "#46B8DAFF",
"#357EBDFF", "#9632B8FF", "#B8B8B8FF"]
cycle = Cycle([:color, :linestyle, :marker], covary=true) # altogether
my_markers = [:circle, :rect, :utriangle, :dtriangle, :diamond,
:pentagon, :cross, :xcross]
my_linestyle = [:solid, :dash, :dot, :dashdot, :dashdotdot]
Theme(
fontsize=16, font="CMU Serif",
colormap=:linear_bmy_10_95_c78_n256,
palette=(color=my_colors, marker=my_markers, linestyle=my_linestyle),
Lines=(cycle=cycle,),
Scatter=(cycle=cycle,),
Axis=(xlabelsize=20,xlabelpadding=-5,
xgridstyle=:dash, ygridstyle=:dash,
xtickalign=1, ytickalign=1,
yticksize=10, xticksize=10,
xlabel="x", ylabel="y"),
Legend=(framecolor=(:black, 0.5), backgroundcolor=(:white, 0.5)),
Colorbar=(ticksize=16, tickalign=1, spinewidth=0.5),
)
end
And apply it to a plotting function like the following:
function scatters_and_lines()
x = collect(0:10)
xh = range(4, 6, 25)
yh = range(70, 95, 25)
h = randn(25, 25)
fig = Figure(size=(600, 400), font="CMU Serif")
ax = Axis(fig[1, 1]; xlabel=L"x", ylabel=L"f(x,a)")
for i in x
lines!(ax, x, i .* x; label=latexstring("$(i) x"))
scatter!(ax, x, i .* x; markersize=13, strokewidth=0.25,
label=latexstring("$(i) x"))
end
hm = heatmap!(xh, yh, h)
axislegend(L"f(x)"; merge=true, position=:lt, nbanks=2, labelsize=14)
Colorbar(fig[1, 2], hm, label="new default colormap")
limits!(ax, -0.5, 10.5, -5, 105)
colgap!(fig.layout, 5)
fig
end
with_theme(scatters_and_lines, new_cycle_theme())
At this point you should be able to have complete control over your colors, line styles, markers and colormaps for your plots. Next, we will dive into how to manage and control layouts.