Skip to content

Instantly share code, notes, and snippets.

@mickeykedia
Last active May 6, 2016 19:31
Show Gist options
  • Save mickeykedia/4d247c4741cdbccef3a527f5201275f4 to your computer and use it in GitHub Desktop.
Save mickeykedia/4d247c4741cdbccef3a527f5201275f4 to your computer and use it in GitHub Desktop.
Bird Swing Graph for Elections

Bird Swing Graph

A graph for showing how close election results are for a particular political party in a set of constituencies.

The green bars represent seats that the party has won (or is leading in), and the black bars represent the seats that the party has lost (or is trailing in).

The bars are further color coded according to the party's performance last time.

  • Dark green bars represent constituencies in which the party lost the last time (swing seats)
  • Solid black bars represent constituencies in which the party won the last time (swing seats)

Useful for

  • Showing Live Election Results in a close election (where just the number of leads/trails is not enough information)
  • Showing how close the elections after the results have been declared

This visualization was used for the 2014 General elections in India at www.electioncharts.com. The github repo for that site can be found at https://github.com/mickeykedia/electioncharts

Built with blockbuilder.org

[{"status": "WIN", "constituency_name": "Murshidabad", "loosing_party_name": "Indian National Congress", "loosing_candidate_name": "Abdul Mannan", "party_id": 26, "last_time_party_id": 2, "votes": 426947, "lead": 18453, "candidate_name": "Badaruddoza Khan", "constituency_id": 422, "candidate_id": 8766}, {"status": "WIN", "constituency_name": "Raiganj", "loosing_party_name": "Indian National Congress", "loosing_candidate_name": "Deepa Dasmunsi", "party_id": 26, "last_time_party_id": 2, "votes": 317515, "lead": 1634, "candidate_name": "Md. Salim", "constituency_id": 414, "candidate_id": 7145}, {"status": "LOST", "winning_party_name": "Indian National Congress", "constituency_name": "Jangipur", "winning_candidate_name": "Abhijit Mukherjee", "party_id": 26, "last_time_party_id": 2, "votes": 370040, "lead": -8161, "candidate_name": "Muzaffar Hossain", "constituency_id": 420, "candidate_id": 11843}, {"status": "LOST", "winning_party_name": "Indian National Congress", "constituency_name": "Maldaha Uttar", "winning_candidate_name": "Mausam Noor", "party_id": 26, "last_time_party_id": 2, "votes": 322904, "lead": -65705, "candidate_name": "Khagen Murmu", "constituency_id": 416, "candidate_id": 10872}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Birbhum", "winning_candidate_name": "Satabdi Roy", "party_id": 26, "last_time_party_id": 165, "votes": 393305, "lead": -67263, "candidate_name": "Dr. Elahi Kamre Mahammad", "constituency_id": 454, "candidate_id": 10765}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Jalpaiguri", "winning_candidate_name": "Bijoy Chandra Barman", "party_id": 26, "last_time_party_id": 26, "votes": 425167, "lead": -69606, "candidate_name": "Mahendra Kumar Roy", "constituency_id": 412, "candidate_id": 6955}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Krishnanagar", "winning_candidate_name": "Tapas Paul", "party_id": 26, "last_time_party_id": 165, "votes": 367534, "lead": -71255, "candidate_name": "Jha Shantanu", "constituency_id": 423, "candidate_id": 13850}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Diamond Harbour", "winning_candidate_name": "Abhishek Banerjee", "party_id": 26, "last_time_party_id": 165, "votes": 437183, "lead": -71298, "candidate_name": "Dr. Abul Hasnat", "constituency_id": 432, "candidate_id": 9612}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Bankura", "winning_candidate_name": "Sreemati Dev Varma (Moon Moon Sen)", "party_id": 26, "last_time_party_id": 26, "votes": 384949, "lead": -98506, "candidate_name": "Acharia Basudeb", "constituency_id": 448, "candidate_id": 7256}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Durgapur", "winning_candidate_name": "Dr. Mamtaz Sanghamita", "party_id": 26, "last_time_party_id": 26, "votes": 447190, "lead": -107331, "candidate_name": "Sk. Saidul Haque", "constituency_id": 451, "candidate_id": 7283}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Bardhaman Purba", "winning_candidate_name": "Sunil Kumar Mondal", "party_id": 26, "last_time_party_id": 26, "votes": 460181, "lead": -114479, "candidate_name": "Iswar Chandra Das", "constituency_id": 450, "candidate_id": 10331}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Jadavpur", "winning_candidate_name": "Sugata Bose", "party_id": 26, "last_time_party_id": 165, "votes": 459041, "lead": -125203, "candidate_name": "Sujan Chakraborty", "constituency_id": 433, "candidate_id": 7125}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Mathurapur", "winning_candidate_name": "Choudhury Mohan Jatua", "party_id": 26, "last_time_party_id": 165, "votes": 489325, "lead": -138436, "candidate_name": "Rinku Naskar", "constituency_id": 431, "candidate_id": 13259}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Bangaon", "winning_candidate_name": "Kapil Krishna Thakur", "party_id": 26, "last_time_party_id": 165, "votes": 404612, "lead": -146601, "candidate_name": "Debesh Das", "constituency_id": 425, "candidate_id": 9404}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Kolkata Uttar", "winning_candidate_name": "Sudip Bandyopadhyay", "party_id": 26, "last_time_party_id": 165, "votes": 196053, "lead": -147634, "candidate_name": "Bagchi Rupa", "constituency_id": 435, "candidate_id": 13303}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Bishnupur", "winning_candidate_name": "Khan Saumitra", "party_id": 26, "last_time_party_id": 26, "votes": 429185, "lead": -149685, "candidate_name": "Susmita Bauri", "constituency_id": 449, "candidate_id": 7270}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Serampore", "winning_candidate_name": "Kalyan Banerjee", "party_id": 26, "last_time_party_id": 165, "votes": 362407, "lead": -152526, "candidate_name": "Tirthankar Ray", "constituency_id": 439, "candidate_id": 14571}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Kolkata Dakshin", "winning_candidate_name": "Subrata Bakshi", "party_id": 26, "last_time_party_id": 165, "votes": 278414, "lead": -153301, "candidate_name": "Nandini Mukherjee", "constituency_id": 434, "candidate_id": 15829}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Dum Dum", "winning_candidate_name": "Saugata Roy", "party_id": 26, "last_time_party_id": 165, "votes": 328310, "lead": -154934, "candidate_name": "Asim Kumar Dasgupta", "constituency_id": 427, "candidate_id": 8651}, {"status": "LOST", "winning_party_name": "Bharatiya Janata Party", "constituency_name": "Asansol", "winning_candidate_name": "Babul Supriya Baral (Babul Supriyo)", "party_id": 26, "last_time_party_id": 26, "votes": 255829, "lead": -164154, "candidate_name": "Bansa Gopal Choudhury", "constituency_id": 452, "candidate_id": 7291}, {"status": "LOST", "winning_party_name": "Indian National Congress", "constituency_name": "Maldaha Dakshin", "winning_candidate_name": "Abu Hasem Khan Chowdhury", "party_id": 26, "last_time_party_id": 2, "votes": 209480, "lead": -170811, "candidate_name": "Abul Hasnat Khan", "constituency_id": 419, "candidate_id": 8200}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Hooghly", "winning_candidate_name": "Dr. Ratna De (Nag)", "party_id": 26, "last_time_party_id": 165, "votes": 425228, "lead": -189084, "candidate_name": "Pradip Saha", "constituency_id": 440, "candidate_id": 12510}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Howrah", "winning_candidate_name": "Prasun Banerjee", "party_id": 26, "last_time_party_id": 165, "votes": 291505, "lead": -196956, "candidate_name": "Sridip Bhattacharya", "constituency_id": 436, "candidate_id": 14137}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Uluberia", "winning_candidate_name": "Sultan Ahmed", "party_id": 26, "last_time_party_id": 165, "votes": 369563, "lead": -201222, "candidate_name": "Sabir Uddin Molla", "constituency_id": 438, "candidate_id": 13377}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Ranaghat", "winning_candidate_name": "Tapas Mandal", "party_id": 26, "last_time_party_id": 165, "votes": 388684, "lead": -201767, "candidate_name": "Archana Biswas", "constituency_id": 424, "candidate_id": 8523}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Barrackpore", "winning_candidate_name": "Dinesh Trivedi", "party_id": 26, "last_time_party_id": 165, "votes": 272433, "lead": -206773, "candidate_name": "Subhashini Ali", "constituency_id": 426, "candidate_id": 14185}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Kanthi", "winning_candidate_name": "Adhikari Sisir Kumar", "party_id": 26, "last_time_party_id": 165, "votes": 447259, "lead": -229490, "candidate_name": "Sinha Tapas", "constituency_id": 443, "candidate_id": 14479}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Bolpur", "winning_candidate_name": "Anupam Hazra", "party_id": 26, "last_time_party_id": 26, "votes": 394581, "lead": -236112, "candidate_name": "Dome Ramchandra", "constituency_id": 453, "candidate_id": 7299}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Tamluk", "winning_candidate_name": "Adhikari Suvendu", "party_id": 26, "last_time_party_id": 165, "votes": 470447, "lead": -246481, "candidate_name": "Sekh Ibrahim Ali", "constituency_id": 442, "candidate_id": 13722}, {"status": "LOST", "winning_party_name": "Bharatiya Janata Party", "constituency_name": "Darjeeling", "winning_candidate_name": "S.S.Ahluwalia", "party_id": 26, "last_time_party_id": 1, "votes": 167186, "lead": -321071, "candidate_name": "Saman Pathak (Suraj)", "constituency_id": 413, "candidate_id": 13457}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Arambagh", "winning_candidate_name": "Aparupa Poddar (Afrin Ali)", "party_id": 26, "last_time_party_id": 26, "votes": 401919, "lead": -346845, "candidate_name": "Saktimohan Malik", "constituency_id": 441, "candidate_id": 7200}, {"status": "LOST", "winning_party_name": "All India Trinamool Congress", "constituency_name": "Jhargram", "winning_candidate_name": "Uma Saren", "party_id": 26, "last_time_party_id": 26, "votes": 326621, "lead": -347883, "candidate_name": "Dr. Pulin Bihari Baske", "constituency_id": 445, "candidate_id": 7227}]
{"wins":2,"leads":0}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Bird Swing Graph</title>
<!-- Bootstrap Core CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<style>
.bar.postive {
fill:steelblue;
}
.bar.negative {
fill:brown;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
rect:hover {
opacity: 0.5;
}
.gridStyle {
border: 1px solid rgb(212,212,212);
width: 100%;
height: 400px;
}
</style>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<!-- jQuery Version 1.12.3 -->
<script src="https://code.jquery.com/jquery-1.12.3.min.js" integrity="sha256-aaODHAgvwQW1bFOGXMeX+pC4PZIPsvn2h1sArYOhgXQ=" crossorigin="anonymous"></script>
<!-- Bootstrap Core JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script type="text/javascript">
// Finally we have data and now and we are going to do something with it !
function drawMap(error, data, results){
var margin = {top:40,right:10,bottom:10,left:50};
var svg = d3.select('.container').append("svg");
var numberFormat = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
console.log(error);
console.log(data);
/**
* going to be used to calculate position of dotted line.
* @type {number}
*/
var i = 0;
var j = 0;
// Sorting data by lead
data = data.sort(function(a,b){
return b.lead - a.lead;
});
// Adding the total number of constituencies which
// in which the party is leading/winning
i = results.wins + results.leads;
// last index of total data length
j = data.length-i;
var height = 400 - margin.top - margin.bottom,
// Finding the width of the first element and using that as the default width for responsiveness
width = 900 - margin.left - margin.right,
// square root scale for the leads
yScale = d3.scale.pow().exponent(0.5)
.domain(d3.extent(data,function(d){
return d.lead;
}))
.range([height,0]).clamp(true),
// ordinal scale for individual constituencies
xScale= d3.scale.ordinal()
.domain(data.map(function(d){
return d.constituency_id+":"+d.candidate_name;
})).rangeBands([0,width],0.15,0.05);
console.log(results);
// defining colours to use for each bar
var colour_swing = "#006600",
colour ="#4D944D",
colourTrail="#7D7D7E",
colourTrail_swing="#000000";
// minimum lead (prolly a large negative number )
var min = d3.min(data,function(d){
return d.lead;
})
/**
* find the first candidate with a lead, first candidate with a trail and the first candidate.
* Figure out the x axis position of those three points and draw lines and write text
* on the SVG. Text must count the number of wins/leads and trails as well.
* Add (lead %)
*/
svg.attr('height',height + margin.top + margin.bottom)
.attr('width',width + margin.left + margin.right)
.attr("border",1);
// Adding a 'g' element to the SVG
var svgG = svg.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
console.log(svg);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svgG.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr("class",function(d){
return "bar";
})
.attr('width',xScale.rangeBand())
.attr('y',function(d){
return yScale(Math.max(0, d.lead));
})
.attr('x',function(d){
return xScale(d.constituency_id+":"+d.candidate_name);
})
.attr('fill',function(d){
if(d.status=="TRAIL" || d.status=="LOST"){
if(d.party_id == d.last_time_party_id){
return colourTrail_swing;
}else {
return colourTrail;
}
}else {
if(d.party_id != d.last_time_party_id){
return colour_swing;
}else {
return colour;
}
}
})
.attr('title',function(d){
var str = d.constituency_name+" ("+d.status+")";
if(d.party_id == d.last_time_party_id){
if(d.status =="TRAIL" || d.status =="LOST"){
str=str+" | <b>SWING SEAT</b><br/>";
}
}else {
if(d.status =="WIN" || d.status =="LEAD"){
str=str+" | <b>SWING SEAT</b><br/>";
}
}
return str;
})
.attr('data-content',function(d){
var str = "Candidate : "+ d.candidate_name+"<br/>";
/**
* Put commas in the numbers,
* difficult to read them or else.
* @type {string}
*/
if(d.status=="TRAIL"){
str = str+"Trail by: "+(-d.lead).toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Winning Party: "+ d.winning_party_name+"<br/>";
}else if (d.status=="LEAD"){
str = str+"Lead by : "+ d.lead.toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Runner-up Party: "+ d.loosing_party_name+"<br/>";
}else if(d.status=="LOST"){
str=str+"Lost by : "+ (-d.lead).toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Winning Party: "+ d.winning_party_name+"<br/>";
}else {
str = str+"Won by :"+ d.lead.toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Runner-up Party: "+ d.loosing_party_name+"<br/>";
};
return str;
})
.attr('data-toggle', 'popover')
.attr('leads',function(d){
return d.lead;
})
.transition()
.duration(1000)
.attr('height', function (d) {
return Math.abs(yScale(d.lead) - yScale(0));
});
$(function () {
$('[data-toggle="popover"]').popover({trigger:"hover", content:"content", container:"body", html:true})
});
/**
* Adding a line to demarcate the trailing/leading line.
* also a text which says - constituency in the right place on the graph.
*/
if (i>0 & i < data.length){
// i represents the number of winning/lead seats and that's why the line is drawn there (?)
var trailLineX = xScale(data[i].constituency_id+":"+ data[i].candidate_name);
svgG.append("line")
.attr("x1",trailLineX)
.attr("y1",0)
.attr("x2",trailLineX)
.attr("y2",height)
.attr("stroke","black")
.attr("stroke-dasharray","5,5");
if(trailLineX < width/4){
svgG.append('text')
.attr('x',trailLineX+margin.left)
.attr('y',yScale(0)-margin.top)
.attr('font-style','italic')
.text('Constituency');
}else {
svgG.append('text')
.attr('x',trailLineX-(2*margin.left+margin.right))
.attr('y',yScale(0)+margin.top)
.attr('font-style','italic')
.text('Constituency');
}
}
/**
* adding axes.
*/
svgG.append("g")
.attr("class","y axis")
.call(yAxis)
.append("text")
.attr("y",-10)
.attr("x",-5)
.attr("dy",".71em")
.style("text-anchor","end")
.style("font-size","0.8em")
.text("Margin");
svgG.append("g")
.attr("class","x axis")
.append("line")
.attr("y1",yScale(0))
.attr("y2",yScale(0))
.attr("x2",width);
}
queue()
.defer(d3.json, 'cpim_bengal.json')
.defer(d3.json, 'cpim_bengal_result.json') // REPLACE REF WITH DATA
.await(drawMap);
</script>
</head>
<body>
<div class="container">
<div id="chart_div" class="row">
</div>
</div>
<!-- /.container -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment