aceminer
aceminer

Reputation: 4295

Crossfilter stacked bar charts negate values

I am using a crossfilter2 with dcv3

My data is in a csv which i loaded into memory

Original Data

Day, ID
1, 2
1, 2
1, 2
2, 5
3, 6
4, 6 

Processed data

Day, ID, target
1, 2, True
1, 2, True
1, 2, True
2, 5, False
3, 6, False
4, 6, False 

Currently what i am trying to do is create a crossfilter stackedbar chart with 2 bars. If ID == 2, i consider it as one group, and ID !=2 as another group. However, i cannot do it dynamically it in DC/crossfilter which results me having to preprocess the data to add a new column and work off the column as shown by my solution below.

Is there a better way?

var dimID = ndx.dimension(function(d) { return d.day; });

var id_stacked = dimID.group().reduce(
function reduceAdd(p, v) {
    p[v.target] = (p[v.target] || 0) + 1;
    return p;
},
function reduceRemove(p, v) {
    p[v.target] = (p[v.target] || 0) - 1;
    return p;
},
function reduceInitial() {
    return {};
    });

//Doing the stacked bar chart here
stackedBarChart.width(1500)
.height(150)
.margins({top: 10, right: 10, bottom: 50, left: 40})
.dimension(dimID)
.group(id_stacked, 'Others', sel_stack("True"))
.stack(id_stacked, 'Eeid of interest', sel_stack("False"))

This is my sel_stack function

function sel_stack(i) {
    return function(d) {
        return d.value[i] ? d.value[i] : 0;

    };
}

I am plotting a bar chart with x-axis being the day and the Y-axis being the frequency of ID == 2 or ID!=2 in a stacked bar chart

Upvotes: 1

Views: 50

Answers (1)

Gordon
Gordon

Reputation: 20150

So you want to group by day and then stack by whether ID===2. Although dc.js will accept many different formats, often the trick is getting the data into the right shape.

You're on the right track, but you don't need the extra column in order to create stacks for "is 2" and "not 2". You can calculate it directly:

var dayDimension = ndx.dimension(function(d) { return d.Day; }),
    idStackGroup = dayDimension.group().reduce(
        function add(p, v) {
            ++p[v.ID===2 ? 'is2' : 'not2'];
            return p;
        },
        function remove(p, v) {
            --p[v.ID===2 ? 'is2' : 'not2'];
            return p;
        },
        function init() {
              return {is2: 0, not2: 0};
        });

These are standard add/remove functions for reducing multiple values for each bin. You'll find other variations where the name of the field is driven by the data. But here we know what fields will exist, so we can initialize them to zero in init and not worry about encountering new fields.

The add function is called when a row is added to the crossfilter or a filter changes so that a row is included; the remove function is called whenever a row is filtered out or removed from crossfilter. Since we're not worried about undefined (1) we can simply increment (++) and decrement (--) the values.

Finally we need accessors to pull these values out of the object. I think it's simpler to put the stack accessors inline - sel_stack was written for adding a dynamic number of stacks. (YMMV)

  .group(idStackGroup, 'Others', d => d.value.not2)
  .stack(idStackGroup, 'Eeid of interest', d => d.value.is2);

two / not two stacks

https://jsfiddle.net/gordonwoodhull/fu4w96Lh/23/

(1) If you do any arithmetic on undefined it casts to NaN and NaN ruins all further calculations.

Upvotes: 1

Related Questions