Open Datseris opened 2 years ago
@SimonDanisch I'm not making a PR yet because I just don't know where to put this function. But here is the code:
(EDIT: Code updated to be even better!)
using Makie
Text = Union{Symbol, <: AbstractString}
"""
subplotgrid(m, n; kwargs...) -> fig, axs
Create a grid of `m` rows and `n` columns of axes in a new figure and return the figure and
the `Matrix` of axis.
## Keyword arguments
- `sharex/sharey = false`: make every row share the y axis and/or every row column
share the x axis. In this case, tick labels are hidden from the shared axes.
- `titles::Vector{String}`: if given, they are used as titles
for the axes of the top row.
- `xlabels::Vector{String}`: if given, they are used as x labels of the axes
in the bottom row
- `ylabels::Vector{String}`: if given, they are used as y labels of the axes in the
leftmost column
- `title::String`: if given, it is used as super-title for the entire figure
using the `figuretitle!` function.
- `kwargs...`: all further keywords are propagated to `Figure`.
"""
function subplotgrid(m, n;
sharex = false, sharey = false, titles = nothing,
xlabels = nothing, ylabels = nothing, title = nothing, kwargs...
)
fig = Makie.Figure(; kwargs...)
axs = Matrix{Axis}(undef, m, n)
for i in 1:m
for j in 1:n
axs[i,j] = Axis(fig[i,j])
end
end
if sharex
for j in 1:n
Makie.linkxaxes!(axs[:,j]...)
for i in 1:m-1; Makie.hidexdecorations!(axs[i,j]; grid = false); end
end
end
if sharey
for i in 1:m # iterate through rows
Makie.linkyaxes!(axs[i,:])
for j in 2:n; Makie.hideydecorations!(axs[i,j]; grid = false); end
end
end
if !isnothing(titles)
for j in 1:n
axs[1, j].title = titles[j]
end
end
if !isnothing(xlabels)
for j in 1:n
axs[end, j].xlabel = xlabels isa Text ? xlabels : xlabels[j]
end
end
if !isnothing(ylabels)
for i in 1:m
axs[i, 1].ylabel = ylabels isa Text ? ylabels : ylabels[i]
end
end
!isnothing(title) && figuretitle!(fig, title)
return fig, axs
end
"""
figuretitle!(fig, title; kwargs...)
Add a title to a `Figure`, that looks the same as the title of an `Axis`
by using the same default font. `kwargs` are propagated to `Label`.
"""
function figuretitle!(fig, title;
valign = :bottom, padding = (0, 0, 0, 0),
font = "TeX Gyre Heros Bold", # same font as Axis titles
kwargs...,
)
Label(fig[0, :], title;
tellheight = true, tellwidth = false, valign, padding, font, kwargs...
)
return
end
feel free to give a review and/or tell me where to put it.
bump!
I will also make an in-place version that takes an existing figure or GridLayout
as an argument and adds the axis. Just tell me where to actually put it to make a PR :D
Maybe this would be good for a Block
, it could be called FacetGrid for example. That's because it could benefit from spanning labels on the axes and maybe boxes for the titles such as commonly found in ggplot.
I am not aware of what a Block
is (https://makie.juliaplots.org/stable/search/index.html?q=Block ). Is it internal?
It's the new implementation of what Layoutable was, on master. A building block possibly made of other building blocks, that you can place in a grid as one rectangular object.
Yes, I'll make it a block once I have the instructions of how to do so. Is it just a simple wrap of a GridLayout
? In reality, the function I've coded above can just return a GridLayout
.
There's an initial implementation here https://github.com/JuliaPlots/Makie.jl/pull/1827
begin
f = Figure()
fg = MakieLayout.FacetGrid(f[1, 1], 3, 4, sharey = :all,
rowlabels = ["Hi", "Whats", "Up"])
fg.columnlabels = ["a", "b", "c", "d"]
lines!(fg[1, 2], cumsum(randn(50)))
lines!(fg[3, 4], cumsum(randn(50)))
Label(f[0, :], "A FacetGrid demonstration", tellwidth = false)
f
end
I'm not sure yet if I like making an object out of this or if it should just make a bunch of axes wherever and return those. The options to style this are potentially infinite, and it's not going to be practical to implement them all with overlapping keywords etc. For example position of label boxes, if it's a full grid or if there are positions missing, etc. However, using a Block does make the thing somewhat nicely self contained.
I really hope the Makie developers strongly consider adding the above subplotgrid
function to the API and exporting it, because it has become rather tedius to copy paste it in 20 different science projects and then include the file with this definition in every one scripts in these projects. Given the prevalence of "facet/grid plots" in science, this function is likely to be widespread used if available.
Yes, definitely. I need a bit more time to think about how exactly to integrate it, as this function is only one example of a multi-axis (or object) plotting recipe, for which we need better infrastructure anyway. So I don't want to include this as is only to come up with a unified way a bit later, leading to more breaking changes.
I've updated my original post with an even better and more general version of the code, that I Am particularly prooud about! I use this code in literally every science project I have!
Yeah I agree, let me try to prepare a PR soon, I have some small ideas about the API that I'd want to try. But it would be very useful to have
Would be nice to have something equivalent with PyPlot's
subplots
function:such a function would make an XxY grid layout, and optionaly share their x and/or y axis (also hiding ticks if axis are shared).
(I'll do this, assign me on GitHub please)