Skip to content

Instantly share code, notes, and snippets.

@huembelin
Last active March 19, 2017 23:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huembelin/c9a331d9eb828ff793f5cff284c9067c to your computer and use it in GitHub Desktop.
Save huembelin/c9a331d9eb828ff793f5cff284c9067c to your computer and use it in GitHub Desktop.
Tax Redistribution
name value state
1 gini pre-tax 0.413 0
2 federal income tax -0.008 1
3 cantonal income tax -0.006 1
4 cantonal wealth tax -0.001 1
5 community income tax -0.006 1
6 community wealth tax -0.001 1
7 church tax -0.001 1
21 social 0.002 2
31 work related 0.001 2
41 real etatet 0.014 2
51 financial 0.003 2
61 transfers 0.001 2
71 others 1e-05 2
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar.total rect {
fill: steelblue;
}
.bar.positive rect {
fill: darkolivegreen;
}
.bar.negative rect {
fill: crimson;
}
.bar line.connector {
stroke: grey;
stroke-dasharray: 3;
}
.bar text {
fill: black;
font: 12px sans-serif;
text-anchor: middle;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<h1> Income inequality impact of taxes and deducations </h1>
<input type="checkbox" class="myCheckbox" value="1"> Show impact of taxes
<input type="checkbox" class="myCheckbox" value="2"> Show impact of deducations
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
/// Define size of canvas
var margin = {top: 20, right: 30, bottom: 70, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
// Scales
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], padding);
var y = d3.scale.linear()
.range([height, 0]);
// Axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
// place chart in chart-container
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// clip-path -> to show bars properly with shorter y-Axis domain
chart.append("clipPath")
.attr("id","clip")
.append("rect")
.attr("width",width)
.attr("height",height);
// round number formater
var formatNumber=d3.format(".2n")
// here we load the data
d3.csv("gData.csv", type, function(error, data) { // data formating is included in form of a later specified "type"-function
var gData = data;
startData = data.filter(function(d) {return d.state=="0"})
console.log(startData)
// Transform data (i.e., finding cumulative values and total) for easier charting
var cumulative = 0;
for (var i = 0; i < startData.length; i++) {
startData[i].start = cumulative;
cumulative += startData[i].value;
startData[i].end = cumulative;
startData[i].class = ( startData[i].value >= 0 ) ? 'positive' : 'negative'
}
startData.push({
name: 'Gini post-tax',
end: cumulative,
start: 0,
class: 'total'
});
// define domains
x.domain(startData.map(function(d) { return d.name; }));
y.domain([0.35, 0.45]); // shorter to show whats happening
// call x-Axis based dynamicaly on x-categories
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, x.rangeBand()); // this is an important part! wrap is defined below
// call y-Axis
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
console.log(startData)
// draw bars
var bar = chart.selectAll(".bar")
.data(startData, function (d) {return d.name}) // append data with a key
.enter().append("g")
.attr("class", function(d) { return "bar " + d.class })
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); })
.attr("width", x.rangeBand())
.attr("clip-path", "url(#clip)") // clip the rectangle
/// append value labels
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) { return y(d.end) + 5; })
.attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + "1.5em" })
.text(function(d) { return formatNumber(d.end - d.start);});
// draw connector line
bar.filter(function(d) { return d.class != "total" }).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5 )
.attr("y1", function(d) { return y(d.end) } )
.attr("x2", x.rangeBand() / ( 1 - padding) - 5 )
.attr("y2", function(d) { return y(d.end) } )
/// Update condition to fire update function
d3.selectAll(".myCheckbox").on("change",update);
update();
// define update function
function update () {
// This part does the filtering based on the checkboxes
var choices = ["0"];
d3.selectAll(".myCheckbox").each(function(d){
cb = d3.select(this);
if(cb.property("checked")){
choices.push(cb.property("value"));
}
});
console.log(choices)
if(choices.length > 1){
newData = gData.filter(function(d)
{
if (d.state == choices[0] || d.state == choices[1] || d.state == choices[2] )
return d;});
} else {
newData = startData;
}
/////////////
/// update part with new data
var cumulative = 0;
for (var i = 0; i < newData.length; i++) {
newData[i].start = cumulative;
cumulative += newData[i].value;
newData[i].end = cumulative;
newData[i].class = ( newData[i].value >= 0 ) ? 'positive' : 'negative'
}
newData.push({
name: 'Gini post-tax',
end: cumulative,
start: 0,
class: 'total'
});
// define domains
x.domain(newData.map(function(d) { return d.name; }));
y.domain([0.35, 0.45]); // shorter to show whats happening
// call y-Axis
chart.select(".y")
.call(yAxis);
// call x-Axis based dynamicaly on x-categories
chart.select(".x")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, x.rangeBand()); // this is an important part! wrap is defined below
// Select all bars
var bar = chart.selectAll(".bar")
.data(newData, function (d) {return d.name})
.enter().append("g")
.attr("class", function(d) { return "bar " + d.class })
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
// update bars
bar.attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); })
.attr("width", x.rangeBand())
.attr("clip-path", "url(#clip)") // clip the rectangle
// draw new bars
console.log(newData)
var bar = chart.selectAll(".bar")
.data(newData, function (d) {return d.name}) // append data with a key
.enter().append("rect")
.attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); })
.attr("width", x.rangeBand())
.attr("clip-path", "url(#clip)") // clip the rectangle
// remove bars that are not needed anymore
bar.remove();
/// append value labels
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) { return y(d.end) + 5; })
.attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + "1.5em" })
.text(function(d) { return formatNumber(d.end - d.start);});
// draw connector line
bar.filter(function(d) { return d.class != "total" }).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5 )
.attr("y1", function(d) { return y(d.end) } )
.attr("x2", x.rangeBand() / ( 1 - padding) - 5 )
.attr("y2", function(d) { return y(d.end) } )
}
});
// Part that does not need to be updated
// append y-Axis label
chart.append("text")
.attr("class","ylegend")
.attr("x",0)
.attr("y",15)
.style("text-anchor", "end")
.style("font-size",13)
.attr("transform", "rotate(-90)")
.text("Gini-Coefficient");
/// helper funcions
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
// parse the data
function type(d) {
d.value = +d.value;
return d;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment