block by michalskop 9246304

SK: Spatial distribution of presidential candidates 2014

Full Screen

Spatial distribution of presidential candidates, SK 2014

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.

index.html

<!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="//d3js.org/d3.v3.js"></script>
 <script src="//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: //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 !
  ////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>

wpca_candidates.csv

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