index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scatterplot Chart</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="scatter plot">
<meta name="author" content="Michal Škop">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="./d3.scatterplotwithlineplot.js"></script>
<script src="./d3.tips.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/simplex/bootstrap.min.css" />
<style type="text/css">
text {
font-family: sans-serif;
}
.tick {
fill-opacity: 0;
stroke: #000000;
stroke-width: 1;
}
.domain {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
}
.axis line {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
stroke: gray;
}
circle {
fill-opacity: .3;
stroke: black;
stroke-opacity: 0.99;
}
circle:hover {
fill-opacity: 1;
}
.label {
font-family: sans-serif;
font-size: 15px;
}
.cssd {
fill: orange;
stroke: orange;
}
.kscm {
fill: red;
stroke: red;
}
.ods {
fill: blue;
stroke: blue;
}
.top-09 {
fill: #b0b;
stroke: #b0b;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
pointer-events: none;
max-width: 250px;
}
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
position: absolute;
pointer-events: none;
}
.d3-tip:after {
content: "\25BC";
margin: -1px 0 0 0;
top: 100%;
left: 0;
text-align: center;
}
line {
stroke:gray;
stroke-width:1;
opacity: .15;
}
.perfect {
stroke: gray;
stroke-width:3;
opacity: 0.5;
}
.part-1 {
stroke: red;
stroke-width:1;
opacity: 0.15;
}
.part-2 {
stroke: green;
stroke-width:1;
opacity: 0.15;
}
.part-3 {
stroke: blue;
stroke-width:1;
opacity: 0.15;
}
</style>
</head>
<body>
<div class="navbar navbar-default">
<div class="navbar-header">
<h1 class="navbar-brand">Prague 2010 - 2014</h1>
</div>
</div>
<div id="chart"></div>
<div class="alert alert-info">
WPCA
</div>
<script type="text/javascript">
d3.csv("praha-2010-2014-roll-call-votes-wpca-cutting-lines.csv",function(error,lines) {
d3.csv("praha-2010-2014-roll-call-votes-wpca.csv",function(error,csvdata) {
linesselected = []
for (k in lines) {
if ((parseFloat(lines[k]['w2']) > 0.5) && (parseFloat(lines[k]['cl_beta0']) < 50)) {
beta = [lines[k]['normal_x'],lines[k]['normal_y']];
beta0 = lines[k]['cl_beta0'];
if (beta[1] != 0) {
lines[k]['a'] = -beta0/beta[1];
lines[k]['b'] = -beta[0]/beta[1];
} else {
lines[k]['a'] = 0;
lines[k]['b'] = 0;
}
if (k < 690) {
lines[k]['class'] = 'part-1';
} else {
if (k < 1815) {
lines[k]['class'] = 'part-2';
} else {
lines[k]['class'] = 'part-3';
}
}
lines[k]['name'] = "i:"+k+"<br>w1:"+lines[k]['w1']+"<br>w2:"+lines[k]['w2']+"<br>loss:"+lines[k]['loss']+"<br>name:"+lines[k]['motion:name'];
linesselected.push(lines[k]);
}
}
spdata = [];
csvdata.forEach(function(d) {
spdata.push({"x":d["wpca:d1"],"y":d["wpca:d2"],"r":1,"class":d["class"],"name":d["name"]})
});
var scatterplotwithlineplot = [{
"data": spdata,
"margin": {top: 10, right: 10, bottom: 30, left: 30},
"axes": {"labels":{"x":"Dimension 1", "y":"Dimension 2"}},
"minmax":{"x":{'min':-18,'max':18},"y":{'min':-18,'max':18},"r":{'min':0,'max':1},"rrange":{'min':0,'max':10}},
"size":{"width":600,"height":400},
"lines": linesselected
}];
var svg = d3.select("#chart")
.append("svg")
.attr("width",scatterplotwithlineplot[0]['size']['width'])
.attr("height",scatterplotwithlineplot[0]['size']['height']);
tip = d3.tip().attr('class', 'd3-tip').html(function(d) {
return "<span class=\'stronger\'>" + d["name"] + "</span><br>";
});
svg.call(tip)
var sp = d3.scatterplotwithlineplot()
.data(function(d) {return d.data})
.margin(function(d) {return d.margin})
.axes(function(d) {return d.axes})
.minmax(function(d) {return d.minmax})
.size(function(d) {return d.size})
.lines(function(d) {return d.lines})
var scatter = svg.selectAll(".scatterplot")
.data(scatterplotwithlineplot)
.enter()
.append("svg:g")
.attr("transform", "translate(" + scatterplotwithlineplot[0].margin.left + "," + scatterplotwithlineplot[0].margin.top + ")")
.call(sp);
});
});
</script>
</body>
</html>
d3.scatterplot.js
d3.scatterplot = function() {
function scatterplot(selection) {
selection.each(function(d, i) {
var data = (typeof(data) === "function" ? data(d) : d.data),
margin = (typeof(margin) === "function" ? margin(d) : d.margin),
axes = (typeof(axes) === "function" ? axes(d) : d.axes),
minmax = (typeof(minmax) === "function" ? minmax(d) : d.minmax),
size = (typeof(size) === "function" ? size(d) : d.size);
var width = size['width'] - margin.left - margin.right,
height = size['height'] - margin.top - margin.bottom;
var xScale = d3.scale.linear()
.domain([minmax['x']['min'], minmax['x']['max']])
.range([0, width])
var yScale = d3.scale.linear()
.domain([minmax['y']['min'], minmax['y']['max']])
.range([height, 0])
var rScale = d3.scale.linear()
.domain([minmax['r']['min'],minmax['r']['max']])
.range([minmax['rrange']['min'],minmax['rrange']['max']]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var element = d3.select(this);
element.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
element.append("g")
.attr("class", "axis y-axis")
.call(yAxis);
element.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.x);
})
.attr("cy", function(d) {
return yScale(d.y);
})
.attr("r", function(d) {
return rScale(1);
})
.attr("class", function(d) {
if (typeof(d['class'] != 'undefined')) return d['class'];
else return 'circle';
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
element.append("text")
.attr("class", "x-label label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height-5)
.text(axes['labels']['x']);
element.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 5)
.attr("x", 0)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text(axes['labels']['y']);
});
}
scatterplot.data = function(value) {
if (!arguments.length) return value;
data = value;
return scatterplot;
};
scatterplot.margin = function(value) {
if (!arguments.length) return value;
margin = value;
return scatterplot;
};
scatterplot.axes = function(value) {
if (!arguments.length) return value;
axes = value;
return scatterplot;
};
scatterplot.minmax = function(value) {
if (!arguments.length) return value;
minmax = value;
return scatterplot;
};
scatterplot.size = function(value) {
if (!arguments.length) return value;
size = value;
return scatterplot;
};
return scatterplot;
}
d3.scatterplotwithlineplot.js
d3.scatterplotwithlineplot = function() {
function scatterplotwithlineplot(selection) {
selection.each(function(d, i) {
var data = (typeof(data) === "function" ? data(d) : d.data),
lines = (typeof(lines) === "function" ? lines(d) : d.lines),
margin = (typeof(margin) === "function" ? margin(d) : d.margin),
axes = (typeof(axes) === "function" ? axes(d) : d.axes),
minmax = (typeof(minmax) === "function" ? minmax(d) : d.minmax),
size = (typeof(size) === "function" ? size(d) : d.size);
var width = size['width'] - margin.left - margin.right,
height = size['height'] - margin.top - margin.bottom;
var xScale = d3.scale.linear()
.domain([minmax['x']['min'], minmax['x']['max']])
.range([0, width])
var yScale = d3.scale.linear()
.domain([minmax['y']['min'], minmax['y']['max']])
.range([height, 0])
var rScale = d3.scale.linear()
.domain([minmax['r']['min'],minmax['r']['max']])
.range([minmax['rrange']['min'],minmax['rrange']['max']]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var element = d3.select(this);
element.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
element.append("g")
.attr("class", "axis y-axis")
.call(yAxis);
limits = {"x":[minmax["x"]["min"],minmax["x"]["max"]],"y":[minmax["y"]["min"],minmax["y"]["max"]]}
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].n1,lines[k].n2);
lines[k].path =[corners(lines[k],limits,way),corners(lines[k],limits,-1*way)];
}
var line = element.selectAll ('.line')
.data(lines)
.enter()
.append("line")
.attr("x1",function(d) {return xScale(d.x1)})
.attr("y1",function(d) {return yScale(d.y1)})
.attr("x2",function(d) {return xScale(d.x2)})
.attr("y2",function(d) {return yScale(d.y2)})
.attr("id", function (d, i) {return "q-" + i;})
.attr("class", function(d) {
if (typeof(d['class'] != 'undefined')) return d['class'];
else return 'line';
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
element.selectAll(".circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.x);
})
.attr("cy", function(d) {
return yScale(d.y);
})
.attr("r", function(d) {
return rScale(1);
})
.attr("class", function(d) {
if (typeof(d['class'] != 'undefined')) return d['class'];
else return 'circle';
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
element.append("text")
.attr("class", "x-label label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height-5)
.text(axes['labels']['x']);
element.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 5)
.attr("x", 0)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text(axes['labels']['y']);
});
}
scatterplotwithlineplot.data = function(value) {
if (!arguments.length) return value;
data = value;
return scatterplotwithlineplot;
};
scatterplotwithlineplot.lines = function(value) {
if (!arguments.length) return value;
lines = value;
return scatterplotwithlineplot;
};
scatterplotwithlineplot.margin = function(value) {
if (!arguments.length) return value;
margin = value;
return scatterplotwithlineplot;
};
scatterplotwithlineplot.axes = function(value) {
if (!arguments.length) return value;
axes = value;
return scatterplotwithlineplot;
};
scatterplotwithlineplot.minmax = function(value) {
if (!arguments.length) return value;
minmax = value;
return scatterplotwithlineplot;
};
scatterplotwithlineplot.size = function(value) {
if (!arguments.length) return value;
size = value;
return scatterplotwithlineplot;
};
return scatterplotwithlineplot;
function corners(l,e,o) {
outp = linecross (l,e);
out = [];
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]]);
}
}
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 = [];
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]);
}
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;
}
}
d3.tips.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['d3'], factory)
} else if (typeof module === 'object' && module.exports) {
module.exports = function(d3) {
d3.tip = factory(d3)
return d3.tip
}
} else {
root.d3.tip = factory(root.d3)
}
}(this, function (d3) {
return function() {
var direction = d3_tip_direction,
offset = d3_tip_offset,
html = d3_tip_html,
node = initNode(),
svg = null,
point = null,
target = null
function tip(vis) {
svg = getSVGNode(vis)
point = svg.createSVGPoint()
document.body.appendChild(node)
}
tip.show = function() {
var args = Array.prototype.slice.call(arguments)
if(args[args.length - 1] instanceof SVGElement) target = args.pop()
var content = html.apply(this, args),
poffset = offset.apply(this, args),
dir = direction.apply(this, args),
nodel = d3.select(node),
i = directions.length,
coords,
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
nodel.html(content)
.style({ opacity: 1, 'pointer-events': 'all' })
while(i--) nodel.classed(directions[i], false)
coords = direction_callbacks.get(dir).apply(this)
nodel.classed(dir, true).style({
top: (coords.top + poffset[0]) + scrollTop + 'px',
left: (coords.left + poffset[1]) + scrollLeft + 'px'
})
return tip
}
tip.hide = function() {
var nodel = d3.select(node)
nodel.style({ opacity: 0, 'pointer-events': 'none' })
return tip
}
tip.attr = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).attr(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.attr.apply(d3.select(node), args)
}
return tip
}
tip.style = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).style(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.style.apply(d3.select(node), args)
}
return tip
}
tip.direction = function(v) {
if (!arguments.length) return direction
direction = v == null ? v : d3.functor(v)
return tip
}
tip.offset = function(v) {
if (!arguments.length) return offset
offset = v == null ? v : d3.functor(v)
return tip
}
tip.html = function(v) {
if (!arguments.length) return html
html = v == null ? v : d3.functor(v)
return tip
}
function d3_tip_direction() { return 'n' }
function d3_tip_offset() { return [0, 0] }
function d3_tip_html() { return ' ' }
var direction_callbacks = d3.map({
n: direction_n,
s: direction_s,
e: direction_e,
w: direction_w,
nw: direction_nw,
ne: direction_ne,
sw: direction_sw,
se: direction_se
}),
directions = direction_callbacks.keys()
function direction_n() {
var bbox = getScreenBBox()
return {
top: bbox.n.y - node.offsetHeight,
left: bbox.n.x - node.offsetWidth / 2
}
}
function direction_s() {
var bbox = getScreenBBox()
return {
top: bbox.s.y,
left: bbox.s.x - node.offsetWidth / 2
}
}
function direction_e() {
var bbox = getScreenBBox()
return {
top: bbox.e.y - node.offsetHeight / 2,
left: bbox.e.x
}
}
function direction_w() {
var bbox = getScreenBBox()
return {
top: bbox.w.y - node.offsetHeight / 2,
left: bbox.w.x - node.offsetWidth
}
}
function direction_nw() {
var bbox = getScreenBBox()
return {
top: bbox.nw.y - node.offsetHeight,
left: bbox.nw.x - node.offsetWidth
}
}
function direction_ne() {
var bbox = getScreenBBox()
return {
top: bbox.ne.y - node.offsetHeight,
left: bbox.ne.x
}
}
function direction_sw() {
var bbox = getScreenBBox()
return {
top: bbox.sw.y,
left: bbox.sw.x - node.offsetWidth
}
}
function direction_se() {
var bbox = getScreenBBox()
return {
top: bbox.se.y,
left: bbox.e.x
}
}
function initNode() {
var node = d3.select(document.createElement('div'))
node.style({
position: 'absolute',
top: 0,
opacity: 0,
'pointer-events': 'none',
'box-sizing': 'border-box'
})
return node.node()
}
function getSVGNode(el) {
el = el.node()
if(el.tagName.toLowerCase() === 'svg')
return el
return el.ownerSVGElement
}
function getScreenBBox() {
var targetel = target || d3.event.target;
while ('undefined' === typeof targetel.getScreenCTM && 'undefined' === targetel.parentNode) {
targetel = targetel.parentNode;
}
var bbox = {},
matrix = targetel.getScreenCTM(),
tbbox = targetel.getBBox(),
width = tbbox.width,
height = tbbox.height,
x = tbbox.x,
y = tbbox.y
point.x = x
point.y = y
bbox.nw = point.matrixTransform(matrix)
point.x += width
bbox.ne = point.matrixTransform(matrix)
point.y += height
bbox.se = point.matrixTransform(matrix)
point.x -= width
bbox.sw = point.matrixTransform(matrix)
point.y -= height / 2
bbox.w = point.matrixTransform(matrix)
point.x += width
bbox.e = point.matrixTransform(matrix)
point.x -= width / 2
point.y -= height / 2
bbox.n = point.matrixTransform(matrix)
point.y += height
bbox.s = point.matrixTransform(matrix)
return bbox
}
return tip
};
}));
load_datapackage.py
import csv
import json
import operator
datapackage = json.loads(open('datapackage.json').read())
def row2header(row):
header = {}
j = 0
for h in row:
header[h] = j
j = j + 1
return header
def row2data(row,header):
out = {}
j = 0
for h in header:
out[h] = row[header[h]]
return out
dp = {}
for resource in datapackage['resources']:
if resource["name"] == "people":
dp["people"] = resource
if resource["name"] == "vote_events":
dp["vote_events"] = resource
if resource["name"] == "votes":
dp["votes"] = resource
headers = {}
data = {"people":[],"votes":[],"vote_events":[]}
for key in data:
i = 0
with open(dp[key]["path"], 'r') as csvfile:
csvreader = csv.reader(csvfile)
for row in csvreader:
if i == 0:
headers[key] = row2header(row)
else:
data[key].append(row2data(row,headers[key]))
i = i + 1
people = {}
for person in data["people"]:
people[person['identifier']] = person
vote_events = {}
for vote_event in data["vote_events"]:
vote_events[vote_event['identifier']] = vote_event
data["votes"] = sorted(data["votes"],key=operator.itemgetter("vote_event_id","voter_id"))
praha-2010-2014-roll-call-votes-wnominate.csv
identifier,name,party_suggesting,wnominate:d1,wnominate:d2,class
3167,Jan Slezák,ČSSD,0.844365298748,-0.475551307201,cssd
3169,Milan Urban,TOP 09,-0.847399175167,-0.141846179962,top-09
3184,Pavel Hurda,ODS,0.475262463093,0.576651632786,ods
3186,Ondřej Pecha,ODS,0.458752334118,0.538636863232,ods
3230,Marie Kousalíková,ODS,0.536619365215,0.43625587225,ods
3337,Josef Nosek,ODS,0.552258729935,0.653618454933,ods
3464,Pavel Klega,ODS,0.58333581686,0.480701893568,ods
3472,Miloslav Ludvík,ČSSD,0.867789626122,-0.436195641756,cssd
3478,Miroslav Poche,ČSSD,0.849666297436,-0.527320802212,cssd
3492,František Adámek,ČSSD,0.851758241653,-0.389698952436,cssd
3762,Marta Semelová,KSČM,0.913647353649,-0.217486530542,kscm
3926,Antonín Weinert,ČSSD,0.825632989407,-0.48580211401,cssd
3967,Karel Březina,ČSSD,0.847999513149,-0.444134861231,cssd
4014,Rudolf Blažek,ODS,0.763845920563,0.556767642498,ods
4041,Petr Hulinský,ČSSD,0.860926926136,-0.497503489256,cssd
4048,Radek Lohynský,ODS,0.418140858412,0.675759851933,ods
4065,Dalibor Mlejnský,ODS,0.57928019762,0.282766759396,ods
4085,Daniel Hodek,ČSSD,0.833508431911,-0.446265757084,cssd
4093,Petr Šimůnek,KSČM,0.877711713314,-0.239676296711,kscm
4306,Petr Bříza,ODS,0.509833693504,0.513472616673,ods
4307,Monika Krobová Hášová,TOP 09,-0.854757726192,-0.257377713919,top-09
4308,Albert Kubišta,TOP 09,-0.945103883743,-0.0787498727441,top-09
4309,Lubomír Ledl,KSČM,0.911291837692,-0.185980215669,kscm
4310,Jiří Pařízek,TOP 09,-0.910431981087,-0.140326648951,top-09
4311,Roman Petrus,ČSSD,0.861748456955,-0.503026068211,cssd
4312,Lukáš Plachý,ČSSD,0.857482492924,-0.465514570475,cssd
4313,Ondřej Počarovský,TOP 09,-0.901716709137,-0.227473780513,top-09
4314,Pavel Richter,TOP 09,-0.913848996162,0.0448365844786,top-09
4315,Vladimír Schmalz,ODS,0.534595668316,0.381742298603,ods
4316,Bohuslav Svoboda,ODS,0.566491723061,0.75400698185,ods
4317,Michal Štěpán,TOP 09,-0.967347860336,-0.173068419099,top-09
4318,Nataša Šturmová,TOP 09,-0.909221410751,-0.0676190182567,top-09
4319,Věra Šturmová,TOP 09,-0.901336550713,-0.0591023527086,top-09
4320,Ludmila Štvánová,TOP 09,-0.85670620203,-0.101598240435,top-09
4321,Lenka Teska Arnoštová,ČSSD,0.890404760838,-0.405343979597,cssd
4322,Petr Trombik,TOP 09,-0.865648150444,-0.180411010981,top-09
4323,Zdeněk Tůma,TOP 09,-0.83565813303,-0.0728031247854,top-09
4324,Andrea Vlásenková,ODS,0.420827120543,0.304606795311,ods
4325,Eva Vorlíčková,TOP 09,-0.999399662018,-0.0193237327039,top-09
4339,Zuzana Bonhomme Hankeová,TOP 09,-0.847445130348,-0.108895994723,top-09
4340,Iveta Borská,TOP 09,-0.903543591499,-0.100302673876,top-09
4341,Jiří Dienstbier,ČSSD,0.858711361885,-0.312924027443,cssd
4342,Martin Dlouhý,TOP 09,-0.879456400871,-0.0642564818263,top-09
4343,Petr Dodal,TOP 09,-0.825085878372,-0.132989287376,top-09
4344,Petr Dolínek,ČSSD,0.863325178623,-0.504648208618,cssd
4345,Milan Růžička,TOP 09,-0.905564665794,-0.137348085642,top-09
4346,Petr Hána,ODS,0.712317824364,0.701857089996,ods
4347,Tomáš Hudeček,TOP 09,-0.996660590172,0.0816562920809,top-09
4348,Helena Chudomelová,TOP 09,-0.833634972572,-0.0807498544455,top-09
4349,Gabriela Kloudová,ODS,0.725271046162,0.193118587136,ods
4350,Alexandra Udženija,ODS,0.427044451237,0.608168005943,ods
4351,Jan Vašíček,TOP 09,-0.89971780777,-0.107256688178,top-09
4352,Jiří Vávra,TOP 09,-0.898380100727,-0.156385675073,top-09
4353,David Zelený,ODS,0.762448370457,0.6470490098,ods
4369,Ivan Kabický,ODS,0.408881902695,0.44592782855,ods
4370,Jan Kalousek,ODS,0.484505742788,0.874788165092,ods
4371,Lukáš Kaucký,ČSSD,0.865863323212,-0.474781185389,cssd
4372,Jiří Liška,TOP 09,-0.832347452641,-0.0914602428675,top-09
4373,Lukáš Manhart,TOP 09,-0.887736558914,0.0477734580636,top-09
4374,Jiří Nouza,TOP 09,-0.8783390522,-0.152244329453,top-09
4375,Václav Novotný,TOP 09,-0.990503966808,-0.13748383522,top-09
4683,Boris Šťastný,ODS,0.707905709743,0.142590999603,ods
4684,Milan Richter,ODS,0.699684739113,0.20471675694,ods
4355,Roman Vaculka,TOP 09,-0.894880175591,-0.041933003813,top-09
4624,Lucie Válová,ČSSD,0.72899967432,-0.68451410532,cssd
3484,Bohumil Zoufalík,ODS,0.974843025208,0.222892835736,ods
4216,Ivan Vinš,ODS,0.902668476105,0.430336415768,ods
praha-2010-2014-roll-call-votes-wpca.csv
identifier,name,party_suggesting,wpca:d1,wpca:d2,wpca:d3,class
3167,Jan Slezák,ČSSD,14.9448913417,8.91394878397,-3.21591231859,cssd
3169,Milan Urban,TOP 09,-15.3748324175,1.11455291993,0.44803846422,top-09
3184,Pavel Hurda,ODS,9.12297995397,-9.76884276446,-2.39501339437,ods
3186,Ondřej Pecha,ODS,8.69567121532,-10.2378197542,-2.92427933734,ods
3230,Marie Kousalíková,ODS,9.40727040416,-8.20110944296,-1.95316085614,ods
3337,Josef Nosek,ODS,8.77587623575,-11.4494284611,0.850965415615,ods
3464,Pavel Klega,ODS,12.2114585445,-7.74100841675,-0.99193327788,ods
3472,Miloslav Ludvík,ČSSD,14.8652343415,9.46064423766,-2.05723820701,cssd
3478,Miroslav Poche,ČSSD,14.8596172767,10.0210779125,-2.14372491869,cssd
3492,František Adámek,ČSSD,14.5024074131,8.73706190798,-1.70622520893,cssd
3762,Marta Semelová,KSČM,6.70810009286,8.38799647887,18.2859774319,kscm
3926,Antonín Weinert,ČSSD,14.5166529089,8.16301067442,-3.27060368418,cssd
3967,Karel Březina,ČSSD,14.6722047046,8.86955896582,-2.49140799807,cssd
4014,Rudolf Blažek,ODS,14.0756781942,-7.57277224975,1.79203632159,ods
4041,Petr Hulinský,ČSSD,14.6165725807,8.48349548493,-3.46141454684,cssd
4048,Radek Lohynský,ODS,9.26097574529,-11.4755028488,-0.815309815961,ods
4065,Dalibor Mlejnský,ODS,12.3475140963,-7.37565857618,0.32402657255,ods
4085,Daniel Hodek,ČSSD,14.5856286848,9.24372564465,-2.6552106916,cssd
4093,Petr Šimůnek,KSČM,5.64457615356,7.81071411201,18.0761207454,kscm
4306,Petr Bříza,ODS,8.52268814436,-10.0034727305,-2.69095279661,ods
4307,Monika Krobová Hášová,TOP 09,-15.7385045648,1.19709678227,0.592001326003,top-09
4308,Albert Kubišta,TOP 09,-16.7535382329,0.0208135523753,-0.701271562167,top-09
4309,Lubomír Ledl,KSČM,7.40973632033,7.58344687105,16.5903965215,kscm
4310,Jiří Pařízek,TOP 09,-16.1592931596,0.535578730618,-0.287090270965,top-09
4311,Roman Petrus,ČSSD,14.9516971752,9.86404829653,-2.19871101468,cssd
4312,Lukáš Plachý,ČSSD,14.8901522218,9.22546208082,-2.19139248475,cssd
4313,Ondřej Počarovský,TOP 09,-15.4120243793,0.715862557805,0.331802354634,top-09
4314,Pavel Richter,TOP 09,-15.964990418,-0.249981477982,-0.331502564035,top-09
4315,Vladimír Schmalz,ODS,11.5786064452,-8.71689840299,-0.0598377011954,ods
4316,Bohuslav Svoboda,ODS,7.16721325286,-10.8514839467,-2.50328233146,ods
4317,Michal Štěpán,TOP 09,-16.4127429929,0.898065918316,-0.141708885424,top-09
4318,Nataša Šturmová,TOP 09,-16.0413535321,0.10115952636,-0.764436585476,top-09
4319,Věra Šturmová,TOP 09,-16.307839438,0.487318373523,-0.316679084316,top-09
4320,Ludmila Štvánová,TOP 09,-15.5486690248,-0.190775443673,0.16079568745,top-09
4321,Lenka Teska Arnoštová,ČSSD,15.5557054536,8.51455748663,-1.1819797874,cssd
4322,Petr Trombik,TOP 09,-15.2692992527,1.25352992483,-0.0972574959438,top-09
4323,Zdeněk Tůma,TOP 09,-15.5794332835,0.557293380133,-0.0294558496958,top-09
4324,Andrea Vlásenková,ODS,8.00885693441,-9.36684457483,-2.31562372631,ods
4325,Eva Vorlíčková,TOP 09,-16.6728192875,0.0311663333347,0.294848227939,top-09
4339,Zuzana Bonhomme Hankeová,TOP 09,-15.3929855567,0.488144192104,0.653984236363,top-09
4340,Iveta Borská,TOP 09,-16.2349424056,1.11885077298,0.681336742804,top-09
4341,Jiří Dienstbier,ČSSD,11.2962677103,3.76507800902,-1.33270907171,cssd
4342,Martin Dlouhý,TOP 09,-16.1170059939,0.246043657009,-0.55134444397,top-09
4344,Petr Dolínek,ČSSD,14.7636760725,10.0461686607,-2.15977390887,cssd
4345,Milan Růžička,TOP 09,-15.8920771975,1.14315550434,0.727702750821,top-09
4346,Petr Hána,ODS,10.8872860406,-10.6601958938,0.236143694465,ods
4347,Tomáš Hudeček,TOP 09,-15.8916389329,-0.245730574682,-0.345553103284,top-09
4348,Helena Chudomelová,TOP 09,-14.8498246746,-0.0536473750414,-0.474812617157,top-09
4349,Gabriela Kloudová,ODS,14.7872145271,-3.91221886652,1.90038086045,ods
4350,Alexandra Udženija,ODS,8.16489704147,-10.3164376104,-2.55662574516,ods
4351,Jan Vašíček,TOP 09,-15.6949354549,0.742474861707,-0.451051207601,top-09
4352,Jiří Vávra,TOP 09,-15.3049736252,0.279939754848,-0.0798532725172,top-09
4353,David Zelený,ODS,12.2821781252,-7.98482174105,1.2173557931,ods
4369,Ivan Kabický,ODS,7.7193601371,-10.692439176,-2.96857736875,ods
4370,Jan Kalousek,ODS,7.94847479804,-11.9667893598,-0.883101622919,ods
4371,Lukáš Kaucký,ČSSD,15.0789402937,9.9056567827,-2.88065715213,cssd
4372,Jiří Liška,TOP 09,-14.9518414778,0.282122606948,0.342636631861,top-09
4373,Lukáš Manhart,TOP 09,-15.7859964626,-0.132459189767,-0.592785426162,top-09
4374,Jiří Nouza,TOP 09,-15.4100148016,0.740625720025,-0.165395433852,top-09
4375,Václav Novotný,TOP 09,-16.5745741837,0.205251168609,0.132878559258,top-09
4683,Boris Šťastný,ODS,13.0687938424,-1.75014720205,-6.79050669296,ods
4684,Milan Richter,ODS,14.4838955498,-0.307268368976,-6.54179693875,ods
4355,Roman Vaculka,TOP 09,-15.9777513845,-0.492901202876,-1.18178113292,top-09
4624,Lucie Válová,ČSSD,9.47309594441,17.3746135201,1.95379814233,cssd
3484,Bohumil Zoufalík,ODS,12.503735192,-11.0612380211,14.6245438748,ods
4216,Ivan Vinš,ODS,12.9935496716,-13.3179239811,15.0955134406,ods
prepare_matrix.r
Xsource = matrix(Xsourcevector,ncol=3,byrow=T)
dimnames(Xsource)[[2]] = c('vote_event_id','voter_id','option')
Xsource = as.data.frame(Xsource)
wnominate.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scatterplot Chart</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="scatter plot">
<meta name="author" content="Michal Škop">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="./d3.scatterplot.js"></script>
<script src="./d3.tips.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/simplex/bootstrap.min.css" />
<style type="text/css">
text {
font-family: sans-serif;
}
.tick {
fill-opacity: 0;
stroke: #000000;
stroke-width: 1;
}
.domain {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
}
.axis line {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.axis {
}
circle {
fill-opacity: .3;
stroke: black;
stroke-opacity: 0.99;
}
circle:hover {
fill-opacity: 1;
}
.year-2011 {
fill: #000;
stroke: #000;
}
.year-2013 {
fill: #fff;
fill-opacity: 0;
stroke: #000;
}
.label {
font-family: sans-serif;
font-size: 15px;
}
.cssd {
fill: orange;
stroke: orange;
}
.kscm {
fill: red;
stroke: red;
}
.ods {
fill: blue;
stroke: blue;
}
.top-09 {
fill: violet;
stroke: violet;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
pointer-events: none;
}
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
position: absolute;
pointer-events: none;
}
.d3-tip.n:after {
content: "\25BC";
margin: -1px 0 0 0;
top: 100%;
left: 0;
text-align: center;
}
.d3-tip.e:after {
content: "\25C0";
margin: -4px 0 0 0;
top: 50%;
left: -8px;
}
.d3-tip.s:after {
content: "\25B2";
margin: 0 0 1px 0;
top: -8px;
left: 0;
text-align: center;
}
.d3-tip.w:after {
content: "\25B6";
margin: -4px 0 0 -1px;
top: 50%;
left: 100%;
}
</style>
</head>
<body>
<div class="navbar navbar-default">
<div class="navbar-header">
<h1 class="navbar-brand">Prague 2010 - 2014</h1>
</div>
</div>
<div id="chart"></div>
<div class="alert alert-dismissable alert-info">
WNominate
</div>
<script type="text/javascript">
d3.csv("praha-2010-2014-roll-call-votes-wnominate.csv",function(error,csvdata) {
spdata = [];
csvdata.forEach(function(d) {
spdata.push({"x":d["wnominate:d1"],"y":-d["wnominate:d2"],"r":1,"class":d["class"],"name":d["name"]})
});
var scatterplot = [{
"data": spdata,
"margin": {top: 10, right: 10, bottom: 30, left: 30},
"axes": {"labels":{"x":"Dimension 1", "y":"Dimension 2"}},
"minmax":{"x":{'min':-1,'max':1},"y":{'min':-1,'max':1},"r":{'min':0,'max':1},"rrange":{'min':0,'max':10}},
"size":{"width":600,"height":400}
}];
var svg = d3.select("#chart")
.append("svg")
.attr("width",scatterplot[0]['size']['width'])
.attr("height",scatterplot[0]['size']['height']);
tip = d3.tip().attr('class', 'd3-tip').html(function(d) {
return "<span class=\'stronger\'>" + d["name"] + "</span><br>";
});
svg.call(tip)
var sp = d3.scatterplot()
.data(function(d) {return d.data})
.margin(function(d) {return d.margin})
.axes(function(d) {return d.axes})
.minmax(function(d) {return d.minmax})
.size(function(d) {return d.size})
var scatter = svg.selectAll(".scatterplot")
.data(scatterplot)
.enter()
.append("svg:g")
.attr("transform", "translate(" + scatterplot[0].margin.left + "," + scatterplot[0].margin.top + ")")
.call(sp);
});
</script>
</body>
</html>
wnominate.py
import csv
import json
import operator
import numpy
import rpy2.robjects as robjects
r=robjects.r
exec(open("load_datapackage.py").read())
Xsourcevector = []
for row in data["votes"]:
Xsourcevector.append(row["vote_event_id"])
Xsourcevector.append(row["voter_id"])
Xsourcevector.append(row["option"])
robjects.globalenv["Xsourcevector"] = robjects.StrVector(Xsourcevector)
r.source("prepare_matrix.r")
r.source("wnominate_script.r")
with open(datapackage['name']+'-wnominate.csv', 'w') as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(["identifier","name","candidate_list","wnominate:d1","wnominate:d2"])
rWNpositions = numpy.array(robjects.globalenv['WNpositions'])
rXpeople = robjects.globalenv['Xpeople']
rXvote_events = robjects.globalenv['Xvote_events']
i = 0
for row in rWNpositions:
if row[0] != 'NA':
csvwriter.writerow([people[rXpeople[i]]['identifier'],people[rXpeople[i]]['name'],people[rXpeople[i]]['candidate_list'],row[0],row[1]])
i = i + 1
wnominate_script.r
# INPUT PARAMETERS
# _X_RAW_DB, _LO_LIMIT_1
# raw data in csv using db structure, i.e., a single row contains:
# code of representative, code of division, encoded vote (i.e. one of -1, 1, 0, NA)
# for example:
# “Joe Europe”,”Division-007”,”1”
#Xsource = read.csv("data/votes.csv")
Xsource$vote_event_id = as.factor(Xsource$vote_event_id)
Xsource$voter_id = as.factor(Xsource$voter_id)
Xsource$option_numeric = rep(0,length(Xsource$option))
Xsource$option_numeric[Xsource$option=='yes'] = 1
Xsource$option_numeric[Xsource$option=='no'] = -1
Xsource$option_numeric[Xsource$option=='abstain'] = -1
Xsource$option_numeric[Xsource$option=='not voting'] = NA
Xsource$option_numeric[Xsource$option=='absent'] = NA
Xsource$option_numeric = as.numeric(Xsource$option_numeric)
#Xrawdb = _X_RAW_DB
Xrawdb = Xsource
# reorder data; divisions x persons
# we may need to install and/or load some additional libraries
# install.packages("reshape2")
library("reshape2")
# install.packages("sqldf")
# library("sqldf")
#prevent reordering, which is behaviour of acast:
#Xrawdb$V1 = factor(Xrawdb$V1, levels=unique(Xrawdb$V1))
Xrawdb$voter_id = factor(Xrawdb$voter_id, levels=unique(Xrawdb$voter_id))
Xraw = acast(Xrawdb,voter_id~vote_event_id,value.var='option_numeric')
Xpeople = dimnames(Xraw)[[1]]
Xvote_events = dimnames(Xraw)[[2]]
WNraw=apply(Xraw,2,as.numeric) #different from WPCA
dimnames(WNraw)[[1]] = Xpeople
#wnominate
library("wnominate")
wn = rollcall(WNraw,yea=1,nay=-1,missing=c(0,NA))
wnr = wnominate(wn,polarity=c(1,1))
#summary(wnr)
#plot(wnr)
WNpositions = cbind(wnr$legislators$coord1D,wnr$legislators$coord2D)
wpca.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scatterplot Chart</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="scatter plot">
<meta name="author" content="Michal Škop">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="./d3.scatterplot.js"></script>
<script src="./d3.tips.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/simplex/bootstrap.min.css" />
<style type="text/css">
text {
font-family: sans-serif;
}
.tick {
fill-opacity: 0;
stroke: #000000;
stroke-width: 1;
}
.domain {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
}
.axis line {
fill: none;
fill-opacity: 0;
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.axis {
}
circle {
fill-opacity: .3;
stroke: black;
stroke-opacity: 0.99;
}
circle:hover {
fill-opacity: 1;
}
.year-2011 {
fill: #000;
stroke: #000;
}
.year-2013 {
fill: #fff;
fill-opacity: 0;
stroke: #000;
}
.label {
font-family: sans-serif;
font-size: 15px;
}
.cssd {
fill: orange;
stroke: orange;
}
.kscm {
fill: red;
stroke: red;
}
.ods {
fill: blue;
stroke: blue;
}
.top-09 {
fill: violet;
stroke: violet;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
pointer-events: none;
}
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
position: absolute;
pointer-events: none;
}
.d3-tip:after {
content: "\25BC";
margin: -1px 0 0 0;
top: 100%;
left: 0;
text-align: center;
}
</style>
</head>
<body>
<div class="navbar navbar-default">
<div class="navbar-header">
<h1 class="navbar-brand">Prague 2010 - 2014</h1>
</div>
</div>
<div id="chart"></div>
<div class="alert alert-dismissable alert-info">
WPCA
</div>
<script type="text/javascript">
d3.csv("praha-2010-2014-roll-call-votes-wpca.csv",function(error,csvdata) {
spdata = [];
csvdata.forEach(function(d) {
spdata.push({"x":d["wpca:d1"],"y":d["wpca:d2"],"r":1,"class":d["class"],"name":d["name"]})
});
var scatterplot = [{
"data": spdata,
"margin": {top: 10, right: 10, bottom: 30, left: 30},
"axes": {"labels":{"x":"Dimension 1", "y":"Dimension 2"}},
"minmax":{"x":{'min':-18,'max':18},"y":{'min':-18,'max':18},"r":{'min':0,'max':1},"rrange":{'min':0,'max':10}},
"size":{"width":600,"height":400}
}];
var svg = d3.select("#chart")
.append("svg")
.attr("width",scatterplot[0]['size']['width'])
.attr("height",scatterplot[0]['size']['height']);
tip = d3.tip().attr('class', 'd3-tip').html(function(d) {
return "<span class=\'stronger\'>" + d["name"] + "</span><br>";
});
svg.call(tip)
var sp = d3.scatterplot()
.data(function(d) {return d.data})
.margin(function(d) {return d.margin})
.axes(function(d) {return d.axes})
.minmax(function(d) {return d.minmax})
.size(function(d) {return d.size})
var scatter = svg.selectAll(".scatterplot")
.data(scatterplot)
.enter()
.append("svg:g")
.attr("transform", "translate(" + scatterplot[0].margin.left + "," + scatterplot[0].margin.top + ")")
.call(sp);
});
</script>
</body>
</html>
wpca.py
import csv
import json
import operator
import numpy
import rpy2.robjects as robjects
r=robjects.r
exec(open("load_datapackage.py").read())
Xsourcevector = []
for row in data["votes"]:
Xsourcevector.append(row["vote_event_id"])
Xsourcevector.append(row["voter_id"])
Xsourcevector.append(row["option"])
robjects.globalenv["Xsourcevector"] = robjects.StrVector(Xsourcevector)
r.source("prepare_matrix.r")
r.source("wpca_script.r")
with open(datapackage['name']+'-wpca.csv', 'w') as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(["identifier","name","candidate_list","wpca:d1","wpca:d2","wpca:d3"])
i = 0
k = 0
rXproj = numpy.array(robjects.globalenv['Xproj'])
rXpeople = robjects.globalenv['Xpeople']
rXvote_events = robjects.globalenv['Xvote_events']
for item in robjects.globalenv['pI']:
if item:
csvwriter.writerow([people[rXpeople[i]]['identifier'],people[rXpeople[i]]['name'],people[rXpeople[i]]['candidate_list'],rXproj[k,0],rXproj[k,1],rXproj[k,2]])
k = k + 1
i = i + 1
wpca_cutting_lines.py
r.source("wpca_cutting_lines_script.r")
rcl = numpy.array(robjects.globalenv['cl'])
out = []
with open(datapackage['name']+'-wpca-cutting-lines.csv', 'w') as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(["normal_x","normal_y","cl_beta0","loss","w1","w2","identifier","motion:name","start_date","result"])
i = 0
for ve in rcl:
out.append(list(rcl[i]) + [vote_events[rXvote_events[i]]["identifier"],vote_events[rXvote_events[i]]["motion:name"],vote_events[rXvote_events[i]]["start_date"],vote_events[rXvote_events[i]]["result"]])
csvwriter.writerow(out[i])
i = i + 1
wpca_cutting_lines_script.r
# CUTTING LINES
# additional parameters:
# _N_FIRST_DIMENSIONS
# how many dimensions?
n_first_dimensions = 2
# loss function
LF = function(beta0) -1*sum(apply(cbind(y*(x%*%beta+beta0),zeros),1,min))
# preparing variables
normals = Xy[,1:n_first_dimensions]
loss_f = data.frame(matrix(0,nrow=dim(X)[1],ncol=4))
colnames(loss_f)=c("Parameter1","Loss1","Parameter_1","Loss_1")
parameters = data.frame(matrix(0,nrow=dim(X)[1],ncol=3))
colnames(parameters)=c("Parameter","Loss","Direction")
xfull = t(t(Xe$vectors[,1:n_first_dimensions]) * sqrt(Xe$values[1:n_first_dimensions]))
#calculating all cutting lines
for (i in as.numeric(1:dim(X)[1])) {
beta = Xy[i,1:n_first_dimensions]
y = t(as.matrix(X[i,]))[,pI]
x = xfull[which(!is.na(y)),]
y = y[which(!is.na(y))]
zeros = as.matrix(rep(0,length(y)))
# note: “10000” should be enough for any real-life case:
res1 = optim(c(1),LF,method="Brent",lower=-10000,upper=10000)
# note: the sign is arbitrary, the real result may be -1*; we need to minimize the other way as well
y=-y
res2 = optim(c(1),LF,method="Brent",lower=-10000,upper=10000)
# the real parameter is the one with lower loss function
# note: theoretically should be the same (either +1 or -1) for all divisions(rows), however, due to missing data, the calculation may lead to a few divisions with the other direction
loss_f[i,] = c(res1$par,res1$value,res2$par,res2$value)
if (res1$value<=res2$value) {
parameters[i,] = c(res1$par,res1$value,1)
} else {
parameters[i,] = c(res2$par,res2$value,-1)
}
}
CuttingLines = list(normals=normals,parameters=parameters,loss_function=LF,weights=cbind(w1,w2))
cl = cbind(CuttingLines$normals,CuttingLines$parameters$Parameter,CuttingLines$parameters$Loss,w1,w2)
wpca_projection_script.r
# PROJECTION OF PART OF THE LEGISLATIVE TERM INTO THE WHOLE TERM MODEL
# additional parameters:
# _TI, _LO_LIMIT_2
# lower limit to eliminate from projections (may be the same as lo_limit); number
lo_limitT = .1
# indices whether divisions are in the given part or not
# vector of length div, contains TRUE or FALSE
# TI = _TI
TI = as.logical(TI)
# scaling, weighting and missing values treatment
XrawTc = Xraw[,pI]
XrawTc[!TI,] = NA
XTc = (XrawTc - attr(Xstand,"scaled:center"))/attr(Xstand,"scaled:scale")
# Indices of NAs; division x person
TIc = XTc
TIc[!is.na(TIc)] = 1
TIc[is.na(TIc)] = 0
# weights, divisions x persons
XTw0c = XTc * w1 * w2
XTw0c[is.na(XTw0c)] = 0
# weights for non missing data; divisions x persons
TIcw = TIc*w1*w2
# sum of weights of divisions for each persons
s = apply(TIcw,2,sum)
pTw = s/max(s)
# index of persons in calculation
pTI = pTw > lo_limitT
# weighted scaled with NA->0 and cutted persons with too few votes division x persons
XTw0cc = XTw0c[,pTI]
# index of missing data cutted persons with too few votes divisions x persons
TIcc = TIc[,pTI]
# weights of divisions, person x division
aZ = abs(Z)
dweights = t(t(aZ)%*%TIcc / apply(aZ,2,sum))
dweights[is.na(dweights)] = 0
# projection of persons
XTw0ccproj = t(XTw0cc)%*%Z / dweights
# analytical chart:
plot(XTw0ccproj[,1],XTw0ccproj[,2])
wpca_script.r
# INPUT PARAMETERS
# _X_RAW_DB, _LO_LIMIT_1
# raw data in csv using db structure, i.e., a single row contains:
# code of representative, code of division, encoded vote (i.e. one of -1, 1, 0, NA)
# for example:
# “Joe Europe”,”Division-007”,”1”
#Xsource = read.csv("data/votes.csv")
Xsource$vote_event_id = as.factor(Xsource$vote_event_id)
Xsource$voter_id = as.factor(Xsource$voter_id)
#Xsource = Xsource[c("voter_id","vote_event_id","option")]
Xsource$option_numeric = rep(0,length(Xsource$option))
Xsource$option_numeric[Xsource$option=='yes'] = 1
Xsource$option_numeric[Xsource$option=='no'] = -1
Xsource$option_numeric[Xsource$option=='abstain'] = -1
Xsource$option_numeric[Xsource$option=='not voting'] = NA
Xsource$option_numeric[Xsource$option=='absent'] = NA
Xsource$option_numeric = as.numeric(Xsource$option_numeric)
#Xrawdb = _X_RAW_DB
Xrawdb = Xsource
# lower limit to eliminate from calculations, e.g., .1; number
lo_limit = .1
# reorder data; divisions x persons
# we may need to install and/or load some additional libraries
# install.packages("reshape2")
library("reshape2")
# install.packages("sqldf")
# library("sqldf")
#prevent reordering, which is behaviour of acast:
#Xrawdb$V1 = factor(Xrawdb$V1, levels=unique(Xrawdb$V1))
Xrawdb$voter_id = factor(Xrawdb$voter_id, levels=unique(Xrawdb$voter_id))
Xraw = acast(Xrawdb,voter_id~vote_event_id,value.var='option_numeric')
Xpeople = dimnames(Xraw)[[1]]
Xvote_events = dimnames(Xraw)[[2]]
Xraw=apply(Xraw,1,as.numeric)
# scale data; divisions x persons (mean=0 and sd=1 for each division)
Xstand=t(scale(t(Xraw),scale=TRUE))
# WEIGHTS
# weights 1 for divisions, based on number of persons in division
w1 = apply(abs(Xraw)==1,1,sum,na.rm=TRUE)/max(apply(abs(Xraw)==1,1,sum,na.rm=TRUE))
w1[is.na(w1)] = 0
# weights 2 for divisions, "100:100" vs. "195:5"
w2 = 1 - abs(apply(Xraw==1,1,sum,na.rm=TRUE) - apply(Xraw==-1,1,sum,na.rm=TRUE))/apply(!is.na(Xraw),1,sum)
w2[is.na(w2)] = 0
# analytical charts for weights:
#plot(w1)
#plot(w2)
#plot(w1*w2)
# weighted scaled matrix; divisions x persons
X = Xstand * w1 * w2
# MISSING DATA
# index of missing data; divisions x persons
I = X
I[!is.na(X)] = 1
I[is.na(X)] = 0
# weighted scaled with NA substituted by 0; division x persons
X0 = X
X0[is.na(X)]=0
# EXCLUSION OF REPRESENTATIVES WITH TOO FEW VOTES (WEIGHTED)
# weights for non missing data; division x persons
Iw = I*w1*w2
# sum of weights of divisions for each persons; vector of length “persons”
s = apply(Iw,2,sum)
pw = s/(t(w1)%*%w2)
# index of persons kept in calculation; vector of length “persons”
pI = pw > lo_limit
# weighted scaled with NA->0 and cutted persons with too few weighted votes; division x persons
X0c = X0[,pI]
# index of missing cutted (excluded) persons with too few weighted votes; divisions x persons
Ic = I[,pI]
# indexes of cutted (excluded) persons with too few votes; divisions x persons
Iwc = Iw[,pI]
# “X’X” MATRIX
# weighted X’X matrix with missing values substituted and excluded persons; persons x persons
C=t(X0c)%*%X0c * 1/(t(Iwc)%*%Iwc) * (sum(w1*w1*w2*w2))
# substitution of missing data in "covariance" matrix (the simple way)
C0 = C
C0[is.na(C)] = 0
# DECOMPOSITION
# eigendecomposition
Xe=eigen(C0)
# W (rotation values of persons)
W = Xe$vectors
# projected divisions into dimensions
Xy=X0c%*%W
# analytical charts of projection of divisions and lambdas
#plot(Xy[,1],Xy[,2])
#plot(sqrt(Xe$values[1:10]))
# lambda matrix
lambda = diag(sqrt(Xe$values))
lambda[is.na(lambda)] = 0
# projection of persons into dimensions
Xproj = W%*%lambda
# analytical chart
#plot(Xproj[,1],Xproj[,2])
# lambda^-1 matrix
lambda_1 = diag(sqrt(1/Xe$values))
lambda_1[is.na(lambda_1)] = 0
# Z (rotation values of divisions)
Z = X0c%*%W%*%lambda_1
# analytical charts
# second projection
Xproj2 = t(X0c) %*% Z
# without missing values, they are equal:
#plot(Xproj[,1],Xproj2[,1])
#plot(Xproj[,2],Xproj2[,2])