首页 > 解决方案 > 避免在 d3.layout.stack()["group 1", "group 2"] 中枚举组名

问题描述

当未手动列出组时,我很难理解 d3.layout.stack() 。在下面的示例中,与我在其他问题中发现的类似,组在 [] 中列为“Apple”等,但据我了解,这必须手动输入。我正在寻找一种不必手动输入“Apple”、“Blueberry”等的方法。

var dataset = d3.layout.stack()(["Apple", "Blueberry", "Lettuce", "Orange"].map(function(fruit) {
      return data.map(function(d) {
        return {x: d.orchard, y: +d[fruit]};
      });
    }));

我尝试在我的数据对象中插入一行,如下所示,称为“名称”:

[{names='Apple','Blueberry','Lettuce','Orange'}, {Apple=1.0, Orange=2.0, Lettuce=1.0, orchard=小明, Blueberry=1.0}, {Apple=1.0, Orange=1.0, Lettuce=1.0, orchard=小陈, Blueberry=1.0}, {Apple=1.0, Orange=1.0, Lettuce=1.0, orchard=小虎, Blueberry=1.0}, {Orange=1.0, Lettuce=1.0, orchard=小桃, Blueberry=1.0, Apple=1.0}]

有没有办法编写类似于下面的代码?

var dataset = d3.layout.stack()([d3.keys(names)].map(function(fruit) {

我应该更专注于将唯一的名称列表插入到我的数据对象中,还是通过在我的 d3 代码本身中解析我的数据来累积唯一组名列表来做到这一点?

我想知道,如果 d3.keys 逻辑有意义,它是否也可以应用于以下上下文,而不是枚举每个案例:

legend.append("text")
         .attr("x", width + 5)
          .attr("y", 9)
          .attr("dy", ".35em")
          .style("text-anchor", "start")
              .text(function(d, i) {
                for(var j =0; j<4; j++){
                   switch (i) {

                      case j: return d3.keys[j]

//                switch (i) {
//            
//                  case 0: return "orange"
//                  case 1: return "apple"
//                  case 2: return "blueberry"
//                  case 3: return "lettuce"

              }
              }
            });

标签: d3.js

解决方案


我最终只是将整个图表转换为 d3 v5。下面是一些笔记,这些笔记基于我查看的大量资源以及我自己的工作:

堆叠条的更好做法是使用

  .data(d3.stack().keys(keys)(data))

在哪里

 var keys = d3.keys(data[0]).filter(function(d){
          return d != "orchard";
        });         

或者换句话说:

   var keys = d3.keys(data[0]).filter(d => d != "orchard")

这对于在 javascript 中预先解析的数据很有用。假设您在 csv 中只有列:

var keys = csv.columns.slice(0);

很有用,但同样适用于堆叠的理念。

小问题:如果数据中稍后出现新的类别,即新的水果菠萝是 data[1] 的一部分但不是 data[0] 的一部分,则 key 将无法识别菠萝。它只响应第一个条目的数据对象。

在保持相同过滤器的同时不依赖data[1], data[0], etc.,和“累积”密钥:data[0], data[1], etc.

  var key = [];
  for(var i =0; i < d3.keys(data).length; i++){
     var joinin = d3.keys(data[i]).filter(d => d != "orchard")
     var key = key.concat(joinin)
//     console.log(key)  
 }

编写该代码很可能有更好的方法,但解释是:

如果你写了这样的东西,你只会得到一组数据的键:

  var key = d3.keys(data[2]).filter(function(d){
          return d != "orchard";
        }); 

如果你写了这个,你会得到每次数据迭代的键:

  var key = [];
  for(var i =0; i < d3.keys(data).length; i++){
     var key = d3.keys(data[i]).filter(d => d != "orchard")
     console.log(key)
     key.push(key);
  }

所以诀窍是使用 for 循环来获取数据的每次迭代,但将其连接到一个单一的列表中,该列表没有重复。

[编辑] 如果你想要赋予每个键的值怎么办?同样,这是针对这样的数据结构,这有点不合常规:

  data = [ 
              {0:
                {"Apple": 1}
                {"orchard": xx}
              }
              {1:
                {"Apple": 2}
                {"orchard": xx}
              }
           ]

您可以使用下面的 where keywill return["Apple"]key_valueswill return [1, 2]。基本上,过滤器 d > 0 会阻止任何字符串,例如果园名称“xx”。与过滤掉果园一样。

  var key = [];
  var key_values = [];
  for(var i =0; i < d3.keys(data).length; i++){
     var key_value = d3.entries(data[i]).map(d => d.value).filter(d => d > 0)
     var key_values = key_values.concat(key_value)
     var joinin = d3.keys(data[i]).filter(d => d != "orchard")
     var key = key.concat(joinin)
 }

(EDIT2)关于传说..

我刚刚替换了我的代码,我知道 d3v5 可以简化以下(?),

 var legend = svg.selectAll(".legend")
  .data(color.domain())
  .enter()
  .append("g")
  .attr("class","legend")
  .attr("transform",function(d,i) {
    return "translate(1300," + i * 15 + ")";
  });

在我的例子中,输入变量是离散的,比如“Apple”等,而不是整数,所以我们使用 scaleOrdinal 并且只使用域的“keys”。你真的不需要写那个,我认为它默认为离散变量的输入列表?不知道为什么它有效。

  var color =  d3.scaleOrdinal()
       .domain(keys)
    // .range (whatever you want)

推荐阅读