Search code examples
d3.jschartslinechartdc.jsstacked-area-chart

Make a chart with area charts having both positive and negative y axis in dc or d3.js


I want to make a chart like that shown in the image below.

Chart Sample

By the looks of it, it looks like a composite chart, having two area charts added on top of it OR a stacked area chart. I am having difficulty in drawing this chart. When I tried with drawing stacked area chart with one stack having negative values. I got a graph where values were plotted from top to bottom but not from below the x axis. I also tried with an example of d3 streamgraph, by tweaking values of y0 and y1, but could not get this effect. I am new to D3, please help me in this regard.


Solution

  • I think this is what you want, but remember, this will work only for two series and you have to calculate y domain dynamically.

    var margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = 400 - margin.left - margin.right,
      height = 200 - margin.top - margin.bottom;
    
    var parseDate = d3.time.format("%y-%b-%d").parse,
      formatPercent = d3.format(".0%");
    
    var x = d3.time.scale()
      .range([0, width]);
    
    var y = d3.scale.linear()
      .range([height, 0]);
    
    var color = d3.scale.category20();
    
    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");
    
    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");
    
    var area = d3.svg.area()
      .x(function(d) {
        return x(d.date);
      })
      .y0(function(d) {
        return y(d.y0);
      })
      .y1(function(d) {
        return y(d.y0 + d.y);
      });
    
    var stack = d3.layout.stack()
      .values(function(d) {
        return d.values;
      });
    
    var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var data = [{
      "date": "11-Oct-13",
      "A": 41.62,
      "B": -22.36
    }, {
      "date": "11-Oct-14",
      "A": 41.95,
      "B": -22.15
    }, {
      "date": "11-Oct-15",
      "A": 37.64,
      "B": -10.77
    }, {
      "date": "11-Oct-16",
      "A": 37.27,
      "B": -24.65
    }, {
      "date": "11-Oct-17",
      "A": 42.74,
      "B": -21.87
    }];
    
    y.domain([-24.77, 42.74]);
    color.domain(d3.keys(data[0]).filter(function(key) {
      return key !== "date";
    }));
    
    data.forEach(function(d) {
      d.date = parseDate(d.date);
    });
    
    var browsers = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            date: d.date,
            y: d[name],
            y0: 0
          };
        })
      };
    });
    
    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));
    
    var vars = svg.selectAll(".vars")
      .data(browsers)
      .enter().append("g")
      .attr("class", "vars");
    
    vars.append("path")
      .attr("class", "area")
      .attr("d", function(d) {
        return area(d.values);
      })
      .style("fill", function(d) {
        return color(d.name);
      });
    
    
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
    
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>