Based on answers from http://VolebnaKalkulacka.sk using Weighted PCA method as described here: https://gist.github.com/michalskop/8514867
Cutting lines with loss function 0 (no errors, perfect cut) are shown.
Based on answers from http://VolebnaKalkulacka.sk using Weighted PCA method as described here: https://gist.github.com/michalskop/8514867
Cutting lines with loss function 0 (no errors, perfect cut) are shown.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font: 12px sans-serif; | |
} | |
.point { | |
fill: #1F77B4; | |
stroke: #1F77B4; | |
fill-opacity: .3; | |
stroke-width: 1.5px; | |
stroke-opacity: 1; | |
} | |
g.active .point { | |
fill-opacity: .9; | |
} | |
g.active .name { | |
font-weight: bold; | |
} | |
.line-text { | |
font: 10px sans-serif; | |
} | |
g.active .line-text { | |
font-weight: bold; | |
cursor: default; | |
} | |
g.active line { | |
stroke-width: 4 | |
} | |
.name { | |
fill: blue; | |
font-family: sans-serif; | |
cursor: default; | |
} | |
line { | |
stroke:gray; | |
stroke-width:2; | |
opacity: .15; | |
} | |
.quality-1 line { | |
stroke-dasharray:10,10 | |
} | |
.area { | |
visibility: hidden; | |
} | |
g.active .area { | |
opacity: .15; | |
visibility: visible; | |
} | |
g.active .yes { | |
fill: green; | |
} | |
g.active .no { | |
fill: red; | |
} | |
g.yes circle { | |
stroke: green; | |
} | |
g.no circle { | |
stroke: red; | |
} | |
</style> | |
<div id="chart"></div> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> | |
<script> | |
var margin = {top: 20, right: 30, bottom: 30, left: 30}, | |
width = 500 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var x = d3.scale.linear() | |
.range([0, width]) | |
.domain([-2.5,2.5]); | |
var y = d3.scale.linear() | |
.range([height, 0]) | |
.domain([-2.5,2.5]); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left"); | |
var svg = d3.select("#chart").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 + ")"); | |
d3.csv("wpca_candidates.csv", function(error, data) { | |
limits = {"x": [-2.5,2.5],"y": [-2.5,2.5]}; | |
lines = [{ | |
'a':0, | |
'b':-1.06, | |
'd1':-2.053, | |
'd2':-1.93, | |
'id': 1, | |
'description':'Možnosť vyhlásiť amnestiu'}, | |
{ | |
'a': 0.145, | |
'b': -0.177, | |
'd1':0.37, | |
'd2':2.07, | |
'description':'Imunita prezidenta'}, | |
{ | |
'a': 0.335, | |
'b': 2.7665358349, | |
'd1': 1.54, | |
'd2': -0.56, | |
'description':'Zákaz interupcií'}, | |
{ | |
'a': -1.019, | |
'b': 2.142, | |
'd1':-2.1, | |
'd2':0.98, | |
'description':'Registrované partnerstvo'}, | |
{ | |
'a': 3.99, | |
'b': -2.87, | |
'd1':-0.85, | |
'd2':-0.3, | |
'description':'Kotleba pozývaný ako iní župani'}, | |
{ | |
'a': -0.27, | |
'b': -0.21, | |
'd1':0.33, | |
'd2':1.6, | |
'description':'Obch. vzťahy x ľudská práva'}, | |
{ | |
'a': 2.6613760408, | |
'b': -2.5781557199, | |
'd1':-0.16, | |
'd2':-0.06, | |
'description':'Rómčina', | |
'quality':1 }, | |
{ | |
'a': 6.38, | |
'b': -3.16, | |
'd1':-0.33, | |
'd2':-0.1, | |
'description':'Voľby cez internet'}, | |
{ | |
'a': -2.5817095387, | |
'b': 21.1590242094, | |
'd1':-0.49199217, | |
'd2':0.02325212, | |
'description':'Maďarčina - stejné postavenie', | |
'quality': 1 | |
}, | |
]; | |
for (k in lines) { | |
ps = linecross(lines[k],limits); | |
if (ps.length == 2) { | |
lines[k].x1 = ps[0][0]; | |
lines[k].y1 = ps[0][1]; | |
lines[k].x2 = ps[1][0]; | |
lines[k].y2 = ps[1][1]; | |
} | |
way = get_sign(lines[k].b,lines[k].d1,lines[k].d2); | |
lines[k].path =[corners(lines[k],limits,way),corners(lines[k],limits,-1*way)]; | |
} | |
var line = svg.selectAll ('g') | |
.data(lines) | |
.enter().append('g') | |
.attr("class",function(d) {if (d.quality == 1) return "line quality-1"; else return "line";}) | |
.attr("id", function (d, i) {return "q-" + i;}) | |
.on("mouseover", function (d, i) { | |
d3.select(this).classed({"active":true}); | |
highlight_persons(data,i); | |
}) | |
.on("mouseout",function (d,i) { | |
d3.select(this).classed({"active":false}); | |
dehighlight_persons(i); | |
}) | |
line.append("line") | |
.attr("x1",function(d) {return x(d.x1)}) | |
.attr("y1",function(d) {return y(d.y1)}) | |
.attr("x2",function(d) {return x(d.x2)}) | |
.attr("y2",function(d) {return y(d.y2)}); | |
line.append("text") | |
.attr("text-anchor", "middle") | |
.attr("class","line-text") | |
.text(function (d, i) { | |
return d.description; | |
}) | |
.attr("x", function(d, i) { | |
if (d.x1 > d.x2) | |
return x(d.x2); | |
else | |
return x(d.x1); | |
}) | |
.attr("y", function(d, i) { | |
if (d.x1 > d.x2) | |
return y(d.y1); | |
else | |
return y(d.y2); | |
}) | |
.style("text-anchor", "start") | |
.attr("transform", function(d, i) { | |
rot=-Math.atan(d.b)/2/Math.PI*360; //d.slope; | |
if (d.x1 > d.x2){ | |
xx = x(d.x1); | |
yy = y(d.y1); | |
} else { | |
xx = x(d.x2); | |
yy = y(d.y2); | |
} | |
return "rotate(" + rot +"," +xx + "," + yy +")"}); | |
//explanation: http://code.hazzens.com/d3tut/lesson_3.html | |
var area = d3.svg.line() | |
.x(function(d) {return x(d.x);}) | |
.y(function(d) {return y(d.y);}); | |
var areas = line.selectAll("path.area") | |
.data(function(d) { | |
return d.path; | |
}).enter() | |
.append("path") | |
.attr("class",function(d, i) {if (i == 0) return "area yes"; else return "area no";}) | |
.attr("d",area); | |
var point = svg.selectAll ('.candidate') | |
.data(data) | |
.enter().append("g") | |
.attr("class","candidate") | |
.attr("id", function (d, i) {return "p-" + i;}) | |
.on("mouseover", function (d, i) { | |
d3.select(this).attr("class","candidate active"); | |
}) | |
.on("mouseout",function (d,i) { | |
d3.select(this).attr("class","candidate"); | |
}) | |
point.append('circle') | |
.attr("cx", function(d) { | |
return x(d.d1) | |
}) | |
.attr("cy", function(d) {return y(d.d2) }) | |
.attr("r", 10) | |
.attr("class", "point") | |
point.append('text') | |
.text(function (d, i) {return d.name; }) | |
.attr("x", function(d) {return x(d.d1) }) | |
.attr("y", function(d) {return y(d.d2) }) | |
.attr("r", 10) | |
.attr("class", "name") | |
.style("text-anchor","middle") | |
}) | |
function highlight_persons(values,index) { | |
//cannot use jQuery addClass with svg ! | |
//http://stackoverflow.com/questions/8638621/jquery-svg-why-cant-i-addclass | |
$(".candidate").each(function(i,e) { | |
$(this).attr('class',function() { | |
if (parseInt(values[i]['q'+index]) == 1) return 'candidate yes'; | |
if (parseInt(values[i]['q'+index]) == -1) return 'candidate no'; | |
return; | |
}); | |
}); | |
} | |
function dehighlight_persons(index) { | |
$(".candidate").attr('class','candidate'); | |
} | |
function corners(l,e,o) { | |
//l = {"a":0,"b":1} //line, a and slope, i.e., y=a+bx | |
//e = {"x": [-10,10], "y": [-10,10]} //limits | |
//o = 1 //orientation -1 or 1 | |
//crossing x0, x1 | |
//crossing y0, y1 | |
outp = linecross (l,e); | |
out = []; | |
//vertices | |
for (i=0;i<=1;i++){ | |
for (j=0;j<=1;j++){ | |
if (o*(l.a+l.b*e.x[i]-e.y[j]) > 0) | |
outp.push([e.x[i],e.y[j]]); | |
} | |
} | |
//sort the outps, anticlockwise | |
if (outp.length > 0) { | |
mid = [0,0]; | |
for (i in outp) { | |
mid[0] += outp[i][0]; | |
mid[1] += outp[i][1]; | |
} | |
mid[0] = mid[0] / outp.length; | |
mid[1] = mid[1] / outp.length; | |
for (i in outp) { | |
p = outp[i][1] - mid[1]; | |
q = outp[i][0] - mid[0]; | |
if (q != 0) | |
outp[i][2] = Math.atan(p/q) + (q<0 ? Math.PI : 0); | |
else | |
outp[i][2] = Math.PI/2 + Math.PI*sign(p); | |
} | |
outp = outp.sort(function(w,z) { | |
return w[2] > z[2]; | |
}); | |
for (i in outp) { | |
outp[i].splice(2,1); | |
out.push({"x":outp[i][0],"y":outp[i][1]}); | |
} | |
} | |
return out; | |
} | |
function linecross (l,e) { | |
out = []; | |
//crossing x0, x1 | |
for (i=0;i<=1;i++){ | |
Y = l.a + l.b*e.x[i]; | |
if ((Y > e.y[0]) && (Y < e.y[1])) | |
out.push([e.x[i],Y]); | |
} | |
//crossing y0, y1 | |
for (j=0;j<=1;j++){ | |
if (l.b != 0) { | |
X = (e.y[j] - l.a)/l.b; | |
if ((X > e.x[0]) && (X < e.x[1])) | |
out.push([X,e.y[j]]); | |
} | |
} | |
return out; | |
} | |
function get_sign(b,d1,d2) { | |
t = b*d1-d2; | |
if (t > 0) return 1; | |
if (t < 0) return -1; | |
return 0; | |
} | |
function sign(x) { | |
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN; | |
} | |
</script> | |
<script> | |
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
ga('create', 'UA-8592359-13', 'ocks.org'); | |
ga('send', 'pageview'); | |
</script> |
name | d1 | d2 | q0 | q1 | q2 | q3 | q4 | q5 | q6 | q7 | q8 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Melník | 1.07849065 | -0.1191408 | -1 | -1 | 1 | -1 | 1 | 1 | 0 | 1 | -1 | |
Martinčko | -1.99039949 | 0.745852 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | |
Šimko | -1.57697495 | -1.2902898 | 1 | -1 | -1 | 1 | 1 | -1 | 1 | 1 | 1 | |
Čarnogurský | 0.03047429 | -1.9369032 | 1 | -1 | 1 | -1 | 1 | -1 | 1 | 1 | -1 | |
Kiska | -1.01942107 | 2.3123596 | -1 | 1 | -1 | 1 | 1 | 1 | 1 | 1 | -1 | |
Procházka | 0.81328076 | 0.7885653 | -1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | -1 | |
Hrušovský | 2.39481329 | 0.6917826 | -1 | 1 | 1 | -1 | -1 | 1 | 1 | -1 | -1 | |
Bárdos | 1.73643192 | 0.6233822 | -1 | 1 | 1 | -1 | -1 | 1 | 1 | 1 | 1 | |
Mezenská | 0.32224881 | -2.1949306 | 1 | -1 | 1 | -1 | 1 | -1 | 1 | 1 | -1 | |
Kňažko | -1.78894421 | 0.3793228 | 1 | 0 | -1 | 1 | 1 | 1 | 1 | 1 | -1 |