2
votes

Hi i'm new to D3 and I am trying convert the Normalized Stacked Bar Chart to Stacked bar chart. I have common dropdown to switch between both charts(Normalized stacked bar chart and Stacked bar chart). Is it simple to switch between Normalized stacked bar chart and Stacked bar chart or we need to any major changes?

I have created a plunker for it.

  var svg = d3.select("svg"),
    margin = {
      top: 20,
      right: 60,
      bottom: 30,
      left: 40
    },
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var y = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.1)
    .align(0.1);

  var x = d3.scaleLinear()
    .rangeRound([height, 0]);

  var z = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", '#02CA22','#FB5652','#FFB005']);

  var stack = d3.stack()
    .offset(d3.stackOffsetExpand);

  d3.csv("data.csv", type, function(error, data) {
    if (error) throw error;

    data.sort(function(a, b) {
      return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total;
    });

    y.domain(data.map(function(d) {
      return d.State;
    }));
    z.domain(data.columns.slice(1));

    var serie = g.selectAll(".serie")
      .data(stack.keys(data.columns.slice(1))(data))
      .enter().append("g")
      .attr("class", "serie")
      .attr("fill", function(d) {
        return z(d.key);
      });

    serie.selectAll("rect")
      .data(function(d) {
        return d;
      })
      .enter().append("rect")
      .attr("y", function(d) {
        return y(d.data.State);
      })
      .attr("x", function(d) {
        return x(d[1]);
      })
      .attr("width", function(d) {
        return x(d[0]) - x(d[1]);
      })
      .attr("height", y.bandwidth());

    g.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(10, "%"));

    g.append("g")
      .attr("class", "axis axis--y")
      .call(d3.axisLeft(y));

    var legend = serie.append("g")
      .attr("class", "legend")
      .attr("transform", function(d) {
        var d = d[0];
        return "translate(" +  ((x(d[0]) + x(d[1])) / 2) + ", " +(y(d.data.State) - y.bandwidth())+ ")";
      });

    legend.append("line")
      .attr("y1", 5)
      .attr("x1", 15)
      .attr("x2", 15)
      .attr("y2", 12)
      .attr("stroke", "#000");

    legend.append("text")
      .attr("x", 9)
      .attr("dy", "0.35em")
      .attr("fill", "#000")
      .style("font", "10px sans-serif")
      .text(function(d) {
        return d.key;
      });
  });

  function type(d, i, columns) {
    for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
    d.total = t;
    return d;
  }
1

1 Answers

3
votes

No, it's not simple. You have to do some half-dozen changes (just like changing a horizontal bar chart to a vertical bar chart).

This is the list of necessary changes:

  1. Remove .offset(d3.stackOffsetExpand). According to the API:

Applies a zero baseline and normalizes the values for each point such that the topline is always one.

So, your stack function should be:

var stack = d3.stack()
    .offset(d3.stackOffsetNone);

Or you can simply drop the .offset.

  1. Change your sorting from this:

    data.sort(function(a, b) {
        return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total;
    });
    

To this:

    data.sort(function(a, b) { return b.total - a.total; });
  1. Set the domain for the x scale:

    x.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
    
  2. Invert the range of the x scale:

    var x = d3.scaleLinear()
        .rangeRound([0, width]);
    
  3. Change the x and width of the rects:

    .attr("x", function(d) {
        return x(d[0]);
     })
     .attr("width", function(d) {
         return x(d[1]) - x(d[0]);
     })
    

Here is the plunkr: http://plnkr.co/edit/Sa3FqmYk5KkQRnSNM2Wf?p=preview