Grouped bar plots, with different number of bars in each group

I have a few data sets:

d1 = [1,3,4,3,2]
d2 = [2]
d3 = [3]
d4 = [5,3]

I want to create a bar plot, with one group for each data set, containing 4/1/1/2 bars respectively. Is this possible? I found groupedbar, but that only seems to work if each group have the same number of bars.

I guess this would be possible by customizing the position and width of each individual of the 9 bars. But I want to make quite a few such plots, typically with more bars, so this would become difficult.

Attempt with Makie :

using CairoMakie

function test_bar()
    d1 = [1,3,4,3,2]
    d2 = [2]
    d3 = [3]
    d4 = [5,3]

    fig = Figure(resolution = (1200, 800),fontsize = 32,colormap = (:Spectral_10, 0.85))  
    
    ax =  Axis(fig[1, 1])     
    ax.ylabel = "y label"
    ax.xlabel = "x label"

    ds=(d1,d2,d3,d4)
    
    shift=1
    gap=2
    xs,ys=Vector{Int}(),Vector{Int}()
    xticks=Vector{String}()
    for (i,di) ∈ enumerate(ds)
        b=shift
        shift+=length(di)
        newxs = collect(b:shift-1)
        xs = vcat(xs,newxs) 
        ys = vcat(ys,di)
        xticks = vcat(xticks,[string(i)*","*string(j) for j ∈ eachindex(di)])
        shift += gap
    end


    barplot!(xs,ys, strokewidth = 2,color=xs)
    ax.xticks = (xs, xticks)
    ax.xticklabelrotation = 45
    CairoMakie.save("toto.png",fig)

    fig
end

test_bar()
2 Likes

Thanks :slight_smile:

1 Like

Here’s an example using Bokeh:

using Bokeh, DataFrames
# to show plots in the browser
Bokeh.settings!(display="browser")
# flatten the data out into a table, with "i" and "j" columns giving the index of each datum
ds = [[1,3,4,3,2],[2],[3],[5,3]];
df = DataFrame([(; i=string(i), j=string(j), ij=(string(i), string(j)), x) for (i,d) in enumerate(ds) for (j,x) in enumerate(d)]);
# construct the plot
fig = figure(x_range=unique(df.ij));
plot!(fig, VBar, x="ij", top="x", color=factor_cmap("i", "Category10_8", unique(df.i)), source=df);
display(fig)

Or using the unreleased and highly experimental new Algebrokeh:

using Bokeh, Algebrokeh, DataFrames
Bokeh.settings!(display="browser", theme="algebrokeh")
# same as before
ds = [[1,3,4,3,2],[2],[3],[5,3]];
df = DataFrame([(; i=string(i), j=string(j), ij=(string(i), string(j)), x) for (i,d) in enumerate(ds) for (j,x) in enumerate(d)]);
# much simpler plotting
plot(df, VBar, x="@ij", y="@x", color="@i")

3 Likes

A simple way, using Plots.jl’s bar():

using Plots; gr()

d1, d2, d3, d4 = [1,3,4,3,2], [2], [3], [5,3]

dlist = [d1, d2, d3, d4]
ni = length.(dlist)
dc = distinguishable_colors(maximum(ni), [RGB(0,0,0),RGB(1,1,1)], dropseed=true)
plot(xlabel="Series", ylabel="Values", widen=false)
k = 1
for (n,d) in zip(ni, dlist)
    bar!(k:k+n-1, d, bar_width=1, c=dc[1:n])
    k += n + 1
end
xticks!(cumsum(ni .+ 1) .- (ni .+ 1)/2, ["d1","d2","d3","d4"])

NB: the colors respect the element position in each vector

2 Likes

Thanks both of you! I usually use Plots, so it is nice to be able to do it there :slight_smile:

A bit late but it took me more work than I thought to make each group of same size regardless the number of bars in them.

using GMT
bar([2 0.5 1 NaN 1; 4 NaN NaN 2 NaN; 1 3 4 3 2; NaN 3 NaN 1 2],
    fillalpha=[0.3 0.5 0.7], xticks=(:d1, :d2, :d3, :d4), show=true)

2 Likes

@joa-quim, fyi, using GMT v0.43.0 and Win11 Julia 1.8.0, the output from your code looks quite different:

1 Like

@rafael.guerra , yes I know. You’ll need the master version to reproduce my plot. Unfortunately GMT is blocked from AutoMerge due to the libstdc++ dependency conflict issue

You will get something more similar if you do

bar(1:4, [2 0.5 1 NaN 1; 4 NaN NaN 2 NaN; 1 3 4 3 2; NaN 3 NaN 1 2],
    fillalpha=[0.3 0.5 0.7], xticks=(:d1, :d2, :d3, :d4), show=true)

(the need to specify the x coordinates when xticks kw is used was also droped in my recent changes).

2 Likes