score:0
i fixed the colored stack problem and counting issue to how i'd like the counting to be done. i am using d3.js verison 2.5.5, crossfilter.js version 1.3.11, and dc.js and dc.css of version 2.1.0 dev. https://jsfiddle.net/jnf84n7c/
var data = [
{"key":"key-1","state":"ma", "status":["a","r","c"], "items":["orange", "meat", "bread"], "date":"y16"},
{"key":"key-2","state":"ma", "status":["a","o"], "items":["apple", "bread"], "date":"y15"},
{"key":"key-3","state":"tx", "status":["o"], "items":["bread"], "date":"y16"},
{"key":"key-4","state":"tn", "status":["a","r"], "items":["apple", "bread"], "date":"y16"},
{"key":"key-5","state":"tn", "status":["a","o"], "items":["apple", "orange"], "date":"y15"},
{"key":"key-6","state":"tn", "status":[], "items": [], "date":"y14"}
];
var cf = crossfilter(data);
//dimensions and groups:
var dates = cf.dimension(function(d){ return d.date; });
var datesgroup = dates.group();//.reducecount(function(d){ return d.key; });
var states = cf.dimension(function(d){ return d.state; });
var statesgroup = states.group();//.reducecount(function(d){ return d.key; });
var itemsdim = cf.dimension(function(d){ return d.items; });
var itemsgroup = itemsdim.groupall().reduce(reduceadd, reduceremove, reduceinitial).value();
itemsgroup.all = myallfunction;
var states_items_group_apple = states.group().reduce(reduceadd_apple, reduceremove_apple, reduceinitial_items);
var states_items_group_bread = states.group().reduce(reduceadd_bread, reduceremove_bread, reduceinitial_items);
var states_items_group_orange = states.group().reduce(reduceadd_orange, reduceremove_orange, reduceinitial_items);
var states_items_group_meat = states.group().reduce(reduceadd_meat, reduceremove_meat, reduceinitial_items);
var itemsgroup1 = itemsdim.groupall().reduce(reduceadd1, reduceremove1, reduceinitial).value();
var itemsgroup2 = itemsdim.groupall().reduce(reduceadd2, reduceremove2, reduceinitial).value();
var itemsgroup3 = itemsdim.groupall().reduce(reduceadd3, reduceremove3, reduceinitial).value();
itemsgroup1.all = myallfunction;
itemsgroup2.all = myallfunction;
itemsgroup3.all = myallfunction;
var status = cf.dimension(function(d){ return d.status; });
var statusgroup1 = status.groupall().reduce(reduceadd_group1, reduceremove_group1, reduceinitial_group).value();
var statusgroup2 = status.groupall().reduce(reduceadd_group2, reduceremove_group2, reduceinitial_group).value();
var statusgroup3 = status.groupall().reduce(reduceadd_group3, reduceremove_group3, reduceinitial_group).value();
var statusgroup4 = status.groupall().reduce(reduceadd_group4, reduceremove_group4, reduceinitial_group).value();
statusgroup1.all = myallfunction;
statusgroup2.all = myallfunction;
statusgroup3.all = myallfunction;
statusgroup4.all = myallfunction;
var statusgroup = status.groupall().reduce(reduceadd_group, reduceremove_group, reduceinitial_group).value();
statusgroup.all = myallfunction;
//graphs:
var row = dc.rowchart("#rowchart");
row.height(170)
.dimension(itemsdim)
.group(itemsgroup)
.ordering(function(d){return -d.value;})
.renderlabel(true)
.ordinalcolors(["#008600", "#80ff80", "#ff80ff", "#860086"])
.xaxis().ticks(3);
row.filterhandler(myfilterfunction);
var pie1 = dc.piechart("#piechart1");
pie1.height(75).width(75)
.dimension(dates)
.group(datesgroup);
var pie2 = dc.piechart("#piechart2");
pie2.height(75).width(75)
.dimension(states)
.group(statesgroup);
var pie3 = dc.piechart("#piechart3");
pie3.height(75).width(75)
.dimension(status)
.group(statusgroup);
pie3.filterhandler(myfilterfunction);
var bar = dc.barchart("#barchart");
bar.width(500).height(200)
.dimension(states)
.group(states_items_group_bread, 'bread')
.stack(states_items_group_orange, 'orange')
.stack(states_items_group_apple, 'apple')
.stack(states_items_group_meat, 'meat')
.valueaccessor(function(p){ return p.value.count; })
.renderhorizontalgridlines(true)
.renderlabel(true)
.legend(dc.legend().x(100).y(0).horizontal(1).itemheight(13).gap(6).legendwidth(400).itemwidth(100))
.gap(10)
.elasticx(true).elasticy(true)
.yaxislabel("count")
.x(d3.scale.ordinal())
.xunits(dc.units.ordinal)
.margins({top:30, left:50, right:10, bottom:50});
//bar.filterhandler(myfilterfunction);
//bar.on("renderlet", function(_chart){
// _chart.selectall("rect.bar").on("click", _chart.onclick);
//});
var bar2 = dc.barchart("#barchart2");
bar2.width(500).height(200)
.dimension(itemsdim)
.group(itemsgroup1, 'ma')
.stack(itemsgroup2, 'tn')
.stack(itemsgroup3, 'tx')
.renderhorizontalgridlines(true)
.renderlabel(true)
.legend(dc.legend().x(60).y(0).horizontal(1).itemheight(13).gap(6).legendwidth(400).itemwidth(60))
.gap(10)
.yaxislabel("count")
.x(d3.scale.ordinal())
.xunits(dc.units.ordinal)
.ordinalcolors(["#008600", "#80ff80", "#ff80ff", "#860086"])
.margins({top:30, left:50, right:10, bottom:50});
bar2.filterhandler(myfilterfunction);
var bar3 = dc.barchart("#barchart3");
bar3.width(500).height(200)
.dimension(status)
.group(statusgroup1, "bread")
.stack(statusgroup2, "apple")
.stack(statusgroup3, "orange")
.stack(statusgroup4, "meat")
.renderhorizontalgridlines(true)
.renderlabel(true)
.legend(dc.legend().x(60).y(0).horizontal(1).itemheight(13).gap(6).legendwidth(400).itemwidth(60))
.gap(10)
.yaxislabel("count")
.x(d3.scale.ordinal())
.xunits(dc.units.ordinal)
// .ordinalcolors(["#008600", "#80ff80", "#ff80ff", "#860086"])
.margins({top:30, left:50, right:10, bottom:50});
bar3.filterhandler(myfilterfunction);
dc.renderall(); // render graphs
//reduce functions:
function reduceadd(p, v) {
if (v.items[0] === "") return p; // skip empty values
v.items.foreach (function(val, idx) {
p[val] = (p[val] || 0) + 1; //increment counts
});
return p;
}
function reduceremove(p, v) {
if (v.items[0] === "") return p; // skip empty values
v.items.foreach (function(val, idx) {
p[val] = (p[val] || 0) - 1; //decrement counts
});
return p;
}
function reduceinitial() {
return {
bread: 0,
apple: 0,
orange: 0,
meat: 0
};
}
function reduceadd1(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "ma"){
v.items.foreach (function(val, idx) {
p.bread += (val === 'bread' ? 1 : 0);
p.apple += (val === 'apple' ? 1 : 0);
p.orange += (val === 'orange' ? 1 : 0);
p.meat += (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceremove1(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "ma"){
v.items.foreach (function(val, idx) {
p.bread -= (val === 'bread' ? 1 : 0);
p.apple -= (val === 'apple' ? 1 : 0);
p.orange -= (val === 'orange' ? 1 : 0);
p.meat -= (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceadd2(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "tn"){
v.items.foreach (function(val, idx) {
p.bread += (val === 'bread' ? 1 : 0);
p.apple += (val === 'apple' ? 1 : 0);
p.orange += (val === 'orange' ? 1 : 0);
p.meat += (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceremove2(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "tn"){
v.items.foreach (function(val, idx) {
p.bread -= (val === 'bread' ? 1 : 0);
p.apple -= (val === 'apple' ? 1 : 0);
p.orange -= (val === 'orange' ? 1 : 0);
p.meat -= (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceadd3(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "tx"){
v.items.foreach (function(val, idx) {
p.bread += (val === 'bread' ? 1 : 0);
p.apple += (val === 'apple' ? 1 : 0);
p.orange += (val === 'orange' ? 1 : 0);
p.meat += (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceremove3(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.state === "tx"){
v.items.foreach (function(val, idx) {
p.bread -= (val === 'bread' ? 1 : 0);
p.apple -= (val === 'apple' ? 1 : 0);
p.orange -= (val === 'orange' ? 1 : 0);
p.meat -= (val === 'meat' ? 1 : 0);
});
}
return p;
}
function reduceadd_apple(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count += (val === 'apple' ? 1 : 0);
});
return p;
}
function reduceremove_apple(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count -= (val === 'apple' ? 1 : 0);
});
return p;
}
function reduceadd_bread(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count += (val === 'bread' ? 1 : 0);
});
return p;
}
function reduceremove_bread(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count -= (val === 'bread' ? 1 : 0);
});
return p;
}
function reduceadd_orange(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count += (val === 'orange' ? 1 : 0);
});
return p;
}
function reduceremove_orange(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count -= (val === 'orange' ? 1 : 0);
});
return p;
}
function reduceadd_meat(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count += (val === 'meat' ? 1 : 0);
});
return p;
}
function reduceremove_meat(p, v){
if (v.items[0] === "") return p; // skip empty values
p.state = v.state;
v.items.foreach(function(val, idx){
p.count -= (val === 'meat' ? 1 : 0);
});
return p;
}
function reduceadd_group1(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "bread"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a += (val2 === 'a' ? 1 : 0);
p.o += (val2 === 'o' ? 1 : 0);
p.c += (val2 === 'c' ? 1 : 0);
p.r += (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceremove_group1(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "bread"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a -= (val2 === 'a' ? 1 : 0);
p.o -= (val2 === 'o' ? 1 : 0);
p.c -= (val2 === 'c' ? 1 : 0);
p.r -= (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceadd_group2(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "apple"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a += (val2 === 'a' ? 1 : 0);
p.o += (val2 === 'o' ? 1 : 0);
p.c += (val2 === 'c' ? 1 : 0);
p.r += (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceremove_group2(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "apple"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a -= (val2 === 'a' ? 1 : 0);
p.o -= (val2 === 'o' ? 1 : 0);
p.c -= (val2 === 'c' ? 1 : 0);
p.r -= (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceadd_group3(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "orange"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a += (val2 === 'a' ? 1 : 0);
p.o += (val2 === 'o' ? 1 : 0);
p.c += (val2 === 'c' ? 1 : 0);
p.r += (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceremove_group3(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "orange"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2){
p.a -= (val2 === 'a' ? 1 : 0);
p.o -= (val2 === 'o' ? 1 : 0);
p.c -= (val2 === 'c' ? 1 : 0);
p.r -= (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceadd_group4(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "meat"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a += (val2 === 'a' ? 1 : 0);
p.o += (val2 === 'o' ? 1 : 0);
p.c += (val2 === 'c' ? 1 : 0);
p.r += (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceremove_group4(p, v) {
if (v.items[0] === "") return p; // skip empty values
if (v.status[0] === "") return p; // skip empty values
v.items.foreach(function(val1, idx1){
if (val1 === "meat"){
v.status.foreach (function(val2, idx2) {
if (idx1 === idx2) {
p.a -= (val2 === 'a' ? 1 : 0);
p.o -= (val2 === 'o' ? 1 : 0);
p.c -= (val2 === 'c' ? 1 : 0);
p.r -= (val2 === 'r' ? 1 : 0);
}
});
}
});
return p;
}
function reduceadd_group(p, v) {
if (v.status[0] === "") return p; // skip empty values
v.status.foreach (function(val, idx) {
p[val] = (p[val] || 0) + 1;
});
return p;
}
function reduceremove_group(p, v) {
if (v.status[0] === "") return p; // skip empty values
v.status.foreach (function(val, idx) {
p[val] = (p[val] || 0) - 1;
});
return p;
}
function reduceinitial_group() {
return {
a: 0,
o: 0,
c: 0,
r: 0
};
}
function reduceinitial_items(){
return {
count: 0,
state: ''
};
}
//filter function:
function myfilterfunction (dimension, filters) {
dimension.filter(null);
if (filters.length === 0)
dimension.filter(null);
else
dimension.filterfunction(function (d) {
for (var i=0; i < d.length; i++) {
if (filters.indexof(d[i]) >= 0) return true;
}
return false;
});
return filters;
}
//all function:
function myallfunction() {
var newobject = [];
for (var key in this) {
if (this.hasownproperty(key) && key != "all") {
newobject.push({
key: key,
value: this[key]
});
}
}
return newobject;
};
score:1
the first thing you need to do is upgrade to:
- dc.js 2.0 betas
- crossfilter 1.4.0-beta.06 (crossfilter now lives here: https://github.com/crossfilter/crossfilter)
- reductio (recommended) so that you don't have to manually build custom groupings - this not strictly necessary, but groupings are the source of a lot of problems, so i'd recommend using reductio or universe to leverage the work of those who came before.
next, with all this new goodness, we can simplify things a ton. here is an updated fiddle using the new features of these libraries: https://jsfiddle.net/ff8ox8vq/
i'll go through them in a little detail in the complete code sample below.
var data = [
{"key":"key-1","state":"ma", "items":["orange", "meat", "bread"], "date":"y16"},
{"key":"key-2","state":"ma", "items":["apple", "bread"], "date":"y15"},
{"key":"key-3","state":"tx", "items":["bread"], "date":"y16"},
{"key":"key-4","state":"tn", "items":["apple", "bread"], "date":"y16"},
{"key":"key-5","state":"tn", "items":["apple", "orange"], "date":"y15"},
{"key":"key-6","state":"tn", "items": [], "date":"y14"}
];
var cf = crossfilter(data);
no change above.
//dimensions and groups:
var dates = cf.dimension(function(d){ return d.date; });
var datesgroup = dates.group();
var states = cf.dimension(function(d){ return d.state; });
var statesgroup = states.group()
.reducecount
is the default setting of a group. calling it on a new group doesn't do anything. reducecount
also doesn't take any parameters (unlike reducesum
). so we just get rid of it.
var itemsdim = cf.dimension(function(d){ return d.items; }, true);
var itemsgroup = itemsdim.group();
this is where it starts to get interesting. crossfilter 1.4.0 supports an "array dimension" flag on a dimension call. if we set this to true
, crossfilter knows that items
is an array and will be smart about how it handles it. you no longer have to override the .all
method or anything like that. it's handled internally.
var addvaluegroup = function(reducer, key) {
reducer
.value(key)
.filter(function(d) { return d.items.indexof(key) !== -1; })
.count(true)
}
utility function for adding item-specific counts to the state groups.
// reductio nest to break down states by item
var reducer = reductio().count(true)
addvaluegroup(reducer, "orange")
addvaluegroup(reducer, "meat")
addvaluegroup(reducer, "bread")
addvaluegroup(reducer, "apple")
reducer(statesgroup);
configure the grouping of statesgroup
. reductio just builds custom reduce functions. what happens here is that we maintain a top-level count of all records in a state, then we create filtered counts for each type of item. do a console.log(statesgroup.all())
after this runs to see the structure of the resulting group.
//graphs:
var row = dc.rowchart("#rowchart");
row
.renderlabel(true)
.height(200)
.dimension(itemsdim)
.group(itemsgroup)
.ordering(function(d){return -d.value;})
.xaxis().ticks(3);
var pie1 = dc.piechart("#piechart1");
pie1
.height(75)
.width(75)
.dimension(dates)
.group(datesgroup);
no change.
var pie2 = dc.piechart("#piechart2");
pie2
.height(75)
.width(75)
.dimension(states)
.group(statesgroup)
.valueaccessor(function(d) { return d.value.count; });
our reductio reducer changes the structure of the group somewhat, so we need a valueaccessor
.
var bar = dc.barchart("#barchart");
bar.width(500).height(200)
.dimension(states)
.group(statesgroup, 'orange', sel_stack('orange'))
.stack(statesgroup, 'meat', sel_stack('meat'))
.stack(statesgroup, 'bread', sel_stack('bread'))
.stack(statesgroup, 'apple', sel_stack('apple'))
.renderhorizontalgridlines(true)
.renderlabel(true)
.legend(dc.legend())
.gap(10)
.yaxislabel("count")
.x(d3.scale.ordinal())
.xunits(dc.units.ordinal);
just works with no fancy custom filter functions or anything. dc.js and crossfilter know what to do. there does seem to be a bug in dc.js with ordinal stacked bar charts, unfortunately, so you'll have to color the bars correctly post-render at the moment :-( maybe gordon will chime in with a hint here.
dc.renderall();
function sel_stack(i) {
return function(d) {
return d.value[i] ? d.value[i].count : 0;
};
}
slight change due to the updated group structure, and a little safety in case you mis-type one of the item keys.
Source: stackoverflow.com
Related Query
- d3/dc.js - How to create a stacked bar chart while telling crossfilter to treat elements in an array as separate records?
- How to create a stacked bar chart using dc.js?
- how to create a stacked bar chart with images in d3
- How to create a d3 stacked bar chart that only iterates through columns 1 and 2 of data file?
- How do you create a stacked bar chart in d3 when each individual segment is an object?
- How to create stacked row chart with one row with dc.js?
- How to modify axis labels in d3 for a stacked bar chart when the axis labels are mapped as part of the scale's domain
- How to create Stacked Line Chart D3, Multiple Y Axis and common X Axis
- How to make grouped stacked bar chart in d3js?
- d3.js create stacked bar chart from values in object
- How to create vertically grouped bar chart in d3.js using json data?
- How to line up x axis on stacked bar chart with a line overlay in d3.js
- How to show values stacked bar chart utilizing dc.js and d3.js?
- Create Grouped Stacked Bar Chart
- How to create a % difference arrow line with value in a bar chart using D3.js
- D3 v6: How to zoom stacked bar by selected segment to stretch only selected segment and shrink rest segments (and keep initial chart width)?
- I am facing problems while making a dragable stacked bar chart in d3 v4. The major issue I am facing is in the translation of svg when dragging
- How to create a dynamic bar chart in d3.js?
- How to create a drop down in a HTML table and d3 bar chart
- How to add live data to stacked bar chart
- How to create a bar chart made up of very small squares
- how to create separate bar chart with JSON array set?
- How do I set the X values for a singled stacked horizontal bar chart using d3.js?
- How to create bar chart with path property in d3js?
- How to use log scale to show proper stacked bar chart using d3 JS?
- How can I make the legend's similar to the stacked bar chart (and change the legend's label) in D3 v6?
- How to display the total value of a stacked bar chart in d3js
- How to get Average from DOW of Date column using crossfilter to populate in bar chart
- How can I create hierarchical bar chart in d3.js
- how to update text added in dimple.js stacked bar chart on data updation
More Query from same tag
- Drawing 100 circles with IDs and associating mouse events with them
- d3: Avoid unchanged nodes?
- Converting d3.js SVG code to a standalone program — Example?
- How do I create link of each word in d3 cloud?
- How to use D3 selectAll with multiple class names
- How to move/pan the svg to a certain position?
- d3 - Graph Network Node Link Spacing
- d3js text not showing on node graph
- Implementing multiple D3 layout on same page
- d3js - Trouble with select().on('click')
- How to Write Typescript d.ts FIle to Extend @types/d3
- Can I run a component method only in the client using Angular2 Universal?
- invoking an if…then function on a d3 node
- Mysterious syntax error converting a D3 vis from Javascript to Purescript
- D3 - nest.entries() throws Uncaught TypeError
- Clipping of overflowing div inside foreignObject during translate in Firefox
- numberDisplay in dc.js that shows the total sum of values in the row chart
- d3.js - svg filter equivalent with d3.js
- Pre determined x and y values of the tree layout
- Does it matter which term I pass to d3.selectAll() during a data join, if whatever I append seems to rely solely on the .append() call?
- D3 table with Objects along x axis, fields along y axis. Cell coloured if field condition is met
- D3 Sankey link scaling issue
- change the attribute of circles based on multiple selections in d3.js
- Return RGB object instead of string from d3 scale and color interpolation
- d3.js time scale axis ticks are not equally spaced
- How to draw multiple d3js zoomable treemap
- Large or unknown number of data arrays with Multiple XY Line Chart in c3.js
- Using a local geoPath variable for multiple maps using the same path data, different projections in d3.js
- Google Chrome: How can I watch the state of the DOM while holding the mouse button
- D3.js idles between each mousewheel event