Skip to content Skip to sidebar Skip to footer

Using JSON In D3v4 Stacked Bar Chart

I have found a d3v3 stacked bar chart example that I would like to use because it uses json data. There is also a d3v4 canonical bar chart example that loads and uses a csv. I want

Solution 1:

Given the choice between upgrading a v3 example that suits your data source type and modifying the v4 example to take json rather than csv data, the choice to convert the existing canonical v4 example should win.

d3.csv converts csv files to json. d3.csv creates json that looks just like your json from a source csv that has headers equal to your data items' properties. So both examples essentially use the same data format and structure. This is why using the d3v4 example is more straightforward.

To use your json data rather than the csv data in the v4 example you'll need to make two changes:

  1. Getting the right column data:

The columns from the canonical uses var keys = data.columns.slice(1); to get which columns in the csv data should be plotted with rectangles. columns is an attribute added to the data array, by d3.csv, that specifies the column headers. The removed value isn't plotted with rectangles, but idenifies the stack, it could be a stack label and used for x axis placement. As the columns property is added by d3.csv we need a slightly different approach.

In your case it looks like we want to get males, females, unknown from the data and your structure for each group looks like:

{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
}

So we can get the keys/properties (that will be plotted with rectangles) with a slight modification:

var columns = d3.keys(data[0]);  // get the properties of the first item in the data array
var keys = columns.slice(2,5); // extract keys with index 2,3,4. These will be the properties that are represented by rectangles in the chart.
  1. Accounting for multiple groups/stacks with the same name

As the scale for most examples will use the group name, these won't work. Instead we need something unique for each group, an index can work just fine:

x.domain(data.map(function(d,i) { return i; }));

You'll need to format the ticks a bit so you don't get the index as the label, lets say:

d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName })

It should be easy enough to add the category to the tick with that.

  1. Modify the total attribute

Yes I said two steps, this is too short to warrant a whole bullet, but lists are better with three items. The original canonical uses d.total, your data uses d.count, this is used to determine the y scale's domain.

Altogether:

<!DOCTYPE html>
<style>

.axis .domain {
  display: none;
}

</style>
<svg width="600" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, 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 x = d3.scaleBand()
    .rangeRound([0, width])
    .paddingInner(0.05)
    .align(0.1);

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

var z = d3.scaleOrdinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

  var data = [{
     "hospitalName": "hospital1",
     "category": "Injury & Poisoning",        
     "Females": "0",
     "Males": "4",
     "Unknown": "0",
     "count": "4"
},
{
    "hospitalName": "hospital1",
    "category": "Symptoms, Signs, & Ill-Defined Conditions",
    "Females": "1",
    "Males": "1",
    "Unknown": "0",
    "count": "2"
},
{
    "hospitalName": "hospital2",
    "category": "Mental Disorders",
    "Females": "0",
    "Males": "1",
    "Unknown": "0",
    "count": "1"
}]
	
	var columns = d3.keys(data[0]);

  var keys = columns.slice(2,5);

  data.sort(function(a, b) { return b.total - a.total; });
  x.domain(data.map(function(d,i) { return i; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]).nice();
  z.domain(keys);

  g.append("g")
    .selectAll("g")
    .data(d3.stack().keys(keys)(data))
    .enter().append("g")
      .attr("fill", function(d) { return z(d.key); })
    .selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d,i) { return x(i); })
      .attr("y", function(d) { return y(d[1]); })
      .attr("height", function(d) { return y(d[0]) - y(d[1]); })
      .attr("width", x.bandwidth());

  g.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).tickFormat(function(d,i) { return data[i].hospitalName}));

  g.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y).ticks(null, "s"))
    .append("text")
      .attr("x", 2)
      .attr("y", y(y.ticks().pop()) + 0.5)
      .attr("dy", "0.32em")
      .attr("fill", "#000")
      .attr("font-weight", "bold")
      .attr("text-anchor", "start")
      .text("Population");

  var legend = g.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("text-anchor", "end")
    .selectAll("g")
    .data(keys.slice().reverse())
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 19)
      .attr("width", 19)
      .attr("height", 19)
      .attr("fill", z);

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9.5)
      .attr("dy", "0.32em")
      .text(function(d) { return d; });


</script>

If you want to use d3.json, then you can use:

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

  // Parts that use the data here.

})

Post a Comment for "Using JSON In D3v4 Stacked Bar Chart"