Theming coloured boxplots in Makie

Can someone explain to me how to theme a Makie boxplot such that I can colour boxes by specific conditions or ‘groups’, and also control the colour palette for this via a custom theme?

I can alter colour palette via the theme, and have boxplot colours reflect this:

using CairoMakie

CairoMakie.activate!(type = "pdf")

my_theme = Theme(
    palette = (patchcolor = [:red, :green, :blue, :yellow, :orange, :pink],),
    )

set_theme!(my_theme)

fig = Figure(backgroundcolor=:grey90)

axs = Axis(fig[1, 1], aspect=AxisAspect(1))

x = Vector{Float64}(undef, 40)

for i = 1:40
    x[i] = i % 4
end 

CairoMakie.boxplot!(axs, x, rand(40))

ylims!(axs, 0, 1)

save("figures/testcairo.pdf", fig)

(Plot is all red, indicating it’s responding to my change in colours - the default colour scheme would plot this as blue)

However if I now try to colour the box plots by a condition (for instance, if the x-variable is even or not), the palette reverts to the default Makie colour scheme:

CairoMakie.boxplot!(axs, x, rand(40); color=map(x -> x % 2 == 0, x))

(You can see this is incorrect as I have no purple defined in my custom palette!)

How can I get the plot to respond to my custom colour palette?

Aware of closely related post Coloring boxplot groups in Makie - #7 by piever, however the solution uses AlgebraOfGraphics, for which I can find less documentation on theming

Once you pass a vector of numbers as a color, these are looked up in the colormap by scaling with colorrange. To use a categorical colormap that way, set colormap = my_colors, colorrange = (1, length(my_colors))

1 Like

Brilliant, that (specifically:

my_colors = [:red, :green, :blue, :yellow, :orange, :pink]

my_theme = Theme(colormap = my_colors, colorrange = (1, length(my_colors)))

)

works perfectly. Thanks!

This solution uses the first and last elements in my_colors for plotting. What’s the simplest means of having it use the order of elements in my_colors? i.e. for two conditions, use elements 1:2, for three conditions, use elements 1:3, and so on.

Ah hm you use 0 and 1 for colors, my solution was based on the groups being numbered 1 to k and the colorrange going from 1 to n colors. That should perfectly map to categorical colors, not interpolated ones, as long as k is not larger than n. Can you pass colormap and colorrange directly to the boxplot? I don’t think a colorrange belongs in a theme usually, as it’s too data dependent (unless we force categorical color lookup with it like here)

Doesn’t seem to work.

Passing a pre-defined colormap, :tab10, to boxplot:

fig = Figure(backgroundcolor=:grey90)

axs = Axis(fig[1, 1], aspect=AxisAspect(1))

x = Vector{Float64}(undef, 40)

for i = 1:40
    x[i] = i % 4
end 

CairoMakie.boxplot!(axs, x, rand(40); color=map(x -> x % 2 == 0, x), colormap=:tab10)

ylims!(axs, 0, 1)

Works, but again the first and final colours are plotted.

Passing colorrange directly into boxplot has no effect except to colour each box the first colour in the colourmap:

CairoMakie.boxplot!(axs, x, rand(40); color=map(x -> x % 2 == 0, x), colormap=:tab10, colorrange=(1,2)) # expect blue and orange

Changing the value of colorrange to colorrange=(1,3), colorrange=(2,3) has no effect.

You still create color values 0 and 1 (false and true). Either the colorrange needs to then start at 0 or, what I find more straightforward, you start your color categories at 1. Here I’ve extrapolated the approach to multiple groups. mod1 is a handy function for starting the count at 1

fig = Figure(backgroundcolor=:grey90)

n = 200
x = Vector{Float64}(undef, n)

for i = 1:n
    x[i] = mod1(i, 8)
end

colormap = Makie.to_colormap(:tab10)

for (i, k) in enumerate([2, 4, 6, 8])
    ax = Axis(fig[fldmod1(i, 2)...])
    CairoMakie.boxplot!(ax, x, rand(n);
        color=map(x -> mod1(x, k), x), colormap,
        colorrange = (1, length(colormap)))
    ylims!(ax, 0, 1)
end

fig

Excellent, makes sense (and works) now. Thank you for the help.