Update the y-axis of a brushed multi line chart. - javascript

I ran into the exact same issue #SamSelikoff ran into here Update the y-axis of a brushed area chart. The example he was working off of was a single data series area chart here http://bl.ocks.org/mbostock/1667367 while i'm working on a multi-line chart like the one here http://bl.ocks.org/mbostock/3884955.
How do i adapt a data filter for the more complex data mapping in the multi line chart? i.e. with data mapped like so
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
And the y.domain set up like so
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
How do i create a filter similar to this (Sam's univariate data solution)
// Use x.domain to filter the data, then find the max and min duration of this new set, then set y.domain to that
x.domain(brush.empty() ? x2.domain() : brush.extent());
var dataFiltered = data.filter(function(d, i) {
if ( (d.date >= x.domain()[0]) && (d.date <= x.domain()[1]) ) {
return d.duration;
}
})
y.domain([0, d3.max(dataFiltered.map(function(d) { return d.duration; }))]);
I've tried putting a filter within the same syntax used for the min and max of the full data set's y-axis like so
x.domain(brush.empty() ? x2.domain() : brush.extent());
var testMin = d3.min(cities.filter(function(c) { return c.values, function(v) {if ( (v.dates >= x.domain()[0]) && (v.dates <= x.domain()[1]) ){ return v.temperature; } }}))
No luck so far. Any ideas?

Related

Pulling values with javascript using d3.nest for force directed graph

I am trying to add some summary statistics to my force directed D3 graph. I can see the array with console.log(d), but when I try to use the simplest javascript I can't seem to pull the values from the array. Here is the pertinent code:
function fade(opacity) {
return d => {
node.style('stroke-opacity', function (o) {
const thisOpacity = isConnected(d, o) ? 1 : opacity;
console.log(d)
var new_data = d3.nest()
.key(function(j) { return j.group;})
.rollup(function(j) {
return d3.sum(j, function(g) {return g.contribution_total; });
}).map(d); //.entries(d);
//new_data
console.log(new_data)
var expensesByName = d3.nest()
.key(function(g) { return g.group; })
.entries(d);
//.forEach(node);
console.log(expensesByName )
var contributionsByName = d3.nest()
.key(function(g) { return g.contribution_total; })
.entries(d);
//.forEach(node);
//console.log(contributionsByName)
Here is where the live attempt lives: http://people.ischool.berkeley.edu/~jennifer.p/capstone/slider/add_winner_try1.html
All of my console.log() attempts (besides console.log(d)) return an empty array. See picture:
Ultimately I am trying to show a total contribution amount by group (democrat/republican) and then I want to calculate a 'winner' percentage (# nodes with 'W' vs # with null), but right now I just really need help accessing the data period. I've been at this a few hours and it seems so simple, any thoughts are appreciated. TIA!
you need to collect the nodes connected inside the opacity callback and after setting use the list to calculate the d3.next
function fade(opacity) {
return d => {
var connectedNodes = [];
node.style('opacity', function (o) {
var connected = isConnected(d, o);
if (connected) { connectedNodes.push(d); }
return connected ? 1 : opacity;
});
var new_data = d3.nest()
.key(function(j) { return j.group;})
.rollup(function(j) {
return d3.sum(j, function(g) {return g.contribution_total; });
}).entries(connectedNodes);
console.log('new_data', new_data);
link.style('opacity', o => (o.source === d || o.target === d ? 1 : opacity));
};
}
Why do you have a click-handler with fade(1) when you also have the releasenode handler?
.on('click.fade', fade(1))
.on('click',releasenode)

dc.js - Remove empty bins from stacked bar chart upon filtering

Here is the fiddle for a stacked bar chart. This chart filters another line chart.
To remove empty bins, I tried dc.js FAQ, this example and this.
I saw this and this for a stacked bar chart scenario, but my grouping is different.
I've tried different things but I'm not able to get it to work.
Pretty sure I'm missing something simple.
Kindly review my code. Am I doing something wrong? How do i get the remove_empty_bins() working?
var stack = dc.barChart('#stack');
var XDimension = ndx.dimension(function (d) {return d.no;});
var YDimension_before = XDimension.group().reduce(
function(p, d) {
p[d.sub_no] = (p[d.sub_no]|| 0) + +d.avg;
return p;
},
function(p, d) {
p[d.sub_no] = (p[d.sub_no]|| 0) - +d.avg;
return p;
},
function() {
return {};});
var YDimension = remove_empty_bins(YDimension_before);
stack.width(550)
.height(400)
.dimension(XDimension)
.group(YDimension, '1', sel_stack(1))
.transitionDuration(500)
.xUnits(dc.units.ordinal)
.x(d3.scaleBand())
.margins({left: 80, top: 20, right: 80, bottom: 80})
.brushOn(false)
.clipPadding(20)
.elasticX(true)
.elasticY(true)
.title(function(d) {
return [ d.key + '[' + this.layer + '] ',
d.value[this.layer]].join('\n')
});
stack.stack(YDimension, '2', sel_stack(2))
.stack(YDimension, '3', sel_stack(3))
function remove_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
return d.value != 0;
});
}
};
}
I think the problem is that you are reducing to an object, so d.value never equals zero.
You could use Object.values and Array.some to check if any stack is non zero for each bin:
function remove_competely_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
return Object.values(d.value).some(v => v!=0);
});
}
};
}
Warning: dc.js isn't happy if the different stacks don't have the same x values. So that's why you wouldn't want to remove just the empty stacks. Only remove the bin if all the stacks are zero.

Trying to make a bubble chart in dc.js with String values in x-Axis

I am trying to make a bubble chart in dc.js. It should show the total quantity per item type. So the y axis has the domain of numbers and the x axis has the domain of String Values. These string values are not known beforehand, instead they are taken from a database, so i dont know how to define the x domain.
Here is my code
var itemChart = dc.bubbleChart('#item-chart');
//the data
var data = [{
date: "2015-01-01",
itemType: "bags",
quantity: 3
}];
var items = crossfilter(data);
var parseDate = d3.time.format("%Y-%m-%d").parse;
data.forEach(function (d) {
d.date = parseDate(d.date);
d.month = d.date.getMonth();
d.year = d.date.getFullYear();
//d.day = ((d.date.getFullYear() == 0) ? 'Sunday' : ((d.date.getFullYear() == 1) ? 'Monday' : ((d.date.getFullYear() == 2) ? 'Tuesday' : ((d.date.getFullYear() == 3) ? 'Wednesday' : ((d.date.getFullYear() == 4) ? 'Thursday' : ((d.date.getFullYear() == 5) ? 'Friday' : 'Sunday'))))));
d.itemType = d.itemType;
d.quantity = +d.quantity;
});
var itemTypeDim = items.dimension(function (d) {
return d.itemType;
});
var itemTypeProd = itemTypeDim.group().reduceSum(function(d) {
return d.quantity;
});
var prod = yearDim.group().reduceSum(function (d) {
return d.quantity;
});
var rangeVal = [-10, d3.max(itemTypeProd.all(), function(d) { return d.value.quantity; }) ];
itemChart
.width(990)
.height(250)
.transitionDuration(1500)
.margins({top: 10, right: 50, bottom: 30, left: 40})
.dimension(itemTypeDim)
.group(itemTypeProd);
.keyAccessor(function (d){
return d.value.itemType;
})
.valueAccessor(function (d){
return d.value.quantity;
})
.radiusValueAccessor(function (d){
return d.value.quantity;
})
.maxBubbleRelativeSize(0,3)
.elasticX(true)
.elasticY(true)
.yAxisPadding(100)
.xAxisPadding(500)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.xAxisLabel('Item Type')
.yAxisLabel('Total Quantity')
.renderLabel(true)
.label(function (p) {
return p.key;
})
.renderTitle(true)
.title(function (p){
return [
p.key,
'itemType: ' + p.value.customer,
'Quantity:' + p.value.quantity
].join('\n');
});
dc.renderAll();
Interesting - most people use bar charts for counting ordinal values. I am not sure if this use case for bubble charts has been fully explored, but it should work since the ordinal x scale support is at the coordinate grid mixin level.
You'll want at least
.x(d3.scale.ordinal())
https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.coordinateGridMixin+x
https://github.com/mbostock/d3/wiki/Ordinal-Scales
Please let us know how it turns out!

Transforming a dataset with crossfilter

I'm trying to create a dashboard using dc.js and I want to customize how a data table is visualized. So my data looks something like this
agegroup gender group scores total
18-24 M 1 0.04 1
45-54 F 2 2.23 13
25-34 M 1 0.74 6
25-34 M 2 1.47 8
18-24 F 1 2.88 7
35-44 F 2 3.98 14
When I initialize a data table, it'll look the same as my original csv. However what if I want
agegroup gender group1.scores group1.total group2.scores group2.total
18-24 M 0.04 1 0.0 0
18-24 F 2.88 1 0.0 0
25-34 M 0.74 8 1.47 8
25-34 F 0.0 0 0.0 0
Here is how I initalize and set up my data table
dataTable = dc.dataTable('#data-table');
var tableDim = ndx.dimension(function(d) {
return d.gender;
});
dataTable
.width(400)
.height(800)
.dimension(tableDim)
.group(function(d){
return "Counts"
})
.size(20)
.columns([
function(d){
return d.gender;},
function(d){
return d.agegroup;
},
function(d){
return d.group;
},
function(d){
return d.scores;
},
function(d){
return d.total;
},
])
.order(d3.ascending)
.sortBy(function(d){
return d.gender;
});
I know that crossfilter allows you to filter and subset data quickly but I'm not sure how it'll function transforming datasets. Thanks!
So far, I was able to do this for now.
var tableDim = ndx.dimension(function (d) {
return d.agegroup;
});
var dataTable = dc.dataTable("#someTable");
dataTable.width(300).height(800)
.dimension(tableDim)
.group(function (d) {
return "Counts";
})
.columns([
function (d) {
return d.agegroup;
},
function (d) {
return d.gender;
},
function (d) {
if (d.group == 1) return d.scores;
else return 0;
},
function (d) {
if (d.group == 1) return d.total;
else return 0;
},
function (d) {
if (d.group == 2) return d.scores;
else return 0;
},
function (d) {
if (d.group == 2) return d.total;
else return 0;
}]);
dc.renderAll();
Here is the JSFiddle working with the above code. Use this or make a new one next time when you are asking for such solutions on SO.
Remember, using dc.dataTable you may not be able to reduce the number of rows in the data set. If you really want to reduce the number of rows you may try group().reduce() methods and create new fields for group1.total, group1.scores etc..

d3.js t.map is not a function

Hope somebody can help me out because I can't find any reference about this error.
I was working on this piece of code:
var xMin = d3.min(data, function(d) { return d.value; });
var xMax = d3.max(data, function(d) { return d.value; });
if (0 > xMin & 0 > xMax) {
xMax = 0;
}
if (0 < xMin & 0 < xMax) {
xMin = 0;
}
x.domain(xMin, xMax).nice();
y.domain(data.map(function(d) { return d.label; }));
but I must have made some mistake cause now the loading blocks with the error message below in the web console:
"TypeError: t.map is not a function # http://d3js.org/d3.v3.min.js:2
.domain() takes an array as argument, i.e.
x.domain(xMin, xMax).nice();
should be
x.domain([xMin, xMax]).nice();
I had this error when I switched the mock data from an example.
var dataset = d3.layout.stack()(["CountPending", "CountDenied"].map(function (type) {
return data.map(function (d) {
return { x: d.Name, y: +d[type] };
});
}));
In my dataset the example data was using ["pending","denied"] while my real data used the following keys ["CountPending", "CountDenied"]
Use the right keys!
While this might not help the OP, I hope it helps someone.

Categories