Thanks for all of your suggestions! All good options. In the end, I decided to use annotate
. Indeed, it is a major hack and not the prettiest, but it works well for my purposes (I have 9 sets of 6 x 6 grids with no space left for a legend, and I only need to use the title to identify which colour corresponds to each group.)
In case someone needs to do something similar in the future, here is the code that I used (note that I used this stack overflow answer as a starting point):
"""
coloured_title(
p, group_names, colours;
horizontal_shift = -0.25, vertical_shift = 0,
size = 18, title_spacing_constant = 4, title_proportion = 0.02
)
Adds a rudimentary coloured title to `p`. The title is constructed by colouring
each element of `group_names` with the corresponding element of `colours`, and
joining the results. The remaining arguments allow the title to be 'tweaked' as
needed.
"""
function coloured_title(
p, group_names::Vector{String}, colours::Vector{String};
horizontal_shift = -0.25, vertical_shift = 0,
size = 18, title_spacing_constant = 4, title_proportion = 0.02
)
@assert length(group_names) == length(colours)
@assert -1 < horizontal_shift < 1
@assert -1 < vertical_shift < 1
@assert 0 < title_proportion < 1
@assert title_spacing_constant >= 0
# Empty plot that will be used for the title
y = ones(3)
title_plot = scatter(
y,
xlims = (-1, 1),
ylims = (-1, 1),
marker=0, markeralpha=0,axis=false, grid=false, leg=false,
framestyle = nothing, showaxis = false, xticks = false, yticks = false
)
# Add coloured text in the middle of the plot; each subsequent group name
# is shifted by an amount that is proportional to maximum(length.(group_names))
for i ∈ eachindex(group_names)
max_len = maximum(length.(group_names))
current_title = repeat(" ", title_spacing_constant * max_len * (i - 1)) * group_names[i]
annotate!(title_plot, horizontal_shift, vertical_shift, Plots.text(current_title, colours[i], size))
end
# layout for the title plot and your real plot
l = grid(2, 1, heights = [title_proportion, 1 - title_proportion])
# combine the 'title' plot with your real plots
return plot(title_plot, p, layout = l)
end
And here is an example of its use:
# Generate toy data
n = 20 # number of observations per group
x = repeat(1:n, outer = 2)
y = repeat([0, 10], inner = n) .+ randn(2n)
group_names = ["Group 1", "Group 2"]
colours = ["green", "orange"]
c = repeat(colours, inner = n)
# real plot
p = plot(x, y, group = c, colour = c, legend = :none)
coloured_title(p, group_names, colours)