简体   繁体   English

d3 v4:将堆栈与直方图数据一起使用?

[英]d3 v4: Using stack with histogram data?

I have a bunch of data that is one of six categories, each piece of data has a time associated with it. 我有一堆属于六个类别之一的数据,每个数据都有一个与之关联的时间。 I need to use a histogram to bin these data into monthly bins, which is easy, but I also need to stack each category. 我需要使用直方图将这些数据分类到每月的分类中,这很容易,但是我还需要堆叠每个类别。 I've been looking for a stacked histogram example but the only ones I can find are from d3 v3, which is apparently very different in its stacking API. 我一直在寻找堆叠的直方图示例,但我只能从d3 v3中找到它,显然在其堆叠API中有很大的不同。 Right now I'm stuck in that after calling stack() I get nonsensical data back, which I can't use to generate a stacked bar chart. 现在,我陷入困境,在调用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 is an object with six keyed objects (A through F), which each contain an array of data objects. dataGroupedByType是一个具有六个键对象(A到F)的对象,每个对象包含一个数据对象数组。 Then I make histDataByType which results in an array of 6 objects, each of which have a type property (A through F) and a values array, which is always the same length (91 in my case, since my data spans 91 months). 然后,我将创建histDataByType ,该数组将导致包含6个对象的数组,每个对象具有一个type属性(A至F)和一个values数组,该数组的长度始终相同(在我的情况下为91,因为我的数据跨越91个月)。 Within that array is another array with bin data (if any exists), and the x0 and x1 values. 在该数组中是另一个包含bin数据(如果有)以及x0x1值的数组。 At this point, the binning has been done, all I need is to stack everything and get the y0 and y1 values. 至此,分箱已经完成,我所需要做的就是将所有内容堆叠在一起并获得y0y1值。

So, I call stack , but it gives me garbage out; 所以,我叫stack ,但是它给了我很多垃圾。 stackedHistData is an array of 6, each array has a 0 property which equals 0, a 1 property that equals 'NaN', and a data property that has that 91-long array, the index, and the key (A through F). stackedHistData是一个由6个数组组成的数组,每个数组都有一个等于0的0属性,一个等于'NaN'的1属性以及一个具有91个长数组,索引和键(A到F)的data属性。 I'm not even seeing the y0 and y1 values that are meant to be generated by the stack call. 我什至都没有看到要由堆栈调用生成的y0y1值。 How is it meant to be used with this kind of histogram data? 与这种直方图数据一起使用意味着什么?

Figured this out eventually. 最终想通了。 I basically attempted to emulate the data structure found here . 我基本上试图模仿这里找到的数据结构。

Firstly I acquired the keys from the data as well as parsing the times. 首先,我从数据中获取密钥并解析时间。

var keys = [];
data.forEach(function(d) {
    d.date = d3.isoParse(d.relevantDate);
    keys.push(d.type);
});

keys = _.uniq(keys);

Here I'm using the lodash library to unique-ify my array of keys. 在这里,我使用lodash库来唯一化我的键数组。 The next step is to make the bins as you would normally do for a histogram: 下一步是像通常对直方图所做的那样制作垃圾箱:

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; })]);

The domain can be declared here too. 域也可以在这里声明。 Now comes the fun part: 有趣的来了:

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);
}

I make an empty stackData var, and loop through the bins. 我创建一个空的stackData var,并遍历各个垃圾箱。 For each bin I populate an object with x0 and x1, since those are going to be needed for drawing the chart. 对于每个bin,我将使用x0和x1填充对象,因为绘制图表需要这些对象。 Then, I do a foreach loop on the bin which loops over each data item stored within. 然后,我在bin上执行一个foreach循环,该循环遍历存储在其中的每个数据项。 The storage object gets one array per type (aka key) in this loop. 在此循环中,存储对象为每种类型(即键)获得一个数组。 Then there's a backup loop afterwards to catch any types that weren't represented in this bin, so that the stack function can function correctly. 然后有一个备份循环,可以捕获此bin中未表示的任何类型,以便stack功能可以正常运行。 Speaking of that, here it is: 说到这里,它是:

var realStack = d3.stack()
    .keys(keys)
    .value(function(d, key) {
        return d[key].length;
    });

It's pretty simple now that we have all the data massaged properly. 现在,我们已经正确处理了所有数据,这非常简单。 It just needs to get the length of the data buckets instead of the data itself. 它只需要获取数据存储区的长度,而不是数据本身。 Then just use that stack function when appending rects and pass it the stackData variable, and all will work out. 然后在追加rects时使用该堆栈函数,并将其传递给stackData变量即可,所有这些都可以解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM