Built with blockbuilder.org
forked from newsummit's block: Randomized Stacked Bar Chart
forked from newsummit's block: Randomized Stacked Bar Chart - experimental
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
body {
font-family: sans-serif;
position: relative;
margin: 10px auto;
}
</style>
</head>
<body>
<div id="main"></div>
<script>
// D3 API: https://github.com/d3/d3-3.x-api-reference/blob/master/API-Reference.md
// This example originated from https://stackoverflow.com/questions/15857286/how-to-create-a-bar-chart-made-up-of-very-small-squares
var svg,
max,
xAxis,
groups,
types,
width,
height,
xScale,
yScale,
timeScale,
translate,
randomize,
randomDate,
data = [],
timeSeries = [],
sameDateArr = [],
colorScale,
maxSquares = 5,
numDays = 50,
sumA = sumB = sumC = 0,
dateFormat = d3.time.format("%x"),
startDate = new Date(2016, 0, 1),
endDate = new Date(2016, 3, 1),
squareHeight = 12, // pixel height of squares
margin = {top: 20, right: 20, bottom: 80, left: 40};
// A helper to avoid having to use so many quotes inline
translate = function(x,y){
return "translate(" + x + "," + y + ")";
}
// Help me randomize within a range of values
randomize = function(min,max) {
return Math.floor(Math.random()*(max-min+1)+min);
}
// Help me generate random dates with intervals
randomDate = function(start, end) {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
}
// This color scale assigns a sequence of 10 colors to each data "type" : a,b,c
colorScale = d3.scale.category10().domain(["a", "b", "c"]);
// Populate a dummy array with random data
for (var day=0; day < numDays; day++) {
data.push({
a : randomize(0, maxSquares),
b : randomize(0, maxSquares),
c : randomize(0, maxSquares),
date : randomDate(startDate, endDate)
});
}
// Now aggregate all the dates into an associative array (no duplicates)
// https://www.jeromecukier.net/blog/2012/05/28/manipulating-data-like-a-boss-with-d3/
var groupedArr=d3.nest()
.key(function(d) {return dateFormat(d.date);})
.sortKeys(d3.ascending)
.rollup(function(d) {
return {
a: d3.sum(d, function(g) {return g.a;}),
b: d3.sum(d, function(g) {return g.b;}),
c: d3.sum(d, function(g) {return g.c;})
};
})
.entries(data);
// Now walk through each day, creating a sequential array
while(startDate < endDate){
var newDate = startDate.setDate(startDate.getDate() + 1);
startDate = new Date(newDate);
var allKeys = groupedArr.map(function(d) { return d.key; });
var x = groupedArr.filter(function(i) { return i.key === dateFormat(startDate); });
if (x.length > 0) { // allKeys.indexOf(dateFormat(startDate)) > -1
var val = x[0].values;
//timeSeries.push({a: val.a, b: val.b, c: val.c, date: x[0].key });
timeSeries.push({a: val.a, b: val.b, c: val.c, date: startDate });
} else {
//timeSeries.push({ a: 0, b: 0, c: 0, date: dateFormat(startDate) });
timeSeries.push({ a: 0, b: 0, c: 0, date: startDate });
}
}
console.log(timeSeries);
// Graph width based on data
width = squareHeight * timeSeries.length;
// Find the height of the tallest column to set a 'y scale'
max = d3.max(timeSeries, function(d){return d.a + d.b + d.c});
height = max * squareHeight;
// Create a linear scale for the x-axis labels
xScale = d3.scale.linear()
.range([0,width])
.domain([0,timeSeries.length]);
// Create a time scale for the viz.. extracted start/end dates from data
var x_domain = d3.extent(timeSeries, function(d) { console.log(d.date); return d.date; }),
timeScale = d3.time.scale()
//.domain([startDate, endDate])
.domain(x_domain)
.range([0, width]);
var xTime = d3.scale.ordinal()
.domain(d3.time.days(startDate, endDate))
.rangeRoundBands([0, width], .1);
// Create a linear vertical scale so that we can group boxes vertically
yScale = d3.scale.linear()
.domain([0, max])
.rangeRound([height, 0]);
// Define an axis object for x
xAxis = d3.svg.axis()
.scale(timeScale)
.orient("bottom")
.tickFormat(d3.time.format("%d %b"));
// Create svg container and position its main group "g" to top left
svg = d3.select("#main").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 + 0.5));
// Set up an "offsets" array for each color type ('a', 'b', 'c'). This
// associates a color domain for each stack segment (starting block, ending block)
timeSeries.forEach(function(d){
var y0 = 0;
d.offsets = colorScale.domain().map(function(type){
return {type: type, y0: y0, y1: y0 += +d[type], value : d[type]}
});
});
// Now, render and position the x-axis in a group element
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create/position a g element for each tower.
groups = svg.selectAll(".group")
.data(timeSeries)
.enter().append("g")
.attr("transform", function(d,i){return "translate(" + xScale(i) + ", 0)"})
.attr("class", "group");
// Create a g element for each section of a tower.
types = groups.selectAll(".type")
.data(function(d){return d.offsets})
.enter().append("g")
.attr("transform", function(d){ return translate(0,yScale(d.y1))})
.attr("class", "type")
.attr("fill", function(d){return colorScale(d.type)})
// Create the individual squares
types.selectAll("rect")
.data(function(d){return d3.range(0,d.value)})
.enter().append("rect")
.attr("height", squareHeight-0.5)
.attr("width", squareHeight-0.5)
.attr("y", function(d){ return squareHeight * d })
</script>
</body>
https://d3js.org/d3.v3.js