我有一堆数据是六个类别中的一个,每个数据都有一个与之相关的时间。我需要使用直方图将这些数据分成月份箱,这很容易,但我还需要堆叠每个类别。我一直在寻找一个堆叠的直方图示例,但我能找到的唯一一个来自d3 v3,它的堆叠API显然非常不同。现在,我在调用stack()
之后陷入困境,我得到了无意义的数据,我无法用它来生成堆积条形图。
var data = this.data;
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = this.width - margin.left - margin.right,
height = this.height - margin.top - margin.bottom;
data.forEach(function(d) {
d.date = d3.isoParse(d.createdDate);
});
// set the ranges
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) { return d.date; }))
.rangeRound([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var colours = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select(this.$.chart);
var svg2 = svg.select("#canvas");
var histogram = d3.histogram()
.value(function(d) { return d.date; })
.domain(x.domain())
.thresholds(x.ticks(d3.timeMonth));
var dataGroupedByType = d3.nest()
.key(function(d) {
return d.type;
})
.object(data, d3.map);
var histDataByType = [];
for (var key in dataGroupedByType) {
var histData = histogram(dataGroupedByType[key]);
histDataByType.push({type: key, values: histData});
}
var stack = d3.stack()
.keys(["A","B","C","D","E","F"])
.value( function(d, key) {
return d.values;
});
var stackedHistData = stack(histDataByType);
dataGroupedByType
是一个具有六个键控对象(A到F)的对象,每个对象都包含一个数据对象数组。然后我创建histDataByType
,这会产生一个包含6个对象的数组,每个对象都有一个type
属性(A到F)和一个values
数组,它们的长度始终相同(91在我的情况下,因为我的数据跨越91个月)。在该数组中是另一个包含bin数据(如果存在)以及x0
和x1
值的数组。此时,binning已经完成,我只需要堆叠所有内容并获取y0
和y1
值。
所以,我打电话给stack
,但它给了我垃圾; stackedHistData
是一个6的数组,每个数组都有一个等于0的0属性,一个等于' NaN'的属性,以及一个具有91长数组的data
属性,索引和密钥(A到F)。我甚至没有看到堆栈调用要生成的y0
和y1
值。如何与这种直方图数据一起使用?
答案 0 :(得分:0)
最终想出来了。我基本上试图模拟数据结构found here。
首先,我从数据中获取了密钥并解析了时间。
var keys = [];
data.forEach(function(d) {
d.date = d3.isoParse(d.relevantDate);
keys.push(d.type);
});
keys = _.uniq(keys);
这里我使用lodash库来识别我的数组键。下一步是像通常对直方图那样制作垃圾箱:
var histogram = d3.histogram()
.value(function(d) { return d.date; })
.domain(x.domain())
.thresholds(x.ticks(d3.timeMonth));
var bins = histogram(data);
y.domain([0, d3.max(bins, function(d) { return d.length; })]);
域名也可以在这里声明。现在来了有趣的部分:
var stackData = [];
for (var bin in bins) {
//console.log(bins[bin].x0, bins[bin].x1)
var pushableObject = {};
// add the time boundaries.
pushableObject.x0 = bins[bin].x0;
pushableObject.x1 = bins[bin].x1;
// for each bin, split the data into the different keys.
bins[bin].forEach(function(d) {
//console.log(d);
if (!pushableObject[d.type]) { pushableObject[d.type] = [d]}
else pushableObject[d.type].push(d);
})
// if any of the keys didn't get represented in this bin, give them empty arrays for the stack function.
keys.forEach( function(key) {
if (!pushableObject[key]) {
pushableObject[key] = [];
}
})
stackData.push(pushableObject);
}
我创建一个空的stackData var,然后遍历这些bin。对于每个bin,我使用x0和x1填充对象,因为绘制图表需要这些对象。然后,我在bin上执行foreach
循环,循环遍历存储在其中的每个数据项。存储对象在此循环中为每种类型(也称为密钥)获取一个数组。然后是一个备份循环,以捕获此bin中未表示的任何类型,以便stack
函数可以正常运行。说到这里,它是:
var realStack = d3.stack()
.keys(keys)
.value(function(d, key) {
return d[key].length;
});
现在我们已经正确地按摩了所有数据,这很简单。它只需要获取数据桶的长度而不是数据本身。然后只需在附加rects时使用该堆栈函数并将其传递给stackData
变量,所有这些都可以解决。