How can I add categorical labels in a grouped bar plot?

Hi all!

I’m trying to produce a grouped bar plot, as shown here:

julia> results
12-element Vector{NamedTuple{(:program, :mean, :stddev, :score), Tuple{String, Float64, Float64, Float64}}}:
 (program = "fact_ite_#1", mean = 0.13363337671967476, stddev = 6.968981589411716e-5, score = 0.15668496559022413)
 (program = "fact_ite_#10", mean = 0.10939051690866114, stddev = 6.729812577509443e-5, score = 0.16760302229708618)
 (program = "fact_ite_#5", mean = 0.11185326308790666, stddev = 7.011063152232e-5, score = 0.16540776415667527)
 (program = "fact_rec_#1", mean = 0.17970985593298908, stddev = 0.00010983213977160326, score = 0.10724581779034512)
 (program = "fact_rec_#10", mean = 0.1297923567969918, stddev = 7.348603751738248e-5, score = 0.18260521370980007)
 (program = "fact_rec_#5", mean = 0.129506049575937, stddev = 7.106046262476235e-5, score = 0.17652392213987142)
 (program = "fib_ite_#1", mean = 0.14015194727028404, stddev = 7.504999155641526e-5, score = 0.14984037903988084)
 (program = "fib_ite_#10", mean = 0.10199646836797639, stddev = 6.671603793583994e-5, score = 0.17997349855439737)
 (program = "fib_ite_#5", mean = 0.10658693989996804, stddev = 6.676934103832938e-5, score = 0.17310482322253823)
 (program = "fib_rec_#1", mean = 0.17802851040478251, stddev = 0.00010872560469872835, score = 0.10724581779034512)
 (program = "fib_rec_#10", mean = 0.1211624343205089, stddev = 6.821730335085582e-5, score = 0.17307553078197996)
 (program = "fib_rec_#5", mean = 0.12221186281160823, stddev = 6.598186810689261e-5, score = 0.16704219125922398)

julia> groupedbar([[t.mean for t in results] [t.score for t in results]])

giving
plot

I’d like to add labels to both the legend (currently y1 and y2) and the x axis ticks (now numerical, but I’d like to put there [t.program for t in results]. I have seen this SO answer by Bogumił, but it’s error prone and I suspect a better way has been implemented in the last 3 years.

Thanks!

Update: for now I adapted Bogumił’s solution

groupedbar([[t.program, t.program] for t in results] |> flatten,
    Iterators.flatten(zip([t.mean for t in results], [t.score for t in results])) |> collect,
    group = repeat(["measured", "score"], length(results)),
    xrotation = 90)

which gives
plot
but again: isn’t there anything friendlier, and less error prone?

Another possibility (inspired and adapted from this example) is to pass the keyword argument xticks, that may be a Tuple (tickvalues, ticklabels). Passing the ticklabels works, but you also need to pass the number of ticks in tickvalues, such as

p = groupedbar([[t.mean for t in results] [t.score for t in results]],
    xticks = (1:length([t.program for t in results]), [t.program for t in results]),
    rotation = 90)

As a side note, the documentation may benefit from an update, because right now it only mentions that the attribute may be a tuple, but it otherwise never specifies what ticklabels does (although it’s perceivable).

Ok I think I got it. You can leverage the usual pattern plot(x, y), that is, in this case,

p = groupedbar([t.program for t in results],
    [[t.mean for t in results] [t.score for t in results]],
    label = ["means" "scores"],
    xrotation = 90)

This works as intended, is a bit more secure (you just need to keep series and labels in the same order), and I think it’s the simpler idiom.