[ Launch: Negative Stacked Bar Charts ] 6117033 by enoex
[ Launch: Negative Stacked Bar Charts ] 6116099 by enoex
-
-
Save erikhazzard/6117033 to your computer and use it in GitHub Desktop.
Negative Stacked Bar Charts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"description":"Negative Stacked Bar Charts","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"thumbnail":"http://i.imgur.com/TEELsAg.png"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --------------------------------------------------------------------------- | |
// Negative / Positive Stacked bar chart | |
// In the 'reusable' chart pattern: http://bost.ocks.org/mike/chart/ | |
// --------------------------------------------------------------------------- | |
// | |
var getData = function getData(){ | |
// returns some dummy data | |
// *note*: In this example, the data is expected to be in the format of | |
// an array with sub arrays. Each sub array represents a group of stacked | |
// bars, each item in each sub array is an object representing an individual | |
// "stacked" item | |
// | |
// You can change the StackedChart function to work with your data format by | |
// modifying the `setupStack` function. | |
var dummyData = []; | |
var stack = null; | |
// Make some random data | |
// (*note* use Math.ceil so 0 is never generated) | |
for(var i=0; i < Math.ceil(Math.random() * 10); i++){ | |
stack = []; | |
for(var j=0; j < Math.ceil(Math.random() * 5); j++){ | |
stack.push({ | |
value: Math.round(-100 + Math.random() * 200) + 1, | |
name: i + '-' + j, | |
group: 'Group ' + i | |
}); | |
} | |
dummyData.push(stack); | |
} | |
//// To see structure of data: | |
// console.log( JSON.stringify( dummyData, null, 4 ) ); | |
return dummyData; | |
}; | |
var StackedChart = function StackedChart(config){ | |
// Setup SVG | |
// -------------------------------------- | |
// default values | |
config = config || {}; | |
var width = config.width || 500; | |
var height = config.height || 500; | |
var margin = config.margin || 20; | |
var data = config.data || []; | |
var duration = config.duration || 700; | |
// color scale | |
var color = d3.scale.category20c(); | |
// assumes an SVG element already exists | |
// (note: attributes will be reset in myChart()) | |
var svg = d3.select('svg').attr({ | |
width: width, | |
height: height | |
}); | |
// Setup groups | |
var chartGroup = svg.append('g'); | |
var xAxisGroup = svg.append('g').attr('class','axis x'); | |
var yAxisGroup = svg.append('g').attr('class','axis y'); | |
// Setup scales and axes | |
// -------------------------------------- | |
var xScale; | |
var yScale; | |
var updateScales = function updateScales(){ | |
// setup an ordinal scale for the x axis. The input domain will be an | |
// array of group names (from the data) | |
xScale = d3.scale.ordinal().domain(data.map(function(datum,i){ | |
// We'll always have at least element in the datum array | |
return datum[0].group; | |
})) | |
.rangeRoundBands([margin, width - margin], 0.1); | |
// Setup a linear scale for the y axis | |
var merged = d3.merge(data); | |
yScale = d3.scale.linear().domain([ | |
// the min data should be the base y minus the size | |
d3.min(merged, function(d){ return d.y0-d.size; }), | |
// y0 will contain the highest value | |
d3.max(merged, function(d){ return d.y0; }) | |
]) | |
.range([height - margin, margin]) | |
// nice it so we get nice round values | |
.nice(); | |
}; | |
var updateAxis = function(){ | |
// Update the x and y axis | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom") | |
.tickSize(6, 0); | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left"); | |
// use the axes group defined above | |
xAxisGroup.transition() | |
.duration(duration) | |
.attr({ | |
transform: "translate (" + [ | |
0, yScale(0) ] + ")" | |
}) | |
.call(xAxis); | |
yAxisGroup.transition() | |
.duration(duration) | |
.attr({ | |
transform: "translate (" + [ | |
xScale(margin), 0 ] + ")" | |
}) | |
.call(yAxis); | |
}; | |
// Update bars | |
// -------------------------------------- | |
var updateBars = function(){ | |
// This function is called to: | |
// 1. initially create the stacked bars | |
// 2. update stacked bars on all subsequent calls | |
// Setup groups | |
var barGroups = chartGroup.selectAll('.barGroup').data(data); | |
// create groups | |
barGroups.enter().append('svg:g').attr({ | |
'class': 'barGroup' | |
}); | |
// ** exit groups ** | |
// handles if an entire group is removed | |
barGroups.exit().transition() | |
.duration(duration) | |
.style({ opacity: 0 }) | |
.remove(); | |
// secondly, setup the stacked groups | |
var bars = barGroups.selectAll('.bar') | |
.data(function(d){ | |
return d; | |
}); | |
// ** Enter ** | |
bars.enter().append('svg:rect') | |
.attr({ | |
'class': 'bar', | |
width: xScale.rangeBand(), | |
x: function(d,i){ | |
// Pass in the index to the xScale | |
return xScale(d.group); | |
}, | |
y: function(d,i){ | |
return yScale(d.y0); | |
}, | |
height: function(d,i){ | |
return (yScale(0) - yScale(d.size)) / 2; | |
} | |
}) | |
.style({ | |
fill: function(d,i){ | |
return color(i); | |
}, | |
opacity: 0 | |
}) | |
// interaction | |
.on('mouseenter', function(d, i){ | |
console.log('Current item: ', d, 'Index: ', i); | |
}); | |
// ** Update ** | |
bars.transition() | |
.duration(duration) | |
.attr({ | |
width: xScale.rangeBand(), | |
x: function(d,i){ | |
// Pass in the index to the xScale | |
return xScale(d.group); | |
}, | |
y: function(d,i){ | |
return yScale(d.y0); | |
}, | |
height: function(d,i){ | |
return yScale(0) - yScale(d.size); | |
} | |
}) | |
.style({ | |
opacity: 1 | |
}); | |
//** Exit ** | |
// handles if an individual stacked item is removed | |
bars.exit().transition() | |
.duration(duration) | |
.attr({ height: 0 }) | |
.style({ opacity: 0 }) | |
.remove(); | |
return bars; | |
}; | |
// -------------------------------------- | |
// Update / Draw Chart | |
// -------------------------------------- | |
var myChart = function myChart(){ | |
// Update the svg properties | |
svg.attr({ | |
height: height, | |
width: width | |
}); | |
// Format data | |
data = setupStack(data); | |
// Update all parts of the chart | |
updateScales(); | |
updateAxis(); | |
// Update bars | |
updateBars(); | |
return myChart; | |
}; | |
// Helper functions | |
// ---------------------------------- | |
var setupStack = function setupStack(origData){ | |
// Formats the passed in data object to be in a format our | |
// chart can consume. | |
// *note*: This will modify the passed in object. If you don't want | |
// this behavior, you can clone the object (e.g., use underscore's | |
// clone method: origData = _.clone(origData) | |
// | |
// The setup data will be an array of arrays, each object in the | |
// subarray being an object representing an individual "stack". There | |
// are two added properties, `y0` and `size`, which specify the y | |
// position and `height` of the stack item. When created the bars, | |
// use these properties to position and size the bar | |
// | |
// setup some variables | |
var len = origData.length; | |
var i=0; j=0, d=null; | |
var basePositive=0, baseNegative=0; | |
for(i=0;i<len;i++){ // loop through each stacked group | |
// reset bases for each new group | |
basePositive = 0; | |
baseNegative = 0; | |
for(j=0; j<origData[i].length; j++){ // loop through each stack | |
stackItem = origData[i][j]; | |
stackItem.size = Math.abs(stackItem.value); | |
// If the value is negative, we want to place the bar under | |
// the 0 line | |
if (stackItem.value < 0) { | |
stackItem.y0 = baseNegative; | |
baseNegative -= stackItem.size; | |
} else { | |
basePositive += stackItem.size; | |
stackItem.y0 = basePositive; | |
} | |
} | |
} | |
//// To see the format of the data: | |
//console.log(JSON.stringify(origData, null, 4)); | |
return origData; | |
}; | |
// Config Chart | |
// ---------------------------------- | |
myChart.width = function(value) { | |
if (!arguments.length){ return width; } | |
width = value; | |
return myChart; | |
}; | |
myChart.height = function(value) { | |
if (!arguments.length){ return height; } | |
height = value; | |
return myChart; | |
}; | |
myChart.margin = function(value) { | |
if (!arguments.length){ return margin; } | |
margin = value; | |
return myChart; | |
}; | |
myChart.data = function(value) { | |
if (!arguments.length){ return data; } | |
data = value; | |
return myChart; | |
}; | |
myChart.duration = function(value) { | |
if (!arguments.length){ return duration; } | |
duration= value; | |
return myChart; | |
}; | |
return myChart; | |
}; | |
// -------------------------------------- | |
// Create the chart | |
// -------------------------------------- | |
var chart = StackedChart({ | |
// pass in some data | |
data: getData(), | |
// transition duration | |
duration: 900 | |
}); | |
chart(); | |
// further calls to chart() will update it | |
setInterval(function(){ | |
// we can also change any exposed property | |
//chart.margin(Math.random() * 20 ) | |
//.height(Math.random() * 800 ) | |
//.width(Math.random() * 800 ) | |
//.data( getData() ) | |
//(); | |
// pass in new data then generate chart | |
chart.data(getData())(); | |
}, 1800); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment