Plots.jl: shared colorbar with subplots?

I’d like to be able to create an array of subplots with a shared colorbar, i.e., just one colorbar for all subplots. Is that possible?

using Plots

function doit()
    x1 = range(0.0, 1.0, length=51)
    y1 = range(-1.0, 0.0, length=51)
    z1 = sin.(4*pi.*x1)*cos.(6*pi.*reshape(y1, 1, :))

    x2 = range(0.25, 1.25, length=51)
    y2 = range(-1.0, 0.0, length=51)
    z2 = 2.0.*sin.(4*pi.*x2)*cos.(6*pi.*reshape(y2, 1, :))

    x3 = range(0.0, 1.0, length=51)
    y3 = range(-1.25, -0.25, length=51)
    z3 = 3.0*sin.(4*pi.*x3)*cos.(6*pi.*reshape(y3, 1, :))

    x4 = range(0.25, 1.25, length=51)
    y4 = range(-1.25, -0.25, length=51)
    z4 = 4.0.*sin.(4*pi.*x4)*cos.(6*pi.*reshape(y4, 1, :))

    clims = extrema([z1; z2; z3; z4])
    p1 = contourf(x1, y1, z1, clims=clims, c=:viridis, colorbar=false)
    p2 = contourf(x2, y2, z2, clims=clims, c=:viridis, colorbar=false)
    p3 = contourf(x3, y3, z3, clims=clims, c=:viridis, colorbar=false)
    p4 = contourf(x4, y4, z4, clims=clims, c=:viridis, colorbar=true)

    p_all = plot(p1, p2, p3, p4, layout=(2, 2), link=:all)
    savefig(p_all, "shared_colorbar_julia.png")
end

The resulting figure is this:
shared_colorbar_julia
What I’d like is one long colorbar to the right of the subplots, that extends from the top of the upper row to the bottom of the lower row.

Thanks!

Something like this might work: https://github.com/JuliaPlots/Plots.jl/issues/412#issuecomment-249813635

Excellent, thanks. This is what I have so far:

function doit2()
    x1 = range(0.0, 1.0, length=51)
    y1 = range(-1.0, 0.0, length=51)
    z1 = sin.(4*pi.*x1)*cos.(6*pi.*reshape(y1, 1, :))

    x2 = range(0.25, 1.25, length=51)
    y2 = range(-1.0, 0.0, length=51)
    z2 = 2.0.*sin.(4*pi.*x2)*cos.(6*pi.*reshape(y2, 1, :))

    x3 = range(0.0, 1.0, length=51)
    y3 = range(-1.25, -0.25, length=51)
    z3 = 3.0*sin.(4*pi.*x3)*cos.(6*pi.*reshape(y3, 1, :))

    x4 = range(0.25, 1.25, length=51)
    y4 = range(-1.25, -0.25, length=51)
    z4 = 4.0.*sin.(4*pi.*x4)*cos.(6*pi.*reshape(y4, 1, :))

    clims = extrema([z1; z2; z3; z4])
    p1 = contourf(x1, y1, z1, clims=clims, c=:viridis, colorbar=false)
    p2 = contourf(x2, y2, z2, clims=clims, c=:viridis, colorbar=false)
    p3 = contourf(x3, y3, z3, clims=clims, c=:viridis, colorbar=false)
    p4 = contourf(x4, y4, z4, clims=clims, c=:viridis, colorbar=false)

    h2 = scatter([0,0], [0,1], zcolor=[0,3], clims=clims,
                 xlims=(1,1.1), xshowaxis=false, yshowaxis=false, label="", c=:viridis, colorbar_title="cbar", grid=false)

    @show h2[1][:xaxis][:showaxis]
    @show h2[1][:yaxis][:showaxis]

    l = @layout [grid(2, 2) a{0.01w}]
    p_all = plot(p1, p2, p3, p4, h2, layout=l)
    savefig(p_all, "shared_colorbar_julia2.png")
end

This gives me this plot:
shared_colorbar_julia2

Is there a way to get rid of the x and y axis labels? I have xshowaxis=false and yshowaxis=false, but that doesn’t do the trick.

Thanks again!

framestye = :none should work

1 Like

Great, that seemed to do it. Here’s what I ended up with:

using Plots
function doit2()
    x1 = range(0.0, 1.0, length=51)
    y1 = range(-1.0, 0.0, length=51)
    z1 = sin.(4*pi.*x1)*cos.(6*pi.*reshape(y1, 1, :))

    x2 = range(0.25, 1.25, length=51)
    y2 = range(-1.0, 0.0, length=51)
    z2 = 2.0.*sin.(4*pi.*x2)*cos.(6*pi.*reshape(y2, 1, :))

    x3 = range(0.0, 1.0, length=51)
    y3 = range(-1.25, -0.25, length=51)
    z3 = 3.0*sin.(4*pi.*x3)*cos.(6*pi.*reshape(y3, 1, :))

    x4 = range(0.25, 1.25, length=51)
    y4 = range(-1.25, -0.25, length=51)
    z4 = 4.0.*sin.(4*pi.*x4)*cos.(6*pi.*reshape(y4, 1, :))

    clims = extrema([z1; z2; z3; z4])
    p1 = contourf(x1, y1, z1, clims=clims, c=:viridis, colorbar=false)
    p2 = contourf(x2, y2, z2, clims=clims, c=:viridis, colorbar=false)
    p3 = contourf(x3, y3, z3, clims=clims, c=:viridis, colorbar=false)
    p4 = contourf(x4, y4, z4, clims=clims, c=:viridis, colorbar=false)

    h2 = scatter([0,0], [0,1], zcolor=[0,3], clims=clims,
                 xlims=(1,1.1), label="", c=:viridis, colorbar_title="cbar", framestyle=:none)

    l = @layout [grid(2, 2) a{0.035w}]
    p_all = plot(p1, p2, p3, p4, h2, layout=l, link=:all)
    savefig(p_all, "shared_colorbar_julia2.png")
end

The plot:
shared_colorbar_julia2

Thanks everyone!

3 Likes

Cool that it worked! You should mark it as solved.

1 Like

Here is a hack that lets you change the position and size of the colorbar via an invisible inset subplot, for lost souls like me trying to make this work with the pyplot backend:

x1 = range(0.0, 1.0, length=51)
y1 = range(-1.0, 0.0, length=51)
z1 = sin.(4*pi.*x1)*cos.(6*pi.*reshape(y1, 1, :))

x2 = range(0.25, 1.25, length=51)
y2 = range(-1.0, 0.0, length=51)
z2 = 2.0.*sin.(4*pi.*x2)*cos.(6*pi.*reshape(y2, 1, :))

x3 = range(0.0, 1.0, length=51)
y3 = range(-1.25, -0.25, length=51)
z3 = 3.0*sin.(4*pi.*x3)*cos.(6*pi.*reshape(y3, 1, :))

x4 = range(0.25, 1.25, length=51)
y4 = range(-1.25, -0.25, length=51)
z4 = 4.0.*sin.(4*pi.*x4)*cos.(6*pi.*reshape(y4, 1, :))

clims = extrema([z1; z2; z3; z4])
p1 = contourf(x1, y1, z1, clims=clims, c=:viridis, colorbar=false)
p2 = contourf(x2, y2, z2, clims=clims, c=:viridis, colorbar=false)
p3 = contourf(x3, y3, z3, clims=clims, c=:viridis, colorbar=false)
p4 = contourf(x4, y4, z4, clims=clims, c=:viridis, colorbar=false)

blank = plot(foreground_color_subplot=:white)
l = @layout [grid(2, 2) a{0.075w}]
plot(p1, p2, p3, p4, blank, layout=l, link=:all)
p_all = scatter!([0], [0], zcolor=[NaN], clims=clims, label="", c=:viridis, colorbar_title="cbar", background_color_subplot=:transparent, markerstrokecolor=:transparent, framestyle=:none, inset=bbox(0.1, 0, 0.6, 0.9, :center, :right), subplot=6)

cbar_hack