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
<!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>
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)
<!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>
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());
}
}
<!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>
<!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>
<!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>
<!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>