block by pbogden 6510796

bar chart

Full Screen

index.html: grouped-to-stacked of quakes.json (using minimized js) index0.html: simple histogram of quakes.json index1.html: grouped-to-stacked of quakes.json

stacked.html: demo – stacked bar chart grouped.html: demo – grouped-to-stacked bar chart transition hist.html: demo – from Mike Bostock’s tutorial

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>bar-chart</title>
<style>

body {
  font: 10px sans-serif;
}

.bar rect {
  fill: steelblue;
  shape-rendering: crispEdges;
}

.bar text {
  fill: #fff;
}

.axis path, .axis line {
  fill: none;
  stroke: #000;
  stroke-width: 1px;
}

form {
  position: relative;
  left: 350px;
  top: 20px;
  font: 20px sans-serif;
}

</style>
<body>
<form>
  <label><input type="radio" name="mode" value="grouped"> Grouped</label>
  <label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script src="../d3/d3.min.js"></script>
<script src="my.min.js"></script>

Makefile

SOURCE_FILES = d3.min.js grouped2stacked.js grouped.js
GENERATED_JS_FILE = my.min.js

all: $(GENERATED_JS_FILE)

.PHONY: clean all

$(GENERATED_JS_FILE): $(SOURCE_FILES)
	@rm -f $@
	@cat $^ > tmp.js
	@uglify tmp.js > $@
	@rm -f tmp.js

clean:
	rm -- $(GENERATED_JS_FILE)

grouped.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>stacked-to-grouped</title>
<style>

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: auto;
  position: relative;
  width: 960px;
}

text {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

form {
  position: absolute;
  right: 10px;
  top: 10px;
}

</style>
<form>
  <label><input type="radio" name="mode" value="grouped"> Grouped</label>
  <label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script src="d3.min.js"></script>
<script>

/// ORIGINAL
//var n = 4, // number of layers
//    m = 58, // number of samples per layer
//    stack = d3.layout.stack(),
//    layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
// END ORIGINAL

// TESTING
var data = [ [ { "x": 0, "y":  41, "layer": 1}, { "x": 1, "y": 30, "layer": 1} ],
             [ { "x": 0, "y":   9, "layer": 2}, { "x": 1, "y": 50, "layer": 2} ],
             [ { "x": 0, "y":  20, "layer": 3}, { "x": 1, "y": 30, "layer": 3} ] ];
var n = 3, // number of layers
    m = 2, // number of samples per layer
    stack = d3.layout.stack(),
    layers = stack(data),
// END TESTING

    yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
    yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

var margin = {top: 40, right: 10, bottom: 20, left: 10},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .domain(d3.range(m))
    .rangeRoundBands([0, width], .08);

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

var color = d3.scale.linear()
    .domain([0, n - 1])
    .range(["#aad", "#556"]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickSize(0)
    .tickPadding(6)
    .orient("bottom");

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 layer = svg.selectAll(".layer")
    .data(layers)
  .enter().append("g")
    .attr("class", "layer")
    .style("fill", function(d, i) { return color(i); });

var rect = layer.selectAll("rect")
    .data(function(d) { return d; })
  .enter().append("rect")
    .attr("x", function(d) { return x(d.x); })
    .attr("y", height)
    .attr("width", x.rangeBand())
    .attr("height", 0);

rect.transition()
    .delay(function(d, i) { return i * 10; })
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

d3.selectAll("input").on("change", change);

var timeout = setTimeout(function() {
  d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);

function change() {
  clearTimeout(timeout);
  if (this.value === "grouped") transitionGrouped();
  else transitionStacked();
}

function transitionGrouped() {
  y.domain([0, yGroupMax]);

  rect.transition()
      .duration(500)
      .delay(function(d, i) { return i * 10; })
      .attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
      .attr("width", x.rangeBand() / n)
    .transition()
      .attr("y", function(d) { return y(d.y); })
      .attr("height", function(d) { return height - y(d.y); });
}

function transitionStacked() {
  y.domain([0, yStackMax]);

  rect.transition()
      .duration(500)
      .delay(function(d, i) { return i * 10; })
      .attr("y", function(d) { return y(d.y0 + d.y); })
      .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
    .transition()
      .attr("x", function(d) { return x(d.x); })
      .attr("width", x.rangeBand());
}

// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {

  function bump(a) {
    var x = 1 / (.1 + Math.random()),
        y = 2 * Math.random() - .5,
        z = 10 / (.1 + Math.random());
    for (var i = 0; i < n; i++) {
      var w = (i / n - y) * z;
      a[i] += x * Math.exp(-w * w);
    }
  }

  var a = [], i;
  for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
  for (i = 0; i < 5; ++i) bump(a);
  return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
}

</script>

grouped.js

function grouped(data, title) {
  
// TESTING
//  var data = [ [ { "x": 0, "y":  41, "layer": 1}, { "x": 1, "y": 30, "layer": 1} ],
//               [ { "x": 0, "y":   9, "layer": 2}, { "x": 1, "y": 50, "layer": 2} ],
//               [ { "x": 0, "y":  20, "layer": 3}, { "x": 1, "y": 30, "layer": 3} ] ];
//  var n = 3,
//      m = 2,
// END TESTING

  var n = data.length, // number of layers
      m = data[0].length; // number of samples per layer

  var stack = d3.layout.stack(),
      layers = stack(data);
  
  var yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
      yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
  
  var margin = {top: 40, right: 10, bottom: 20, left: 50},
      width = 600 - margin.left - margin.right,
      height = 300 - margin.top - margin.bottom;
  
  var x = d3.scale.ordinal()
      .domain(d3.range(m))
      .rangeRoundBands([0, width], .08);
  
  var y = d3.scale.linear()
      .domain([0, yStackMax])
      .range([height, 0]);
  
  var color = d3.scale.category10();
  
  var xAxis = d3.svg.axis()
      .scale(x)
      .tickSize(5)
      .tickPadding(6)
//      .tickValues(d3.range(5).map(function(d) { return 12*d; }))
//      .tickFormat(function(d,i) { return 2010 + i; })
      .orient("bottom");
  
  var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");
  
  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 layer = svg.selectAll(".layer")
      .data(layers)
    .enter().append("g")
      .attr("class", "layer")
      .style("fill", function(d, i) { return color(i); });
  
  var rect = layer.selectAll("rect")
      .data(function(d) { return d; })
    .enter().append("rect")
      .attr("x", function(d) { return x(d.x); })
      .attr("y", height)
      .attr("width", x.rangeBand())
      .attr("height", 0);
  
  rect.transition()
      .delay(function(d, i) { return i * 10; })
      .attr("y", function(d) { return y(d.y0 + d.y); })
      .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
  
  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
  
  svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(0,0)")
      .call(yAxis);

  svg.append("text")
      .attr("class", "y label") 
      .attr("text-anchor", "end") 
      .attr("x", -6)
      .attr("y", 6)
      .attr("dy", ".75em") 
      .attr("transform", "rotate(-90)") 
      .text("#/month");

  title = (title) ? title : "Earthquakes";
  svg.append("text")
      .attr("class", "x label") 
      .attr("text-anchor", "middle") 
      .attr("x", width/2)
      .attr("y", 0)
      .attr("dy", "+.75em") 
      .style("font-size", "20px")
      .text(title);
  
  d3.selectAll("input").on("change", change);
  
  var timeout = setTimeout(function() {
    d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
  }, 2000);
  
  function change() {
    clearTimeout(timeout);
    if (this.value === "grouped") transitionGrouped();
    else transitionStacked();
  }
  
  function transitionGrouped() {
    y.domain([0, yGroupMax]);
  
    rect.transition()
        .duration(500)
        .delay(function(d, i) { return i * 10; })
        .attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
        .attr("width", x.rangeBand() / n)
      .transition()
        .attr("y", function(d) { return y(d.y); })
        .attr("height", function(d) { return height - y(d.y); });
  }
  
  function transitionStacked() {
    y.domain([0, yStackMax]);
  
    rect.transition()
        .duration(500)
        .delay(function(d, i) { return i * 10; })
        .attr("y", function(d) { return y(d.y0 + d.y); })
        .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
      .transition()
        .attr("x", function(d) { return x(d.x); })
        .attr("width", x.rangeBand());
  }
  
}

grouped2stacked.js

hist.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>bar chart</title>
<script type="text/javascript" src="../d3/d3.min.js"></script>
<style>

.chart {
   font: 24px sans-serif;  /* Note: 1em == font size, default in browsers is 16px */
/*   text-align: right; /* HTML */
   padding: 0px;  /* not sure how to set this */
   margin: 0px;   /* not sure how to set this */
/*   background-color: steelblue;  /* HTML */
/*   color: white;                 /* HTML */
   fill: steelblue; /* SVG */
   stroke: white;   /* SVG */
}

.label {
   stroke: none;   /* SVG */
   fill: white; /* SVG */
}

</style>
</head>

<body>
<script>

var barHeight = 40,
    chartWidth = 960,
    data = [4, 8, 15, 16, 23, 42];

var chart = d3.select("body").append("svg")
    .attr("class", "chart")
    .attr("width", chartWidth)
    .attr("height", barHeight * data.length);

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, chartWidth]);

var y = d3.scale.ordinal()
    .domain(data)
    .rangeRoundBands([0, barHeight*data.length]);

chart.selectAll("rect")
    .data(data)
  .enter().append("rect")
    .attr("y", y)
    .attr("width", x)
    .attr("height", barHeight);

chart.selectAll("text")
    .data(data)
  .enter().append("text")
    .attr("class", "label")
    .attr("x", x)
    .attr("y", function(d) { return y(d) + y.rangeBand() / 2; })
    .attr("text-anchor", "end") // text-align: right
    .attr("dx", "-.35em") // padding-right
    .attr("dy", ".35em") // vertical-align: middle
    .text(String);

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

index0.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>bar chart</title>
<style>

body {
  font: 10px sans-serif;
}

.bar rect {
  fill: steelblue;
  shape-rendering: crispEdges;
}

.bar text {
  fill: #fff;
}

.axis path, .axis line {
  fill: none;
  stroke: #000;
  stroke-width: 1px;
}

</style>
<body>
<script src="../d3/d3.min.js"></script>
<script>

var margin = { top: 50, right: 50, bottom: 50, left: 50 },
    width = 860 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom,
    nbins = 20;

// bounding box for Oklahoma (upper left, lower right)
var ok = [ [ -103.045999, 37.002348 ], [  -94.460820, 33.646149 ] ];

var x = d3.time.scale()
    .range([0, width]);

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

var xAxis = d3.svg.axis()
    .scale(x)
    .ticks(5)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .ticks(5)
    .orient("left");

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 + ")");

d3.json('quakes.json', function(err, quakes) {

  console.log("# of quakes: " + quakes.length);
  console.log(quakes[0]);

//  quakes = quakes.filter(function(d) { return d.mag >= 3.0 });
//  console.log("# after magnitude filter: " + quakes.length);

//  quakes = quakes.filter(function(d) { 
//      return d.coordinates[0] > ok[0][0] && d.coordinates[0] < ok[1][0]
//          && d.coordinates[1] < ok[0][1] && d.coordinates[1] > ok[1][1];
//  });
//  console.log("# after oklahoma filter: " + quakes.length);

  values = quakes.map(function(d) { return d.time });
  magnitudes = quakes.map(function(d) { return d.mag });

  // Print (to console) min & max dates
  var extent = d3.extent(values);
  console.log("extent[0]: ", new Date(extent[0]));
  console.log("extent[1]: ", new Date(extent[1]));
  extent[0] = new Date(new Date(extent[0]).getFullYear(), 0, 0);
  extent[1] = new Date(new Date(extent[1]).getFullYear(), 12, 1);
  x.domain(extent);

  var data = d3.layout.histogram() // histogram
      .bins(x.ticks(nbins))
      (values);

  y.domain([0, d3.max(data, function(d) { return d.y; })])

  var bar = svg.selectAll(".bar")
      .data(data)
    .enter().append("g")
      .attr("class", "bar")
      .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });

  // bars in bar chart
  bar.append("rect")
      .attr("x", 1)
      .attr("width", function(d) { return x(d.x.getTime() + d.dx) - x(d.x) - 1 ; })
      .attr("height", function(d) { return height - y(d.y); });

  // labels in bar chart
  bar.append("text")
      .attr("dy", ".75em")
      .attr("y", 6)
      .attr("x", function(d) { return (x(d.x.getTime() + d.dx) - x(d.x) - 1)/2 ; })
      .attr("text-anchor", "middle")
      .text(function(d) { return d3.format(",.0f")(d.y); });

  // x-axis with tick labels
  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .selectAll("text") // left-aligned ticks
      .attr("y", 6)
      .attr("x", 6)
      .style("text-anchor", "start");

  // y-axis with tick labels
  svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(0," + 0 + ")")
      .call(yAxis)

  // label min & max magnitudes
  var extent = d3.extent(magnitudes);
  console.log("magnitude extent[0]: ", extent[0]);
  console.log("magnitude extent[1]: ", extent[1]);
  svg.append("text")
      .attr("x", width/2)
      .attr("y", '-0.5em')
      .attr("text-anchor", 'middle')
      .style("font-size", '24px')
      .text("Earthquake magnitudes ranging from " + extent[0] + " to " + extent[1]);

});
  
</script>

index1.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>bar chart</title>
<style>

body {
  font: 10px sans-serif;
}

.bar rect {
  fill: steelblue;
  shape-rendering: crispEdges;
}

.bar text {
  fill: #fff;
}

.axis path, .axis line {
  fill: none;
  stroke: #000;
  stroke-width: 1px;
}

form {
  position: relative;
  left: 350px;
  top: 20px;
  font: 20px sans-serif;
}

</style>
<body>
<form>
  <label><input type="radio" name="mode" value="grouped"> Grouped</label>
  <label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script src="../d3/d3.min.js"></script>
<script src="stacked.js"></script>
<script src="grouped.js"></script>
<script>

var margin = { top: 30, right: 50, bottom: 100, left: 50 },
    width = 860 - margin.right - margin.left,
    height = 300 - margin.top - margin.bottom,
    nbins = 60;

// bounding box for Oklahoma (upper left, lower right)
var ok = [ [ -103.045999, 37.002348 ], [  -94.460820, 33.646149 ] ];

var x = d3.time.scale()
    .range([0, width]);

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

d3.json('quakes.json', function(err, quakes) {

  console.log("# of quakes: " + quakes.length);
  console.log(quakes[0]);

  quakes = quakes.filter(function(d) { return d.mag >= 3.0 });
  console.log("# after magnitude filter: " + quakes.length);

  var values = quakes.map(function(d) { return d.time });

  // Oklahoma only
  quakes = quakes.filter(function(d) { 
      return d.coordinates[0] > ok[0][0] && d.coordinates[0] < ok[1][0]
          && d.coordinates[1] < ok[0][1] && d.coordinates[1] > ok[1][1];
  });
  console.log("# after oklahoma filter: " + quakes.length);
  var values2 = quakes.map(function(d) { return d.time });

  var extent = d3.extent(values);
  extent[1] = new Date(new Date(extent[1]).getFullYear(), 12, 1);
  x.domain(extent);

  data = d3.layout.histogram() // histogram
      .bins(x.ticks(nbins))
      (values);

  data1 = d3.layout.histogram() // histogram
      .bins(x.ticks(nbins))
      (values2);

  data = data.map(function(d,i) { return { x:i, y:d.y }; });
  data1 = data1.map(function(d,i) { return { x:i, y:d.y }; });
  data2 = [data, data1];

//  stacked(data2);
  grouped(data2);

});

</script>

index2.html

stacked.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>stacked</title>
<style>

body {
  font: 20px sans-serif;
}
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

</style>
<body>
<script src="../d3/d3.min.js"></script>
<script>

var margin = { top: 10, right: 10, bottom: 100, left: 10 },
    width = 960 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;

var data = [ [ { "x": 0, "y":  41}, { "x": 1, "y": 30} ],
             [ { "x": 0, "y":   9}, { "x": 1, "y": 50} ],
             [ { "x": 0, "y":  20}, { "x": 1, "y": 30} ] ];

var stack = d3.layout.stack();

var layers = stack(data);

var x = d3.scale.ordinal()
    .domain(d3.range(data[0].length))
    .rangeRoundBands([0, width], .1);

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

var xAxis = d3.svg.axis()
    .scale(x)
    .tickSize(0)
    .tickPadding(6)
    .orient("bottom");

var color = d3.scale.category10();

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 layer = svg.selectAll(".layer")
    .data(layers)
  .enter().append("g")
    .attr("class", "layer")
    .style("fill", function(d, i) { return color(i); });

var rect = layer.selectAll("rect")
    .data(function(d) { return d; })
  .enter().append("rect")
    .attr("x", function(d) { return x(d.x); })
    .attr("width", x.rangeBand())
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

</script>

stacked.js