javascriptjsond3.jsstream-graph

right JSON format structure for Streamgraph example D3


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?


Solution

  • 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>