Can't get side-by-side bar graph with multiple series

Even in version 7 with time based multiple series I can stack the bars, but not place them side-by-side as in most other graphing environments.
Is there a workaround to have this very common kind of graph?

You are correct, this is a bug / missing feature in Grafana:

https://community.grafana.com/t/three-histograms-on-one-chart-
grafana-6-5-0/23357

Regards,

Antony.

Hi Antony,
It’s incredible that even in the latest version 7 they still lack this feature. Btw Chronograf can do side-by-side bars. I found a Grafana plugin that adds multibar charts, but I’m unable to install it on Raspberry Pi 4. Do you have experienice in installing manually Grafana plugins?
Bye
Roberto

You can add a suitable time off set to your series to get the same effect.

So it gets a bit more complicated than just adding a time offset. Although my first attempt (above) worked well, it did not consistently render with the same column width. The column width is calculated dynamically, see @torkel 's comment here: Chart with custom bar width for each time intervall

So in order to avoid overlapping columns you should not only offset the series’ timestamps, you also should add ‘dummy’ zeros in each series. In my flux query, the data table ends up looking like this:

and the graph then like this:

image

Note:

  1. I have added a 3rd timestamp with zero for both series, it gives a bettter ‘grouped’ feeling.
  2. I also ended doing a time shift to align the group nicely above the X-axis label (see myOffset - the value probably changes if you are in a different timezone).
  3. In flux, you can do this by creating 3 different tables with conditional mapping and then applying a union() function. Maybe someone knows a more efficient query, but it worked for me.

import “experimental”
import “date”

myOffset = -14h
myStart = date.truncate(t: experimental.addDuration(d:-24h, to: v.timeRangeStart), unit: 1d)
myStop = date.truncate(t: experimental.addDuration(d: 0h, to: v.timeRangeStop), unit: 1d)

Table1 = from(bucket: “R757”)
|> range(start: myStart, stop: myStop)
|> filter(fn: ® => r["_measurement"] == “Counters” or r["_measurement"] == “MMD”)
|> filter(fn: ® => r[“name”] == “OpHour” or r[“name”] == “CountEnergAct”)
|> aggregateWindow(every: 1d, fn: last)
|> difference()
|> map(fn: ® => ({
_time: if (r.name == “OpHour”) then experimental.addDuration(d: 8h, to: r._time) else r._time,
_value: r._value,
_field: if (r.name == “OpHour”) then “Operating Hours” else “Active Energy”}))

Table2 = from(bucket: “R757”)
|> range(start: myStart, stop: myStop)
|> filter(fn: ® => r["_measurement"] == “Counters” or r["_measurement"] == “MMD”)
|> filter(fn: ® => r[“name”] == “OpHour” or r[“name”] == “CountEnergAct”)
|> aggregateWindow(every: 1d, fn: last)
|> difference()
|> map(fn: ® => ({
_time: if (r.name == “OpHour”) then r._time else experimental.addDuration(d: 8h, to: r._time),
_value: 0.0,
_field: if (r.name == “OpHour”) then “Operating Hours” else “Active Energy”}))

Dummies = from(bucket: “R757”)
|> range(start: myStart, stop: myStop)
|> filter(fn: ® => r["_measurement"] == “Counters” or r["_measurement"] == “MMD”)
|> filter(fn: ® => r[“name”] == “OpHour” or r[“name”] == “CountEnergAct”)
|> aggregateWindow(every: 1d, fn: last)
|> map(fn: ® => ({
_time: experimental.addDuration(d: 16h, to: r._time),
_value: 0.0,
_field: if (r.name == “OpHour”) then “Operating Hours” else “Active Energy”}))

UnionTable= union(
tables: [Table1, Table2, Dummies])
|> sort(columns: ["_time"])
|> map(fn: ® => ({_time: experimental.addDuration(d: myOffset, to: r._time), _field: r._field, _value: r._value}))
|> yield()