0

I was trying to do some examples with this streamgraph layout of D3.js. My data has this format. So the my object has objects with the name of the country andy in those objects i have several arrays with different values per month and the date for each month ("Datum")

{
"Irak": {
    "Asylberechtigt": [
        65, 
        60, 
        54, 
        47, 
        47, 
        30, 
        25, 
        21, 
        12, 
        6
    ], 
    "EntscheidungenInsgesamt": [
        8645, 
        7559, 
        6533, 
        5425, 
        4351, 
        3336, 
        2643, 
        2022, 
        1270, 
        645
    ], 
    "InsgesamtMonat": [
        1086, 
        1026, 
        1108, 
        1074, 
        1015, 
        693, 
        621, 
        752, 
        625, 
        645
    ], 
    "Datum": [
        "2015-10-01", 
        "2015-09-01", 
        "2015-08-01", 
        "2015-07-01", 
        "2015-06-01", 
        "2015-05-01", 
        "2015-04-01", 
        "2015-03-01", 
        "2015-02-01", 
        "2015-01-01"
    ]
}, 
"Mazedonien": {
    "Asylberechtigt": [
        0, 
        0, 
        0, 
        0, 
        0, 
        0, 
        0, 
        0, 
        0, 
        0
    ], 
    "EntscheidungenInsgesamt": [
        4734, 
        4091, 
        3527, 
        3268, 
        2715, 
        2238, 
        1923, 
        1489, 
        1094, 
        604
    ], 
    "InsgesamtMonat": [
        643, 
        564, 
        259, 
        553, 
        477, 
        315, 
        434, 
        395, 
        490, 
        604
    ], 
    "Datum": [
        "2015-10-01", 
        "2015-09-01", 
        "2015-08-01", 
        "2015-07-01", 
        "2015-06-01", 
        "2015-05-01", 
        "2015-04-01", 
        "2015-03-01", 
        "2015-02-01", 
        "2015-01-01"
    ]
}
} 

For the streamgraph i need .values and I understood that it has to return some x and y values I'm accessing with this function (0 is Irak for example but in the end i have to iterate through all countries). I think this is no permanent solution. But what should my data look like in general because I realized that nesting is pretty confusing for me. So What structure is required so i can use the functions to visualize the streamgraph?

    var stack = d3.layout.stack()
        .offset("wiggle")
        .values(function(d){
                for (var i = 0; i < d[countries[0]].Datum.length; i++) {
                    var dx = d[countries[0]].Datum[i];
                    var dy = d[countries[0]].InsgesamtMonat[i];
                    //console.log("Countries with InsgesamtMonat "+ countries[i]+" "+test);
                    dStack[i] = { x: dx, y: dy};
                }
                return dStack;
            });
    //.values(function(d) { return d[countries[0]].values; })
  .x(function(d) { return d[countries[0]].Datum; })
  .y(function(d) { return d[countries[0]].InsgesamtMonat; });

Do I still need the .x and the .y ?

Here is my full code

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Testing Streamgraph</title>
 <link rel="stylesheet" href="main.css">
 <script  type="text/javascript" src="d3.min.js"></script>
</head>
<body>
<!--Place all DOM elements here -->
<script>

d3.json("data.json", function(error, data){

    console.log(data)
  plot(data);

  //console.log(data);
})

function plot(data) {
  var dStack = [];

    var w = 800;
    var h = 450;
    var margin = {
        top: 100,
        bottom: 0,
        left: 80,
        right: 40
    };
    var width = w - margin.left - margin.right;
    var height = h - margin.top - margin.bottom;

    var svg = d3.select("body").append("svg")
                .attr("id", "chart")
                .attr("width", w)
                .attr("height", h)
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var dateParser = d3.time.format("%Y-%m-%d").parse;
  var colorScale = d3.scale.category20();

  var n = Object.keys(data).length;
    var m = 10 // number of samples per layer

  //console.log(data[Object.keys("Asylberechtigt")[13]])
  //var test = Object.keys(data).Asylberechtigt;
  var countries = Object.keys(data);
  console.log(countries);
var stack = d3.layout.stack()
  .offset("wiggle")
  .values(function(d){
    for (var i = 0; i < d[countries[0]].Datum.length; i++) {
     var dx = d[countries[0]].Datum[i];
     var dy = d[countries[0]].InsgesamtMonat[i];
     //console.log("Countries with InsgesamtMonat "+ countries[i]+" "+test);
     dStack[i] = { x: dx, y: dy};
    }
    return dStack;
   });
 //.values(function(d) { return d[countries[0]].values; })
  .x(function(d) { return d[countries[0]].Datum; })
  .y(function(d) { return d[countries[0]].InsgesamtMonat; });

var nest = d3.nest()
    .key(function(d) { return d.countries });


  console.log(nest);

  var area = d3.svg.area()
      .interpolate("cardinal")
      .x(function(d){
     var xStack = []
     for (var i = 0; i < d[countries[0]].Datum.length; i++) {
      var dx = d[countries[0]].Datum[i];
      xStack[i] = dx;
     }
     return xStack })
      .y0(function(d) { return y(d.y0); })
      .y1(function(d) { return y(d.y0 + d.y); });

  var layers = stack(nest.countries(data)); //Do i need to nest again because i already have the Countries as objects


    var x = d3.time.scale()
             .domain(d3.extent(data, function(d, i){
             var date = dateParser(Object.keys(d).Datum(i)); // Do't know if this is legit either
       // date= 0;
       return date;
            }))
            .range([0+margin.left,width]);
   var y = d3.scale.linear()
            .domain([0, d3.max(data, function(d){
                return d.value;
            })])
            .range([height,0+margin.top]);

  var xAxis = d3.svg.axis()
     .scale(x)
     .orient("bottom");
  var yAxis = d3.svg.axis()
     .scale(y)
     .orient("left");

    //enter()
    svg.selectAll(".point")
                .data(layers)
                .enter()


}
</script>
</body>
</html>

I don't get how to declare the layer for my data exactly can you also help me with that?

basedian
  • 307
  • 1
  • 5
  • 18
  • There are lots of streamgraph examples (e.g. [here](http://bl.ocks.org/WillTurman/4631136)). Did you have a look at the data they use? – Lars Kotthoff Dec 18 '15 at 17:21
  • I closed your duplicate question that was the same as this from a couple days ago and I answered your `csv` question which is again very similar to this just a few minutes ago. To answer this question is a bit difficult. First, a stream graph "stacks" by one variable. In your case I'm assuming it's country, but then what are we to do with "Asylberechtigt", "EntscheidungenInsgesamt", etc... – Mark Dec 18 '15 at 18:00
  • Second, you don't a strong understanding how how `d3`'s data [accessors](https://github.com/mbostock/d3/wiki/Stack-Layout#values) work. They are not meant to manipulate the data directly but to tell `d3` what properties hold the data of interest. Third, your json format is a mess. I would fix it at whatever is generating the json (server-side, I assume?). You want this `[{name: country1, values:[{date: '1/1/10', value: 3535},{date: '1/2/10', value: 6635}]},{name: country2, values[{ ` etc.... If you must fix in JavaScript it can be done, but again, you have too many "stack" variables. – Mark Dec 18 '15 at 18:01

1 Answers1

1

I double-backed to this question, mostly out of boredom, but I think I was a bit to harsh in my comments. As I said, your data has too many "by" variables, so let's assume that we want to stream graph by country with an optional filter of "InsgesamtMonat", "EntscheidungenInsgesamt", etc... So the question becomes how do we get this data, in JavaScript, into a format that d3.layout.stack will understand?

  // give a stack defined as...
  var stack = d3.layout.stack()
    .offset("wiggle")
    .values(function(d) {
      return d.values;
    });

  // convert data
  // varToStackOn is "EntscheidungenInsgesamt"
  var properData = [];
  for (country in json) {
    var obj = {};
    obj.name = country;
    obj.values = [];
    json[country][varToStackOn].forEach(function(d, i) {
      obj.values.push({
        x: format.parse(json[country]["Datum"][i]),
        y: d
      });
    })
    properData.push(obj);
  }

  // and stack it
  var stackedData = stack(properData);

Once this is done, following the examples becomes easy.

Here it all is all together:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {
    font: 10px sans-serif;
  }
  
  .chart {
    background: #fff;
  }
  
  p {
    font: 12px helvetica;
  }
  
  .axis path,
  .axis line {
    fill: none;
    stroke: #000;
    stroke-width: 2px;
    shape-rendering: crispEdges;
  }
  
  button {
    position: absolute;
    right: 50px;
    top: 10px;
  }
</style>

<body>
  <script src="http://d3js.org/d3.v3.js"></script>


  <div class="chart">
  </div>


  <script>
    var json = {
      "Irak": {
        "Asylberechtigt": [65, 60, 54, 47, 47, 30, 25, 21, 12, 6],
        "EntscheidungenInsgesamt": [8645, 7559, 6533, 5425, 4351, 3336, 2643, 2022, 1270, 645],
        "InsgesamtMonat": [1086, 1026, 1108, 1074, 1015, 693, 621, 752, 625, 645],
        "Datum": ["2015-10-01", "2015-09-01", "2015-08-01", "2015-07-01", "2015-06-01", "2015-05-01", "2015-04-01", "2015-03-01", "2015-02-01", "2015-01-01"]
      },
      "Mazedonien": {
        "Asylberechtigt": [50, 20, 10, 14, 10, 6, 18, 32, 30, 12],
        "EntscheidungenInsgesamt": [4734, 4091, 3527, 3268, 2715, 2238, 1923, 1489, 1094, 604],
        "InsgesamtMonat": [643, 564, 259, 553, 477, 315, 434, 395, 490, 604],
        "Datum": ["2015-10-01", "2015-09-01", "2015-08-01", "2015-07-01", "2015-06-01", "2015-05-01", "2015-04-01", "2015-03-01",
          "2015-02-01", "2015-01-01"
        ]
      }
    }

    var format = d3.time.format("%Y-%d-%m");

    var stack = d3.layout.stack()
      .offset("wiggle")
      .values(function(d) {
        return d.values;
      });

    var width = 300,
      height = 300;

    var x = d3.time.scale()
      .range([0, width])
      .domain(d3.extent(["2015-10-01", "2015-09-01", "2015-08-01", "2015-07-01", "2015-06-01", "2015-05-01", "2015-04-01", "2015-03-01", "2015-02-01", "2015-01-01"], function(d) {
        return format.parse(d);
      }));

    var y = d3.scale.linear()
      .range([height, 0]);

    var color = d3.scale.category10()

    var area = d3.svg.area()
      .x(function(d) {
        return x(d.x);
      })
      .y0(function(d) {
        return y(d.y0);
      })
      .y1(function(d) {
        return y(d.y0 + d.y);
      });

    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);
      
    var stackOn = "InsgesamtMonat";
    setInterval(function(){
      if (stackOn === "InsgesamtMonat")
        stackOn = "EntscheidungenInsgesamt";
      else if (stackOn === "Asylberechtigt")
        stackOn = "InsgesamtMonat";
      else if (stackOn === "EntscheidungenInsgesamt")
        stackOn = "Asylberechtigt";
  
      updateGraph(stackOn);
    }, 1000);

    function updateGraph(varToStackOn) {

      var properData = [];
      for (country in json) {
        var obj = {};
        obj.name = country;
        obj.values = [];
        json[country][varToStackOn].forEach(function(d, i) {
          obj.values.push({
            x: format.parse(json[country]["Datum"][i]),
            y: d
          });
        })
        properData.push(obj);
      }
      
      var stackedData = stack(properData);

      y.domain([0, d3.max(stackedData, function(layer) {
        return d3.max(layer.values, function(d) {
          return d.y0 + d.y;
        });
      })]);

      var paths = svg.selectAll("path")
        .data(stack(properData))
      
      paths
        .enter().append("path")
        .style("fill", function(d, i) {
          return color(i);
        });
      
      paths.transition()
        .attr("d", function(d) {
          return area(d.values);
        });
    }
  </script>
</body>

</html>
Mark
  • 106,305
  • 20
  • 172
  • 230