Makie.jl icon indicating copy to clipboard operation
Makie.jl copied to clipboard

Can't construct legend from stacked barplot axis

Open grahamas opened this issue 2 years ago • 5 comments

Since a stacked barplot can't be constructed as a sequence of labeled plots, I'm not sure how to construct a legend other than from the axis. However, Legend(fig, stack_ax) cannot find plots with labels, so fails.

using CairoMakie
tbl = (x = [1, 1, 1, 2, 2, 2, 3, 3, 3],
       height = 0.1:0.1:0.9,
       grp = [1, 2, 3, 1, 2, 3, 1, 2, 3],
       grp1 = [1, 2, 2, 1, 1, 2, 1, 1, 2],
       grp2 = [1, 1, 2, 1, 2, 1, 1, 2, 1]
)

fig = Figure()
fig[1,1] = ax = Axis(fig)
barplot!(ax, tbl.x, tbl.height,
        stack = tbl.grp,
        color = tbl.grp,
        axis = (xticks = (1:3, ["left", "middle", "right"]),
                title = "Stacked bars"),
        )
fig[1,2] = Legend(fig, ax)

gives

ERROR: There are no plots with labels in the given axis that can be put in the legend. Supply labels to plotting functions like `plot(args...; label = "My label")`
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:33
 [2] layoutable(::Type{Legend}, fig_or_scene::Figure, axis::Axis, title::String; merge::Bool, unique::Bool, kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables/legend.jl:539
 [3] layoutable(::Type{Legend}, fig_or_scene::Figure, axis::Axis, title::String)
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables/legend.jl:538
 [4] _layoutable(::Type{Legend}, ::Figure, ::Axis, ::Vararg{Any, N} where N; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables.jl:69
 [5] _layoutable(::Type{Legend}, ::Figure, ::Axis, ::Vararg{Any, N} where N)
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables.jl:69
 [6] Legend(::Figure, ::Vararg{Any, N} where N; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables.jl:49
 [7] Legend(::Figure, ::Vararg{Any, N} where N)
   @ Makie.MakieLayout ~/.julia/packages/Makie/xbI6d/src/makielayout/layoutables.jl:49
 [8] top-level scope
   @ REPL[21]:1

grahamas avatar Jul 20 '21 22:07 grahamas

I think we need better support for extracting labels from plots... One hack was this: https://github.com/JuliaPlots/Makie.jl/blob/master/src/basic_recipes/series.jl#L122, but that doesn't work for stacked barplots... We should make it possible to overload the extraction of the labels for more complex scenarios

SimonDanisch avatar Jul 21 '21 09:07 SimonDanisch

Is there a workaround to this issue? I have the same problem for grouped box-plots, and I can't figure out how to get a legend to work.

bgroenks96 avatar Jan 21 '22 09:01 bgroenks96

@bgroenks96 I ended up using AlgebraOfGraphics, which magically worked. plt = data(df) * frequency() * mapping(:x, color=:condition, stack=:condition => condition_sorter). YMMV

grahamas avatar Jan 21 '22 18:01 grahamas

I feel like a good starting point would be making

barplot(1:3, [1,1,2]; group=[1,1,2], color=[1,1,2], label=["1". "1", "2"])
axislegend()
current_figure()

work, right now this throws an error

MethodError: no method matching initialize_block!(::Legend, ::Vector{AbstractPlot}, ::Vector{Vector{String}}, ::Nothing)
Closest candidates are:
  initialize_block!(::Legend, ::Observable{Vector{Tuple{Union{Nothing, AbstractString}, Vector{LegendEntry}}}}) at ~/.julia/packages/Makie/fEZv2/src/makielayout/blocks/legend.jl:1

Stacktrace:

Moelf avatar Jun 09 '22 04:06 Moelf

do we want to "internalize" this logic? https://github.com/JuliaPlots/Makie.jl/blob/ac6b5582fd5c17e3c57ae6d9cfc9a80138345701/docs/examples/plotting_functions/barplot.md?plain=1#L98-L103

so that we can directly pass labels = ...?

Moelf avatar Jun 09 '22 05:06 Moelf