Shahabaz
Shahabaz

Reputation: 665

Stackbar chart in dcjs split by dimension

I am trying to create a stack bar chart in dcjs. The dcjs stack bar examples are quite clear the huge difference from the barchart with that of stack is that the stack function. The stack function takes the same group as input and it can take third parameter as function which decides by which value it has to split. I rather want a dimension to be split the entire bar chart.

Lets say the following data point is something like this

data = [
  {activity:"A1",time_taken:10,activity_group:"Master A"},
  {activity:"A2",time_taken:20,activity_group:"Master B"},
  {activity:"A1",time_taken:30,activity_group:"Master C"},
  {activity:"A2",time_taken:15,activity_group:"Master D"}
]

I want to have activity group in x-axis split by its activity representing time taken on y-axis, like this:

goal

How do I achieve this ?

Upvotes: 1

Views: 228

Answers (1)

Gordon
Gordon

Reputation: 20140

Your fiddle is on dc.js version 1.7, which is more than five years old and not something I can wrap my head around. :-/

I converted it to dc.js version 2, which also uses D3 v3.

dc.js is not great at showing the raw data, it's more about showing aggregated data. But in this case it could make sense to create a stack for every activity_group; that way it will automatically be assigned its own color.

Using ES6 we can get a list of all activity_group like this:

const stacks = [...new Set(data.map(row => row.activity_group))];

Now let's aggregate the data by stack:

var groupActivity = dimByActivity.group().reduce(
    function reduceAdd(p, v) {
        p[v.activity_group] += v.time_taken;
        return p;
    },
    function reduceRemove(p, v) {
        p[v.activity_group] -= v.time_taken;
        return p;
    },
    function reduceInitial() {
        return Object.fromEntries(stacks.map(stack => [stack,0]));
    });

This is substantially the same as the stacked bar example except that we have a stack per activity_group.

Note that we are creating all the stacks in every bin, just leaving them zero where they don't exist. This is because dc.js expects the same stacks for every X value - it won't work otherwise.

As in the stacked bar example, we'll programmatically add the stacks to the chart, keeping in mind that we need to use .group() for the first stack:

function sel_stack(valueKey) {
    return function(d) {
    return d.value[valueKey];
  };
}
// ...
stacks.forEach(function(stack, i) {
  if(i===0)
    chanUtil.group(groupActivity, stack, sel_stack(stack));
  else
    chanUtil.stack(groupActivity, stack, sel_stack(stack));
})

Here's the output. I messed a little with the margins and height in order to get the legend not to overlap and there are probably smarter ways to deal with this:

demo screenshot

Fork of your fiddle.

As I said, this is making dc.js do something it doesn't want to do, so YMMV!

Upvotes: 1

Related Questions