【问题标题】:Stack a matrix in d3 without remapping to json在 d3 中堆叠矩阵而不重新映射到 json
【发布时间】:2016-10-12 11:39:19
【问题描述】:

The docs 用于 d3 的堆叠功能 d3.stack 显示了一个包含对象数组的示例(每个 json 对象代表 x 轴测量的点的集合)。例如:

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

我正在尝试使用数据系列矩阵 ([ [], [], [], etc ]) 生成堆叠直方图。遍历行并获得一系列直方图箱很容易(在其他地方预定义了 x 比例和域):

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

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

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

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

[
  [[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
]

有没有办法调用stack 而无需将所有数据转换成 json 键值对?

【问题讨论】:

    标签: javascript json d3.js


    【解决方案1】:

    我看了the source 一眼,没有 cmets + 单个 char 变量 = 我明白如果不修改就不会发生。因此,我提出了我为节省别人一些时间而做出的粗制滥造的尝试:

    /*
     * 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;
    }
    

    调用方式:

    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.
    

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

    这会破坏依赖此索引号来计算 x 位置的分组函数:

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

    我想这说明 Mike 的 gist 仍然使用 v3,尽管在 v4 发布很久之后就进行了更新。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-13
      • 2016-12-21
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多