在d3中堆叠矩阵,而不重新映射到json [英] Stack a matrix in d3 without remapping to json

查看:106
本文介绍了在d3中堆叠矩阵,而不重新映射到json的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

d3的堆叠功能的文档 d3.stack 显示一个对象数组的示例(每个json对象代表x轴测量的点的整体)。例如:

  var data = [
{month:new Date(2015,0,1),apples:3840 ,香蕉:1920,樱桃:960},
{月:新日期(2015,1,1),苹果:1600,香蕉:1440,樱桃:720}
] code>

我试图生成一个数据系列矩阵的堆积直方图( [[], ],[],etc] )。它很容易迭代通过行,并得到一系列直方图bin(已预定义x规模和域名其他地方):

  for(let i = 0; i  bins [i] = d3.histogram()
.domain(x.domain())
。 threshold(x.ticks(10))
(data [i]);
}

在另一个循环中为每个数据系列创建组:

  let bars = this.svg.selectAll(。series+ i)
.data(this.bins [i])
.enter()。append(g)
.classed(series+ i,true)

$ b b

但是当然做它像我被困在这里。我应该如何在该特定系列的正确x,y坐标上 bars.append(rect)换句话说,我现在有一个非常有用的数组 bins ,看起来像:

  [
[[1,2,3,3],[5,8,9],[10],...等],//按5 $ b的分组$ b [[1,3],[7,7,9,9],[11],...等],// series1
[[2,3,3],[8,9] ,[10,12],... etc],// series2
... etc
]

有没有办法调用 stack 而不把所有的数据变成json键,值对?

解决方案

我浏览了,没有注释+单个字符变量=我的理解,它不会发生没有。。因此,我提出了一些尝试,试图挽救别人一段时间:

  / * 
*静态辅助函数直方图仓库数组到一个对象数组
*,适用于送入d3.stack()函数。
* Args:
* bins(array):一个d3直方图的数组bb
* /
static processBins(bins){
let temp = {} // temp的键将是bin名称(即bin分隔符值)
//现在为每个bin创建一个带有键的对象,并创建一个空对象作为数据的占位符
bins [0] .map((bin)=> {temp [bin.x0] = {}});
for(let i = 0; i //遍历每个系列
bins [i] .map(bin => {
temp [bin.x0] [series+ i] = bin.length; //推送每个系列的频率计数
});
}
/ *现在我们有一个顶级键是bin的对象:
{
binName0:{series0:freqCount0,series1:freqCount1,...}
binName1:{...},
...
}
现在,我们将创建一个对象数组,包含所有系列的频率
* /
let result = [];
for(let binName in temp){//遍历bin对象
let resultRow = {};
if(temp.hasOwnProperty(binName)){
resultRow [bin] = binName; //将bin名称键/值对放入结果行
for(let seriesName in temp [binName]){//遍历系列键
if(temp [binName] .hasOwnProperty([ seriesName])){
resultRow [seriesName] = temp [binName] [seriesName];
}
}
}
result.push(resultRow);
}
return result;
}

调用:

  let stack = d3.stack()。keys(bins.map((d,i)=> {returnseries+ i})) // stack based on series name keys 
let layers = stack(MyCoolHistogram.processBins(bins));
//现在你的图层已准备好进入()到d3选择。

编辑:
我注意到匿名函数中的堆栈数据第三个参数似乎是元素数组。也就是说它不再是堆栈层索引。例如,当并排分组条时: http://bl.ocks.org/mbostock/3943967



这会打断依赖此索引号计算x位置的分组函数:

  rect.attr(x,(d,i,j)=> {return x(d.data.bin)+ j * barWidth / numberOfSeries} 

我想这说明Mike的想法仍然使用v3,尽管在v4发布后更新很久。 / p>

要获取图层索引,您必须直接使用 layer.index 属性。因此,当分组时,您将翻译整个图层(当然,这会扭动逐条动画,当然...叹息)。

  let layers = d3.stack(yourData); 
let layer = this.svg.selectAll(。layer)
.data(layers)
layer.transition()
.attr(transform,d => ; {returntranslate(+ d.index * barWidth / numberOfSeries +,0);});


The docs for d3's stacking function d3.stack show an example with an array of objects (each json object representing the ensemble of points for whatever the x-axis is measuring). Eg:

var data = [
  {month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960},
  {month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 720}
]

I'm trying to produce a stacked histogram with a matrix of data series ([ [], [], [], etc ]). It's easy enough to iterate through the rows and get a series of histogram bins (having pre-defined the x scale and domain elsewhere):

for(let i=0; i<data.length; i++){
    bins[i] = d3.histogram()
            .domain(x.domain())
            .thresholds(x.ticks(10))
            (data[i]);
    }

And create groups for each data series inside another loop:

let bars = this.svg.selectAll(".series" + i)
    .data(this.bins[i])
    .enter().append("g")
    .classed("series" + i, true)

But of course doing it like that I get stuck here. How am I supposed to bars.append("rect") at the correct x,y coords for that particular series? Stated differently, I have a really useful array of bins at the moment, looking something like:

[
  [[1,2,3,3], [5,8,9], [10], ... etc], //series0 grouping by bins of 5
  [[1,3], [7,7,9,9], [11], ... etc], //series1
  [[2,3,3], [8,9], [10,12], ... etc], //series2
  ...etc
]

Is there a way to invoke stack without munging all the data into json key,value pairs?

解决方案

I took a glance at the source and no comments + single char variables = me understanding that it's not going to happen without munging. I present therefore my shoddy attempt at saving someone else some time:

/*
 * Static helper method to transform an array of histogram bins into an array of objects
 * suitable for feeding into the d3.stack() function.
 * Args:
 *  bins (array): an array of d3 histogram bins
 */
static processBins(bins){
    let temp = {}; // the keys for temp will be the bin name (i.e. the bin delimiter value)
    // now create an object with a key for each bin, and an empty object as a placeholder for the data
    bins[0].map( (bin) => { temp[bin.x0] = {}});
    for(let i=0; i<bins.length; i++){
        //traverse each series
        bins[i].map( bin => { 
            temp[bin.x0]["series"+i] = bin.length; //push the frequency counts for each series
        });
    }
    /* now we have an object whose top-level keys are the bins:
    { 
        binName0: { series0: freqCount0, series1: freqCount1, ...},
        binName1: {...},
        ...
    }
    now, finally we're going to make an arrays of objects containing all the series' freqencies for that bin
    */
    let result = [];
    for(let binName in temp){                       // iterate through the bin objects
        let resultRow = {};
        if(temp.hasOwnProperty(binName)){
            resultRow["bin"] = binName;             //put the bin name key/value pair into the result row
            for(let seriesName in temp[binName]){   //iterate through the series keys
                if(temp[binName].hasOwnProperty([seriesName])){
                    resultRow[seriesName] = temp[binName][seriesName];
                }
            }
        }
        result.push(resultRow);
    }
    return result;
}

Call like:

let stack = d3.stack().keys( bins.map( (d,i)=>{return "series"+i})); //stack based on series name keys
let layers = stack(MyCoolHistogram.processBins(bins));
//and now your layers are ready to enter() into a d3 selection.

Edit: I note that the stack data third argument in anonymous functions seems to be the array of elements. I.e. it's no longer the stack layer index. Eg, when grouping bars side-by-side: http://bl.ocks.org/mbostock/3943967

This breaks grouping functions that rely on this index number to calculate the x position:

rect.attr("x", (d,i,j) => { return x(d.data.bin) + j*barWidth/numberOfSeries});

I guess it's telling that Mike's gist still uses v3, despite being updated long after v4 came out.

To get the layer index you have to use the layer.index attribute directly. So when grouping you would translate the entire layer (which screws up bar-by-bar animations, of course... sigh).

let layers = d3.stack(yourData);
let layer = this.svg.selectAll(".layer")
            .data(layers)
layer.transition()
    .attr("transform", d => { return "translate(" + d.index*barWidth/numberOfSeries + ",0)"; });

这篇关于在d3中堆叠矩阵,而不重新映射到json的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆