2
votes

I'd like to create a stacked bar chart using DC.JS.

I've tried to utilize the documentation from DC.JS (graph,source code) to no avail - Below is my attempt (here is my attempt in jsfiddle) and my most recent attempt in CodePen.

I'd like the 'Name' as the X axis and 'Type' as the stacks.

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<script src="https://rawgithub.com/NickQiZhu/dc.js/master/web/js/crossfilter.js"></script>
<script src="https://cdnjs.site44.com/dc2.js"></script>
<div id="chart"></div>

Javascript

var data = [ {"Name":"Abby","Type":"Apple"}, {"Name":"Abby","Type":"Banana"}, {"Name":"Bob","Type":"Apple"} ]

data.forEach(function(x) {
  x.Speed = +x.Type;
});

var ndx = crossfilter(data)

var xdim = ndx.dimension(function (d) {return d.Name;});

function root_function(dim,stack_name) {
    return dim.group().reduce(
  function(p, v) {
    p[v[stack_name]] = (p[v[stack_name]] || 0) + v.Speed;
    return p;}, 
  function(p, v) {
    p[v[stack_name]] = (p[v[stack_name]] || 0) - v.Speed;
    return p;}, 
  function() {
    return {};
  });}

var ydim = root_function(xdim,'Type')

function sel_stack(i) {
return function(d) {
  return d.value[i];
};}

var chart = dc.barChart("#chart");

chart
  .x(d3.scale.ordinal().domain(xdim))
  .dimension(xdim)
  .group(ydim, "1", sel_stack('1'))
  .xUnits(dc.units.ordinal);

for(var i = 2; i<6; ++i)
  chart.stack(ydim, ''+i, sel_stack(i));

chart.render();

I've been fiddling with this for some time and I have some additional findings:

I believe the root issue lies in the root_function.

1
Your group reducer doesn't have the right structure. Take a look at your group.reduce call vs. the one in the example.Ethan Jewett
What Ethan said. Specifically, it looks like you can pretty much use Type instead of Expt and then loop over the Type values instead of integers in the for-loop. Feel free to edit your question (and ping us with a comment) if this still does not work.Gordon
@Gordon - I followed your advice here and edited my question. It still doesn't seem to work. Any other ideas?Chris
@EthanJewett - I believe I fixed my group.reduce issue so that it's the same structure, but it still isn't working.Chris
Hi Chris, I am not familiar with dc.js, but I think dimple.js is really good tool to accomplish what you're trying. I used your data to create this EXAMPLEmtkilic

1 Answers

6
votes

v.Speed is always NaN in your current example. Because +x.Type attempts to convert a string like "Apple" into a number and fails. If you just want to count, then add or subtract 1 in your reducer, rather than v.Speed. Then you need to update your sel_stack code and chart code to handle this change, of course.

Here's a working example for the 2 types in your data. You'll have to update it to handle arbitrary numbers of types, probably by building an array of all your types up front and then looping through it to add stacks to the chart: http://codepen.io/anon/pen/GjjyOv?editors=1010

var data = [ {"Name":"Abby","Type":"Apple"}, {"Name":"Abby","Type":"Banana"}, {"Name":"Bob","Type":"Apple"} ]

var ndx = crossfilter(data)

var xdim = ndx.dimension(function (d) {return d.Name;});

In the reducer, just add and subtract 1 to count:

var ydim = xdim.group().reduce(
  function(p, v) {
    p[v.Type] = (p[v.Type] || 0) + 1;
    return p;}, 
  function(p, v) {
    p[v.Type] = (p[v.Type] || 0) - 1;
    return p;}, 
  function() {
    return {};
  });

sel_stack no longer takes a number, but a key:

function sel_stack(valueKey) {
return function(d) {
  return d.value[valueKey];
};}

var chart = dc.barChart("#chart");

Here we hard-code the stack key, for the purpose of the example:

chart
  .x(d3.scale.ordinal().domain(xdim))
  .dimension(xdim)
  .group(ydim, "Apple", sel_stack('Apple'))
  .xUnits(dc.units.ordinal);

Again, the other hard-coded stack key. You'll need to recreate the loop after creating some sort of data structure that holds all of your stack values.

//for(var i = 2; i<6; ++i)
  chart.stack(ydim, 'Banana', sel_stack('Banana'));

chart.render();