🏠 back to Observable

stacked & grouped bars and line - Plot

I’m looking to see if i can achieve a binned timeline graph using plot that shows stacked bars alongside grouped bars overlaid with a line chart.

so data a & b would be stacked alongside data c in binned bars (per month or other time slice) then data series d would be a line overlaying them.

I can work out how to created a bar with overlaid line, but then I can’t figure out the rest.

It’s certainly possible to juxtapose several marks on a Plot, if they can share the same scales. You’ll want to help us help you, maybe by sharing a dataset and a work-in-progress notebook, or a drawing of what you have in mind.

1 Like

here is the beginnings of a notebook i’m working on:

You can see the data in there. x is dates, then the ‘y’ values will be the ones that need grouping/ stacking and turning into lines.

I’m also having issues with how to transform the date from the data i have to group by year / month.

Your dates don’t seem to be interpreted correctly. Try

data = (await FileAttachment("test_data.txt").json()).map((d) => Date(d.x))

… from there you probably can figure out how to back the date interpretations back to months and then do some groupings?

Right - so i’m now up to a further point with your help.
now - how do i get the centre points of the months on the scale to be the middle of the bar?
and how would i get that to be the centre of a group of bars?

1 Like

how should your group of bars look like, what exactly are you trying to add?

Your binX transform:

Plot.binX(
        { y: "sum" },
        { x: (d) => d.x, thresholds: d3.utcMonth, y: "y5" }
      )

results in x1 and x2 being the start and the end of each bin and x being the middle of the bar

like this kind of thing:
image

something like this ?

1 Like

sort of, yes.
except, in the image i’ve uploaded, you can see there is a grouping of 4 bars - one for each data item for that month. The x scale month label is in the middle of that group.

What are the 4 bars within a month refering to ?
The standard axis for time data shows the ticks and axis at the start of the year/ month. But there are ways to achieve it if thats important

So they are just 4 measures, “sales of a”, “sales of b”, “enquiries for products”, “cancellations” all pertaining to the month on the x axis.

Client prefers the month name in the middle of the measures so its easier to see which month the group is for

Thanks for the help, this place is one of the nicest communities i’ve ever been a part of.

1 Like

This is a great thread! Thank you. I have essentially zero Plot skills and continue to learn. This thread inspires me to raise a question :slight_smile: More in a second!

Just to make sure, I understand correctly what you aim for:

  • These four measures which form a bar for each month are “sales of a”,…“cancellations”
  • You like to repeate these measures for each month

So the x axis reflects each month subdivided by these 4 categories.
The y axis reflects different measures (sales as well as cancellations.

Is that correct?
By now Plot does not support dual scales (you would have even 3 different measures). So how would you like the y axis to be defined?

Or am I misunderstanding something?

Correct, but the y axis is just summing the amount of sales and cancellations etc…
that have happened that month. These numbers are around the same range, from 0 to about 55.5 million. They will all be against the same scale.

You could do this with ordinal dates (dates grouped by month), or continuous dates (dates binned by month).

Note that by default, a bin covers the whole space from the beginning of the month to the end of the month, and the tick labels are at the start of the month. Its associated scale is continuous. For groups, otoh, the labels are put in the centre of a band scale.

Here’s a notebook that builds on EEDev’s suggestion, and might help to get you started with either option:

2 Likes

Here’s Fil’s suggestion slightly customized to match your screen shot earlier

2 Likes

Another possibility would be to use faceting to create grouped bars. In this approach, you first have to pivot your data from wide to tall, like so:

flatdata = data.flatMap(d => ["y1", "y2", "y3", "y4", "y5", "y6"].map(name => ({date: d.x, name, value: d[name]}))) 

Then you can facet by month along x, and within each facet, group and sum by each name (y1, y2, etc.).

Plot.plot({
  y: {
    transform: d => d / 1e6
  },
  x: {
    axis: null
  },
  fx: {
    tickFormat: "%b-%y"
  },
  facet: {
    data: flatdata,
    x: d => d3.utcMonth(d.date)
  },
  marks: [
    Plot.barY(flatdata, Plot.groupX({y: "sum"}, {x: "name", y: "value", fill: "name"})),
    Plot.ruleY([0])
  ]
})

However, Plot won’t let you draw a continuous line across facets; facets in Plot are ordinal (discrete) rather than quantitative (continuous). That said, you could use additional discrete marks such as bars or rules to draw those other series.

Plot.plot({
  y: {
    transform: d => d / 1e6
  },
  x: {
    axis: null
  },
  fx: {
    tickFormat: "%b-%y"
  },
  facet: {
    data: flatdata,
    x: d => d3.utcMonth(d.date)
  },
  marks: [
    Plot.barY(flatdata, Plot.groupZ({y: "sum"}, {filter: d => d.name === "y5", y: "value"})),
    Plot.barY(flatdata, Plot.groupX({y: "sum"}, {filter: d => ["y1", "y2", "y3", "y4"].includes(d.name), x: "name", y: "value", fill: "name"})),
    Plot.ruleY([0])
  ]
})
2 Likes

All Brilliant suggestions. Thank you.
I’ll try them out with the client tomorrow and see where we get to.

cheers!