UN Votes analysis using d3 and Numeric Javascript, forked from Principal Component Analysis by ktaneishi.
Preview on /fil/2481e1f3b1d92002317e
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 11px "Cisalpin LT Std", "Lucida Grande";
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis {display: none;}
.dot circle {
stroke: #000;
}
.nuclear {
stroke: #d24232;
stroke-width: 2px;
}
</style>
<body>
<svg>
<defs>
<filter id="blur" x="0" y="0">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
</svg>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="pca.js"></script>
<script>
function parseVote(x) {
if (x == 'Y') return 0;
if (x == 'N') return 5;
if (x == 'A') return 2;
if (x == 'X') return 1;
}
var reposition = true,
zero = {
x: -2,
y: 6
},
inflation = 1.1;
var randomseed = 0,
version = 50;
Math.random = function () {
randomseed++;
return Math.sin(version * randomseed);
}
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
};
margin = {
top: 2,
right: 2,
bottom: 2,
left: 2
};
var width = 900 - margin.left - margin.right,
height = 650 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.domain(['AG','APG','EEG','WEOG','GRULAC'])
.range(['#c27d66', '#e3a26a','#f5ce96', '#fae7b9', '#c4b38e', 'red']);
var nuclear = function(iso){
return ['USA','RUS','FRA','GBR','PRK','CHN','PAK','IND','ISR']
.indexOf(iso)>-1;
}
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("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 groups = d3.map();
var names = d3.map();
d3.csv("country-codes.csv", function (error, data) {
data.forEach(function (d) {
groups.set(d.iso, d.group);
names.set(d.iso, d.country);
});
d3.csv("votes-session-70-nations-unies.csv", function (error, data) {
matrix = [];
data.map(function (d) {
d = d3.values(d);
d = d.slice(1).map(parseVote);
matrix.push(d);
});
var pca = new PCA();
matrix = pca.scale(matrix, true, true);
pc = pca.pca(matrix, 2);
data.map(function (d, i) {
d.x = d.pc1 = -pc[i][0];
d.y = d.pc2 = -pc[i][1];
});
svg.append('g').attr('id', 'bg');
var dots = svg.selectAll(".dot")
.data(data)
.enter().append("g")
.attr({class: "dot"
,id: function(d){return d.iso;}});
var taken = {},
step;
if (reposition) {
for (step = 0.001; step < 1; step *= inflation) {
taken = {};
dots.each(function (d) {
var angle = Math.atan((d.x - zero.x) / (d.y - zero.y));
do {
var x1 = Math.ceil(d.x / step),
y1 = Math.ceil(d.y / step),
v = "" + x1 + "-" + y1;
} while (!!taken[v] && (d.x -= (0.3 + Math.random()) * Math.sin(angle)) && (d.y -= (0.3 + Math.random()) * Math.cos(angle)))
taken[v] = 1;
});
}
step = step / inflation;
dots.each(function (d) {
d.x = step * Math.ceil(d.x / step);
d.y = step * Math.ceil(d.y / step);
});
}
dots
.attr('title',function (d) {
return names.get(d.iso);
});
dots
.append("rect")
.attr({
y: -9,
x: -9,
width: 18,
height: 12,
class: function(d){
if (nuclear(d.iso)) return 'nuclear';
}
})
.style({fill: function (d) {
return color(groups.get(d.iso));
}})
;
dots
.append("text")
.text(function (d) {
return d.iso;
})
.style({
'text-anchor': 'middle',
fill: 'black',
'font-size': '7px',
})
x.domain(d3.extent(data, function (d) {
return d.x;
}))
.nice()
.domain([-20, 17]);
y.domain(d3.extent(data, function (d) {
return d.y;
})).nice()
.domain([-22, 25]);
dots
.attr("transform", function (d) {
return "translate(" + x(d.pc1) + "," + y(d.pc2) + ")";
});
var area = d3.svg.area()
.x(function (d) {
return x(d.x);
})
.y0(function (d) {
return y(d.y);
})
.y1(0);
d3.select('#bg')
.append('rect')
.attr({
x: 0,
y: 0,
width: width,
height: height,
})
.style({
fill: '#3f151d',
stroke: '#031622',
'stroke-width': '3px'
});
d3.select('#bg')
.append("path")
.datum([{
x: -20,
y: -7
}, {
x: 12,
y: 25
}])
.attr("class", "area")
.attr("d", area)
.style({
fill: "#fef6ea"
})
.attr('filter', 'url(#blur)')
;
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("PC1");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("PC2")
dots
.attr("transform", function (d) {
return "translate(" + x(d.pc1) + "," + y(d.pc2) + ")";
})
//.transition()
.attr("transform", function (d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
});
var grouplegend = svg
.append('g')
.attr('id','groups')
.attr("transform", function (d, i) {
return "translate("+(width-25)+","+ (35) + ")";
})
;
grouplegend.append('text')
.text('Groupes régionaux')
.attr({'text-anchor': 'end', fill: '#fdfef1'})
.style({'font-weight': 'bold','font-size': '13px'})
;
var legend = grouplegend
.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) {
return "translate(0," + (10 + i * 20) + ")";
});
var grups = {'APG': 'Asie et Pacifique', 'EEG': 'Europe de l’est', 'AG':'Afrique', 'WEOG':'Europe occidentale et autres', 'GRULAC':'Amérique latine et Caraïbes'}
legend.append("rect")
.attr("x", -20)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", -30)
.attr("y", 9)
.attr("dy", ".35em")
.style({
"text-anchor": "end",
fill: '#fdfef1',
})
.text(function (d) {
return grups[d] || d;
});
grouplegend.append("rect")
.attr({x: -20, y: 120, width: 18, height: 18, class:'nuclear'})
.style("fill", "#dadfbc");
grouplegend.append("text")
.attr("x", -30)
.attr("y", 129)
.attr("dy", ".35em")
.style({
"text-anchor": "end",
fill: '#fdfef1',
})
.text('Pays disposant de l’arme nucléaire');
svg.append('text')
.text('Qui vote avec qui ?')
.attr({
x: 15,
y: 40,
'font-size': '32px'
});
svg.append('g')
.selectAll('text')
.data(['Les 193 États-membres des Nations unies, répartis selon la similarité de leurs votes sur les questions de désarmement', 'et de sécurité internationale lors de la 70e Assemblée générale, septembre-décembre 2015.',
'',
'Principaux points de clivage entre les nations :',
'1. — En bas à droite : consensus, pays votant “oui” à la quasi-totalité des résolutions',
'2. — Vers la gauche : pays occidentaux, plutôt opposés aux résolutions sur le désarmement nucléaire', // L15 L38 L40 L44 L51
'3. — Vers le haut : pays plutôt opposés aux résolutions sur les armes chimiques et les bombes à sous-munitions', // L27 L26 L49
'4. — En haut à gauche : dissensus, pays votant “non” ou s’abstenant plus que les autres.',
])
.enter()
.append('text')
.attr({
transform: function(d,i) {return 'translate(15,' + (height - 118 + 14*i) + ')';
},
fill: '#fdfef1',
})
.text(function(d){return d;});
svg.append('g')
.attr('id', 'points')
.selectAll('text')
.data([
{t: '➊', x: width * 0.64, y: height * 0.7},
{t: '➋', x: width * 0.1, y: height * 0.7},
{t: '➌', x: width * 0.64, y: height * 0.13},
{t: '➍', x: width * 0.1, y: height * 0.13},
])
.enter()
.append('text')
.text(function(d) {
return d.t;
})
.attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; })
.style({
'font-size': '23px',
fill: function(d, i) {
if (i < 2) return '#fdfef1'; return '#3f151d';
},
});
config = {width: width, height:height}
graphsignature(['Philippe Rivière — Visionscarto','Décembre 2015']);
});
});
function graphsignature(lignes) {
signature = svg
.append('g');
signature
.selectAll('text')
.data(lignes)
.enter()
.append('text')
.attr("y", function (d, i) {
return 14 * i;
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.style("font-size", "8px")
.text(function (d) {
return d.toUpperCase();
});
var bbox = signature[0][0].getBBox();
signature
.attr({
transform: 'translate(' + [config.width - bbox.width / 2 - 19, config.height - bbox.height -10] + ')',
});
signature
.selectAll('line')
.data(lignes.slice(1))
.enter()
.append('line')
.attr({
x1: -bbox.width / 2,
x2: bbox.width / 2,
y1: function (d, i) {
return 14 * i + 4;
},
y2: function (d, i) {
return 14 * i + 4;
}
})
.attr("stroke", "white");
}
</script>
https://d3js.org/d3.v3.min.js