Matt
Matt

Reputation: 1007

Stacked Bar Chart by Units & Percentage

Summary

I want to display a bar chart who's dimension is days and who's stacked categories is another (i.e. x-axis = days and stack = category-1), but then, rather than display the simple quantity-sum of each group/stack, I want to show the percentage of the day.

JSFiddle https://jsfiddle.net/wostoj/L1q50d4g/

Details

I have data with dates, quantities, and other classifiers. For the purpose of this question I can simplify it to this:

data = [
 {day: 1, cat: 'a', quantity: 25},
 {day: 1, cat: 'b', quantity: 15},
 {day: 1, cat: 'b', quantity: 10},
 {day: 2, cat: 'a', quantity: 90},
 {day: 2, cat: 'a', quantity: 45},
 {day: 2, cat: 'b', quantity: 15},
]

I can set up a bar chart, by day, that shows total units (quantity), stacked by cat (see JSFiddle). But what I'd like to do is set that same graph so that it shows the quantity on each day, stacked by cat, as a percent of total quantity that day. So the output would look, graphically, like the following.

[
 {day: 1, cat: 'a', percent: 0.5}, //   25    / (25+15+10)
 {day: 1, cat: 'b', percent: 0.5}, // (15+10) / (25+15+10)
 {day: 2, cat: 'a', percent: 0.9}, // (90+45) / (90+45+15)
 {day: 2, cat: 'b', percent: 0.1}  //   15    / (90+45+15)
]

Images

The one on the left comes from the data above and can be made in the JSFiddle. The one on the right is what I'm trying to make, based on the same dataset (xf = crossfilter(data);). I hardcoded it (above and in the JSFiddle) to create the second just to visually show what I'm looking to do.

enter image description here enter image description here

Upvotes: 2

Views: 902

Answers (1)

Gordon
Gordon

Reputation: 20150

I this is usually called a normalized stacked bar chart.

I'd organize the data with just one group so that all the values are accessible at once, using a custom reduce function:

var group = dayDim.group().reduce(
    function(p, v) {
        p[v.cat] = (p[v.cat] || 0) + v.quantity;
        return p;
    },
    function(p, v) {
        p[v.cat] = p[v.cat] - v.quantity;
        return p;
    },
    function() {
        return {};
    });

This is pretty much the standard way to reduce multiple values at once from the FAQ.

The advantage of this is that when you access the values, you can easily divide by the sum of all the values:

  .group(group, 'a', kv => kv.value.a / d3.sum(Object.values(kv.value)))
  .stack(group, 'b', kv => kv.value.b / d3.sum(Object.values(kv.value)))

sum-to-one stacks

Fork of your fiddle.

Upvotes: 1

Related Questions