avoid enumerating group names in d3.layout.stack()[“group 1”, “group 2”]

六眼飞鱼酱① 提交于 2020-05-17 06:49:12

问题


I am having a really hard time understanding d3.layout.stack() when groups are not listed manually. In the below example, similar to what I've found in other questions, groups are listed in [] as "Apple", etc., but as far as I understand this has to be inputted manually. I am seeking a way to not have to manually input "Apple", "Blueberry", etc.

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

I've tried inserting a line in my data object as below, called 'names':

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

Is there a way to code something similar to below?

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

Should I be focused more on inserting a unique list of names into my data object, or do so by parsing my data in my d3 code itself to accumulate a list of unique group names?

I am wondering, if the d3.keys logic makes sense, if it can be applied to the below context too, instead of enumerating each case:

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"

              }
              }
            });

回答1:


I ended up just converting the entire graph to d3 v5. Below is some notes based off a lot of sources I looked at mixed with my own work:

Better practice for stacked bar is to use

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

where

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

or in other words:

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

This is useful for data that is pre-parsed in javascript. Say you have just columns in a csv:

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

is useful, but same philosophy for stacking applies.

Slight issue: if you have new categories arising later on in the data, i.e. a new fruit pineapple is part of data[1] but not data[0], key will not identify pineapple. It only responds to the data object of the first entry.

To not rely on data[1], data[0], etc., and "accumulate" keys for data[0], data[1], etc. while maintaining the same filter:

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

There's most likely a better way of writing that code, but the explanation is:

If you wrote something like this you'd get keys for only one set of data:

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

If you wrote this you get the keys for each iteration of data:

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

So the trick is to use a for loop to get each iteration of data but concat that into one singular list, which has no repeats.

[EDIT] What if you wanted the value given to each key? Again, this is for a data structure like this, which is a little unconventional:

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

You can use the below, where key will return ["Apple"], and key_values will return [1, 2]. Basically the filter d > 0 prevents any strings, so like names of orchards "xx". Does the same as filtering out orchards.

  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) About the legend..

I just replaced my code with, and I know d3v5 can simplify the below(?),

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

In my case input variables are discrete, like "Apple" etc., not integers, so we use scaleOrdinal and just use "keys" for the domain. You don't really need to write that though, I think it defaults to the input list of discrete variables? Not sure why it works.

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


来源:https://stackoverflow.com/questions/61088270/avoid-enumerating-group-names-in-d3-layout-stackgroup-1-group-2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!