Subplots in Plots.jl with separate legend

I am plotting 20+ timeseries with various scales from a DataFrame using Plots.jl, separated into subplots because the original plot is too crowded. Everything works nicely, but I would like a separate legend (with only the plotted series) for each subplot. Self-contained toy example:

using DataFrames
using Plots
using StatPlots

data = let years=[], spelltypes=[], values=[]
    for year in 1980:2000
        for spelltype in 1:30
            push!(values, spelltype+(rand()-0.5)/10)
            push!(years, year)
            push!(spelltypes, spelltype)
        end
    end
    DataFrame(year=years, spelltype=spelltypes, value=values)
end

pick(data, range) = data[[spelltype ∈ range for spelltype in data[:spelltype]],:]
subplot(data, range) = plot(pick(data, range), :year, :value, group=:spelltype)

p1 = subplot(data, 1:10)
p2 = subplot(data, 11:20)
p3 = subplot(data, 21:30)
plot(p1, p2, p3)         # would like separate legend for each subplot
2 Likes

Funny, I get separate legends in each subplot when trying that code

Yeah separate legends is easy and automatic. Did you mean you didn’t want separate legends and just wanted a master legend? That’s difficult and I think I have an issue open for that.

@mkborregaard, @ChrisRackauckas: now I have tried various backends. I get separate legends in gr(), but a single legend in plotly() and plotlyjs(), and an error for pyplot():

julia> pyplot()
INFO: Recompiling stale cache file /home/tamas/.julia/lib/v0.5/PyPlot.ji for module
 PyPlot.                                                                          
Plots.PyPlotBackend()

julia> plot(p1, p2, p3)         # would like separate legend for each subplot
Error showing value of type Plots.Plot{Plots.PyPlotBackend}:
ERROR: MethodError: no method matching py_init_subplot(::Plots.Plot{Plots.PyPlotBac
kend}, ::Plots.Subplot{Plots.PlotlyBackend})                                      
Closest candidates are:
  py_init_subplot(::Plots.Plot{Plots.PyPlotBackend}, ::Plots.Subplot{Plots.PyPlotBa
ckend}) at /home/tamas/.julia/v0.5/Plots/src/backends/pyplot.jl:393               

Using released versions of all packages, and 0.5.0.

Is the above not the syntax I should be using?

AFAICS there is nothing wrong with your syntax. It is strange. I am on Plots master and PyPlot release.

I tried master for Plots, PlotlyJS and Plotly, and I get the combined legend. With master PyPlot, I do get the separate legends.

I think the best course of action is to open an issue on Plots

https://github.com/JuliaPlots/Plots.jl/issues/673

Apologies for reopening an older post, but I have a panel of plots (using Plots.jl) and specifying legend=true at the end while specifying the layout makes it very messy and shows giant legends that cover up most of the subplots. I’d like to specify a master legend i.e. one legend for all subplots, how would I do that?
Thank you!

You can always hack it together with layout and an extra subplot.

plot(
    plot(rand(100,3), legend = false), 
    plot((1:3)', legend = true, framestyle = :none)
    )
2 Likes

So the issue here is that I have originally 15 subplots, and doing “layout=(5,3)” fits the graphs well, however if I add the master legend bit as an extra subplot (I had tried doing this earlier myself), it shows an error saying “When doing layout, n(15) != n_override(16). You’re probably trying to force existing plots into a layout that doesn’t fit them.” I’ve tried playing around with the layout (i.e. I’ve tried tuples of (8,2) to fit the legend subplot but this layout highly distorts the graphs, or layout=(6,3) to accommodate for the extra graph), but it keeps showing the error message, which means that Plots.jl requires exactly the same correct number of subplots in total as specified in the layout. Is there a workaround?

You can use a 4x4 layout. Leave the 16th empty, then plot to it with plot!(...somearguments..., subplot = 16, framestyle = :none)

1 Like

this is perfect, thank you!