[英]d3 v4: Using stack with histogram data?
我有一堆属于六个类别之一的数据,每个数据都有一个与之关联的时间。 我需要使用直方图将这些数据分类到每月的分类中,这很容易,但是我还需要堆叠每个类别。 我一直在寻找堆叠的直方图示例,但我只能从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
值的数组。 至此,分箱已经完成,我所需要做的就是将所有内容堆叠在一起并获得y0
和y1
值。
所以,我叫stack
,但是它给了我很多垃圾。 stackedHistData
是一个由6个数组组成的数组,每个数组都有一个等于0的0属性,一个等于'NaN'的1属性以及一个具有91个长数组,索引和键(A到F)的data
属性。 我什至都没有看到要由堆栈调用生成的y0
和y1
值。 与这种直方图数据一起使用意味着什么?
最终想通了。 我基本上试图模仿这里找到的数据结构。
首先,我从数据中获取密钥并解析时间。
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,我将使用x0和x1填充对象,因为绘制图表需要这些对象。 然后,我在bin上执行一个foreach
循环,该循环遍历存储在其中的每个数据项。 在此循环中,存储对象为每种类型(即键)获得一个数组。 然后有一个备份循环,可以捕获此bin中未表示的任何类型,以便stack
功能可以正常运行。 说到这里,它是:
var realStack = d3.stack()
.keys(keys)
.value(function(d, key) {
return d[key].length;
});
现在,我们已经正确处理了所有数据,这非常简单。 它只需要获取数据存储区的长度,而不是数据本身。 然后在追加rects时使用该堆栈函数,并将其传递给stackData
变量即可,所有这些都可以解决。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.