Plots.jl horizontal and vertical labels across subplots in grid layout

using Plots

n_from = 2
n_to = 3

froms = [Plots.fakedata(100) for i = 1:n_from]
tos = [Plots.fakedata(100) for i = 1:n_to]

gl = grid(n_from + 1 , n_to + 1)
p = plot(;layout = gl, aspect_ratio = 1.0)

# setup subplots
for gi in CartesianIndices(axes(gl))
    gi = Tuple(gi)
    _subplot = gl[gi...]
    if gi == (1, 1)
        # bug, clears all subplots
        #plot!(_subplot, legend = false, grid = false, foreground_color_subplot=:white)
        continue
    end
    plot!(_subplot, aspect_ratio = 1.0, framestyle = :origin, link = :square, ticks=nothing,)
end

# plot column header
for i = 1:n_from
    plot!(gl[1 + i, 1], froms[i], label="")
end

# plot row header
for j = 1:n_to
    plot!(gl[1, 1 + j], tos[j], label="")
end

# plot swept volumes
for i = 1:n_from, j = 1:n_to
    d = froms[i] .* tos[j]
    plot!(gl[1 + i, 1 + j], d, label="")
end

display(p)

produces
demo1

and I would like to do something that looks like the following mock-up

Using the idea of julia - Adding global title to Plots.jl subplots - Stack Overflow to include a subplot that serves only to include a title, I tried:

# [...]
gl = grid(n_from + 1 , n_to + 1)

l = @layout [
    c       b{0.1h};
    a{0.1w} gl
]

p = plot(;layout = l, aspect_ratio = 1.0)
# [...]

which gives

ERROR: LoadError: Cannot convert Plots.EmptyLayout to series data for plotting
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:33
  [2] _prepare_series_data(x::Plots.EmptyLayout)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/series.jl:8
  [3] _series_data_vector(x::Plots.EmptyLayout, plotattributes::Dict{Symbol, Any})
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/series.jl:27
  [4] macro expansion
    @ ~/.julia/packages/RecipesPipeline/CirY4/src/series.jl:144 [inlined]
  [5] apply_recipe(plotattributes::AbstractDict{Symbol, Any}, #unused#::Type{RecipesPipeline.SliceIt}, x::Any, y::Any, z::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesBase/92zOw/src/RecipesBase.jl:282
  [6] _process_userrecipes!(plt::Any, plotattributes::Any, args::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/user_recipe.jl:36
  [7] recipe_pipeline!(plt::Any, plotattributes::Any, args::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/RecipesPipeline.jl:70
  [8] _plot!(plt::Plots.Plot, plotattributes::Any, args::Any)
    @ Plots ~/.julia/packages/Plots/kyYZF/src/plot.jl:172
  [9] #plot!#149
    @ ~/.julia/packages/Plots/kyYZF/src/plot.jl:162 [inlined]
 [10] plot!(args::Any; kw::Any)
    @ Plots ~/.julia/packages/Plots/kyYZF/src/plot.jl:153

If I display(p) without trying to populate any of the plots, I get:

image

But that was due to my misunderstanding of the @layout macro, which does not resolve names like gl, but requires instead

# [...]
# gl = grid(n_from + 1 , n_to + 1)

l = @layout [
    c       b{0.1h}
    a{0.1w} grid(n_from + 1 , n_to + 1)
]

gl = l[2, 2]
p = plot(;layout = l, aspect_ratio = 1.0);
# [...]

which gives

image

which is sort-of the layout I need.

I know you asked about Plots, but it’s too enticing for me to try and solve layout problems in Makie… Sorry!

using CairoMakie

f = Figure()

for i in 1:3, j in 1:4
    if (i, j) != (1, 1)
        ax = Axis(f[i+1, j+1])
        hidedecorations!(ax)
        hidespines!(ax)
        hlines!(ax, 0)
        vlines!(ax, 0)
        lines!(ax, randn(200), color = :lightblue)
        ylims!(-5, 10)
    end
end

Label(f[1, 3:5], "Label across columns")
Label(f[3:4, 1], "Label across rows", rotation = pi/2)

f

6 Likes

:astonished:Looks much easier to control than Plots, time to ditch Plots :heart_eyes:

Thanks, @jules! I would like to use Makie, but I’m nervous about not leveraging recipes written for RecipeBase.jl. Do you (or anyone) have any advice to offer about MakieRecipes.jl?

We’re heavily thinking about the recipes aspect, our internal design is just not one to one compatible with the plots recipe system. So one question is, how much can we emulate that system to make use of existing recipes, and the other is can we design a system that is better suited to makie’s capabilities