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://localhost:3000/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
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: https://localhost:3000/t/chart-with-custom-bar-width-for-each-time-intervall/11269
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:
Note:
- I have added a 3rd timestamp with zero for both series, it gives a bettter âgroupedâ feeling.
- 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).
- 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) => r[â_measurementâ] == âCountersâ or r[â_measurementâ] == âMMDâ)
|> filter(fn: (r) => r[ânameâ] == âOpHourâ or r[ânameâ] == âCountEnergActâ)
|> aggregateWindow(every: 1d, fn: last)
|> difference()
|> map(fn: (r) => ({
_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) => r[â_measurementâ] == âCountersâ or r[â_measurementâ] == âMMDâ)
|> filter(fn: (r) => r[ânameâ] == âOpHourâ or r[ânameâ] == âCountEnergActâ)
|> aggregateWindow(every: 1d, fn: last)
|> difference()
|> map(fn: (r) => ({
_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) => r[â_measurementâ] == âCountersâ or r[â_measurementâ] == âMMDâ)
|> filter(fn: (r) => r[ânameâ] == âOpHourâ or r[ânameâ] == âCountEnergActâ)
|> aggregateWindow(every: 1d, fn: last)
|> map(fn: (r) => ({
_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: (r) => ({_time: experimental.addDuration(d: myOffset, to: r._time), _field: r._field, _value: r._value}))
|> yield()
Hi there,
If you are using Grafana 8, I found this answer by @ewelch which helped me a lot - seems like an easier way to do it.