Skip to content

Instantly share code, notes, and snippets.

@seanjtaylor
Last active December 23, 2015 07:59
Show Gist options
  • Save seanjtaylor/6604307 to your computer and use it in GitHub Desktop.
Save seanjtaylor/6604307 to your computer and use it in GitHub Desktop.
Creds Viz take 2
<html>
<head>
<style>
rect.background {
fill: #EEE;
}
line.rule {
stroke: #FFF;
}
text.labels {
}
rect.vote {
}
rect.selected {
fill: white;
}
</style>
</head>
<body>
<svg>
</svg>
<br />
Toggle Ordering: <input type="button" id="x-order" value="Ordinal"></input>
<br />
Toggle Levels: <input type="button" id="y-diff" value="Probability"></input>
<br />
Toggle Grouped: <input type="button" id="grouped" value="Not Grouped"></input>
<br /> <var id="point"></var>
</body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.v3.min.js"></script>
<script type="text/javascript">
data = [{"pr": [0.4887518980531451], "q": 198, "p": 100, "u": "user:1", "cs": [0, 198], "dt": 1362002296.244313, "out": 1}, {"pr": [0.4777419929494981], "q": 194, "p": 100, "u": "user:1", "cs": [0, 392], "dt": 1362002297.15593, "out": 1}, {"pr": [0.4669799475900601], "q": 190, "p": 100, "u": "user:1", "cs": [0, 582], "dt": 1362002298.131446, "out": 1}, {"pr": [0.4564740857547256], "q": 186, "p": 100, "u": "user:1", "cs": [0, 768], "dt": 1362002299.053804, "out": 1}, {"pr": [0.4462314413902582], "q": 182, "p": 100, "u": "user:1", "cs": [0, 950], "dt": 1362002299.834792, "out": 1}, {"pr": [0.43620192214251935], "q": 179, "p": 100, "u": "user:1", "cs": [0, 1129], "dt": 1362002300.612833, "out": 1}, {"pr": [0.42639107453895414], "q": 176, "p": 100, "u": "user:1", "cs": [0, 1305], "dt": 1362002301.475644, "out": 1}, {"pr": [0.4168035487806823], "q": 173, "p": 100, "u": "user:1", "cs": [0, 1478], "dt": 1362002302.241639, "out": 1}, {"pr": [0.407443137803258], "q": 170, "p": 100, "u": "user:1", "cs": [0, 1648], "dt": 1362002303.044344, "out": 1}, {"pr": [0.3983128198171432], "q": 167, "p": 100, "u": "user:1", "cs": [0, 1815], "dt": 1362002303.838559, "out": 1}, {"pr": [0.389360766050778], "q": 165, "p": 100, "u": "user:1", "cs": [0, 1980], "dt": 1362002304.66829, "out": 1}, {"pr": [0.3805898296312972], "q": 163, "p": 100, "u": "user:1", "cs": [0, 2143], "dt": 1362002305.419034, "out": 1}, {"pr": [0.3720554203704833], "q": 160, "p": 100, "u": "user:1", "cs": [0, 2303], "dt": 1362002306.294308, "out": 1}, {"pr": [0.3637052339620581], "q": 158, "p": 100, "u": "user:1", "cs": [0, 2461], "dt": 1362002307.00364, "out": 1}, {"pr": [0.3780214784774396], "q": 270, "p": 100, "u": "user:79", "cs": [270, 2461], "dt": 1362097070.392947, "out": 0}, {"pr": [0.39201177524645336], "q": 260, "p": 100, "u": "user:79", "cs": [530, 2461], "dt": 1362097072.847491, "out": 0}, {"pr": [0.405688448673852], "q": 251, "p": 100, "u": "user:79", "cs": [781, 2461], "dt": 1362097074.466855, "out": 0}, {"pr": [0.41907033794980597], "q": 243, "p": 100, "u": "user:79", "cs": [1024, 2461], "dt": 1362097079.664115, "out": 0}, {"pr": [0.4321261306868008], "q": 235, "p": 100, "u": "user:79", "cs": [1259, 2461], "dt": 1362097081.050202, "out": 0}, {"pr": [0.44488397296379667], "q": 228, "p": 100, "u": "user:79", "cs": [1487, 2461], "dt": 1362097081.999775, "out": 0}, {"pr": [0.45737642937902717], "q": 222, "p": 100, "u": "user:79", "cs": [1709, 2461], "dt": 1362097083.52105, "out": 0}, {"pr": [0.46958306002339806], "q": 216, "p": 100, "u": "user:119", "cs": [1925, 2461], "dt": 1362982825.403441, "out": 0}, {"pr": [0.4814857413970002], "q": 210, "p": 100, "u": "user:124", "cs": [2135, 2461], "dt": 1363031950.177088, "out": 0}, {"pr": [0.4931254332354658], "q": 205, "p": 100, "u": "user:124", "cs": [2340, 2461], "dt": 1363031951.993299, "out": 0}, {"pr": [0.5044885157856556], "q": 200, "p": 100, "u": "user:124", "cs": [2540, 2461], "dt": 1363031953.333297, "out": 0}, {"pr": [0.5156199157230156], "q": 196, "p": 100, "u": "user:124", "cs": [2736, 2461], "dt": 1363031954.089252, "out": 0}, {"pr": [0.5265092102186909], "q": 192, "p": 100, "u": "user:124", "cs": [2928, 2461], "dt": 1363031955.47486, "out": 0}, {"pr": [0.5371473344866333], "q": 188, "p": 100, "u": "user:124", "cs": [3116, 2461], "dt": 1363031956.327959, "out": 0}, {"pr": [0.547526538084062], "q": 184, "p": 100, "u": "user:71", "cs": [3300, 2461], "dt": 1363048305.660753, "out": 0}, {"pr": [0.5352256373010753], "q": 218, "p": 100, "u": "user:146", "cs": [3300, 2679], "dt": 1363576413.143957, "out": 1}, {"pr": [0.5232219179306447], "q": 212, "p": 100, "u": "user:149", "cs": [3300, 2891], "dt": 1363656534.298865, "out": 1}, {"pr": [0.5339250688397637], "q": 189, "p": 100, "u": "user:149", "cs": [3489, 2891], "dt": 1363656541.768242, "out": 0}, {"pr": [0.5443716021266337], "q": 185, "p": 100, "u": "user:149", "cs": [3674, 2891], "dt": 1363656545.990622, "out": 0}, {"pr": [0.5321148186183294], "q": 217, "p": 100, "u": "user:149", "cs": [3674, 3108], "dt": 1363656547.323686, "out": 1}, {"pr": [0.5201595199384199], "q": 211, "p": 100, "u": "user:149", "cs": [3674, 3319], "dt": 1363656548.348152, "out": 1}, {"pr": [0.5309263793052534], "q": 190, "p": 100, "u": "user:149", "cs": [3864, 3319], "dt": 1363656549.538274, "out": 0}, {"pr": [0.5414388209791797], "q": 186, "p": 100, "u": "user:149", "cs": [4050, 3319], "dt": 1363656551.647232, "out": 0}, {"pr": [0.5517458796316836], "q": 183, "p": 100, "u": "user:149", "cs": [4233, 3319], "dt": 1363656553.642795, "out": 0}, {"pr": [0.561840835092455], "q": 180, "p": 100, "u": "user:149", "cs": [4413, 3319], "dt": 1363656554.930644, "out": 0}, {"pr": [0.5716622594453787], "q": 176, "p": 100, "u": "user:149", "cs": [4589, 3319], "dt": 1363656555.817018, "out": 0}, {"pr": [0.5588173309729533], "q": 230, "p": 100, "u": "user:149", "cs": [4589, 3549], "dt": 1363656558.168185, "out": 1}, {"pr": [0.5462875400979932], "q": 223, "p": 100, "u": "user:149", "cs": [4589, 3772], "dt": 1363656559.115906, "out": 1}, {"pr": [0.5340381803161751], "q": 217, "p": 100, "u": "user:149", "cs": [4589, 3989], "dt": 1363656560.03778, "out": 1}, {"pr": [0.5444843412876504], "q": 185, "p": 100, "u": "user:149", "cs": [4774, 3989], "dt": 1363656562.17308, "out": 0}, {"pr": [0.554723098218698], "q": 182, "p": 100, "u": "user:118", "cs": [4956, 3989], "dt": 1363897874.809114, "out": 0}, {"pr": [0.564748025859154], "q": 179, "p": 100, "u": "user:118", "cs": [5135, 3989], "dt": 1363897875.753546, "out": 0}, {"pr": [0.5745536304643416], "q": 176, "p": 100, "u": "user:118", "cs": [5311, 3989], "dt": 1363897879.95441, "out": 0}, {"pr": [0.5616170264245689], "q": 232, "p": 100, "u": "user:153", "cs": [5311, 4221], "dt": 1364690240.241184, "out": 1}, {"pr": [0.5489900482546072], "q": 225, "p": 100, "u": "user:146", "cs": [5311, 4446], "dt": 1366248577.421973, "out": 1}, {"pr": [0.5366387549568554], "q": 219, "p": 100, "u": "user:8", "cs": [5311, 4665], "dt": 1366345490.163138, "out": 1}, {"pr": [0.5470760606848712], "q": 185, "p": 100, "u": "user:185", "cs": [5496, 4665], "dt": 1373510535.358066, "out": 0}, {"pr": [0.5572478545985555], "q": 181, "p": 100, "u": "user:1", "cs": [5677, 4665], "dt": 1373510573.170755, "out": 0}, {"pr": [0.5672044918069048], "q": 178, "p": 100, "u": "user:1", "cs": [5855, 4665], "dt": 1373510573.952507, "out": 0}, {"pr": [0.5769407348571504], "q": 175, "p": 100, "u": "user:1", "cs": [6030, 4665], "dt": 1373510574.780651, "out": 0}, {"pr": [0.5864522300256834], "q": 172, "p": 100, "u": "user:1", "cs": [6202, 4665], "dt": 1373510575.570258, "out": 0}, {"pr": [0.595735466962288], "q": 169, "p": 100, "u": "user:1", "cs": [6371, 4665], "dt": 1373510576.371747, "out": 0}, {"pr": [0.6048420564550759], "q": 167, "p": 100, "u": "user:188", "cs": [6538, 4665], "dt": 1374713306.458774, "out": 0}, {"pr": [0.6137148419797863], "q": 164, "p": 100, "u": "user:188", "cs": [6702, 4665], "dt": 1374713308.742282, "out": 0}, {"pr": [0.6224059197809644], "q": 162, "p": 100, "u": "user:188", "cs": [6864, 4665], "dt": 1374713312.441157, "out": 0}, {"pr": [0.6309131806066465], "q": 160, "p": 100, "u": "user:188", "cs": [7024, 4665], "dt": 1374713315.324642, "out": 0}, {"pr": [0.6391826051783509], "q": 157, "p": 100, "u": "user:188", "cs": [7181, 4665], "dt": 1374824970.417369, "out": 0}, {"pr": [0.6472665360900266], "q": 155, "p": 100, "u": "user:188", "cs": [7336, 4665], "dt": 1374824971.537634, "out": 0}, {"pr": [0.6552156919248883], "q": 154, "p": 100, "u": "user:188", "cs": [7490, 4665], "dt": 1374824973.56478, "out": 0}, {"pr": [0.6629773994158226], "q": 152, "p": 100, "u": "user:188", "cs": [7642, 4665], "dt": 1374824976.381457, "out": 0}, {"pr": [0.6705517991954939], "q": 150, "p": 100, "u": "user:188", "cs": [7792, 4665], "dt": 1374925034.327889, "out": 0}, {"pr": [0.6779394010913482], "q": 148, "p": 100, "u": "user:188", "cs": [7940, 4665], "dt": 1374925035.996325, "out": 0}, {"pr": [0.6851900825938111], "q": 147, "p": 100, "u": "user:188", "cs": [8087, 4665], "dt": 1375084246.708778, "out": 0}, {"pr": [0.6922547765894579], "q": 145, "p": 100, "u": "user:188", "cs": [8232, 4665], "dt": 1375084293.981616, "out": 0}, {"pr": [0.6991827237997876], "q": 144, "p": 100, "u": "user:188", "cs": [8376, 4665], "dt": 1375085342.520309, "out": 0}, {"pr": [0.7059265892345513], "q": 142, "p": 100, "u": "user:188", "cs": [8518, 4665], "dt": 1375085343.327875, "out": 0}, {"pr": [0.7125348674101749], "q": 141, "p": 100, "u": "user:188", "cs": [8659, 4665], "dt": 1375090502.690305, "out": 0}]
/*
function groupby(seq, keyfunc){
var lastkey = null
var key, item
var result = []
for (var i = 0; i < seq.length; i++) {
item = seq[i]
key = keyfunc(item)
if (key == lastkey){
result[result.length-1][1].push(item)
}
else {
result.push([key, [item]])
lastkey = key
}
}
return result
}
*/
function labelEndsSession(seq, keyfunc) {
var sessionId = 0;
var last = seq[0];
for (var i = 0; i < (seq.length-1); i++) {
seq[i].sessionId = sessionId;
if (keyfunc(seq[i]) != keyfunc(seq[i+1])) {
seq[i].endsSession = true;
sessionId += 1;
seq[i].last = last;
last = seq[i];
} else {
seq[i].endsSession = false;
}
}
seq[seq.length-1].endsSession = true;
seq[seq.length-1].sessionId = sessionId;
seq[i].last = last;
}
function update(data, o) {
// 1. if it's diff mode, ignore the first observation
// 2. if it's grouped mode, ignore the ones that don't end a session
opacity = function(d, i) {
return (o.diff && i == 0) || (o.grouped && !d.endsSession) ? 1e-6 : 1;
}
if (o.ordinal) {
if (o.grouped) {
// ordinal AND grouped
var ngroups = d3.sum(data, function(d) { return d.endsSession ? 1 : 0 })
o.xOrder.domain([0, ngroups-1])
var xAttr = function(d, i) { return o.xOrder(d.sessionId) }
} else {
// ordinal AND NOT grouped
o.xOrder.domain([0, data.length-1]);
var xAttr = function(d, i) { return o.xOrder(i) }
}
var wAttr = function(d, i) {
return d3.min([15, d3.round(o.w / o.xOrder.domain()[1])])
}
} else {
// time scale
var xAttr = function(d, i) { return o.xTime(d.dt) }
var wAttr = function(d, i) { return 10 }
}
if (o.diff) {
// diffed
diffProb = function(d, i) {
if (d.endsSession && o.grouped) {
var end = d.pr[0]
var start = d.last.pr[0]
return 100*(end - start)
} else if (!o.grouped && i > 0) {
// not grouped
return 100*(d.pr[0] - data[i-1].pr[0])
} else {
// grouped and NOT ends session, so ignore it
return 0
}
}
var yrange = d3.extent(data, diffProb)
o.yDiff.domain([d3.min([yrange[0], -1]), d3.max([yrange[1], 1])])
var yAttr = function(d, i) {
var diff = diffProb(d, i)
return diff > 0 ? o.yDiff(diff) : o.yDiff(0)
}
function abs(d) { return d > 0 ? d : -d }
var hAttr = function(d, i) { return abs(o.yDiff(diffProb(d, i)) - o.yDiff(0)) }
var yTicks = o.yDiff.ticks(5)
} else {
// absolute scale
var yAttr = function(d, i) { return o.yProb(100*d.pr[0]) }
var hAttr = function(d, i) { return 10 }
var yTicks = [25, 50, 75]
}
var y = function(d) { return o.diff ? o.yDiff(d) : o.yProb(d) }
var yLab = function(d) { return (o.diff && d > 0) ? '+' + d : d }
var yRule = o.svg.selectAll('g.y').data(yTicks)
var yTrans = yRule.transition().duration(250)
yTrans.select('line')
.attr('y1', y)
.attr('y2', y)
yTrans.select('text')
.attr('y', y)
.text(function(d) { return d })
var yEnter = yRule.enter()
.append('g')
.attr('class', 'y')
yEnter.append('line')
.attr('class', 'rule')
.attr('x1', o.xOrder.range()[0])
.attr('x2', o.xOrder.range()[1])
.attr('y1', y)
.attr('y2', y)
yEnter.append('text')
.attr("class", "labels")
.attr("x", 0)
.attr("y", y)
.attr("text-anchor", "right")
.text(yLab)
yRule.exit().remove()
var rects = o.svg.selectAll('rect.vote').data(data)
// update existing elements
rects.transition()
.duration(750)
.attr('x', xAttr)
.attr('y', yAttr)
.attr('width', wAttr)
.attr('height', hAttr)
.style("fill-opacity", opacity)
var legend = o.svg.selectAll('g.legend').data([data[data.length-1]])
legend.enter()
.append('g')
.attr('class', 'legend')
.append('text')
.attr('x', 0)
.attr('y', 50)
.text(function(d) {
return d.u + ' -> ' + d.pr[0] + ' at ' + d.dt;
})
rects.enter()
.append('rect')
.attr('class', 'vote')
.attr('width', wAttr)
.attr('height', hAttr)
.attr('x', xAttr)
.attr('y', yAttr)
.style("fill-opacity", opacity)
.attr('fill', function(d) { return o.userColor(d.u) })
rects.exit().remove()
}
function init(data) {
var m = [30, 30, 30, 30], // margins: top right bottom left
w = 538 - m[1] - m[3], // width
h = 400 - m[0] - m[2] // height
// default state
options = {
diff: 0,
grouped: 0,
ordinal: 0, // gets reversed immediately
yProb: d3.scale.linear().range([h, 0]).domain([0,100]),
yDiff: d3.scale.linear().range([h, 0]).domain([-.25,.25]),
xTime: d3.scale.linear().range([20, w-10]),
xOrder: d3.scale.linear().range([20, w-10]),
userColor: d3.scale.category20c(),
svg: d3.select('svg').attr("width", w).attr("height", h),
w: w,
h: h
}
options.svg.append("rect")
.attr("width", w)
.attr("height", h)
.attr("class", "background")
// setup our three toggle buttons
var toggleOrder = function() {
options.ordinal = 1 - options.ordinal
d3.select('#x-order').attr('value', ['Time', 'Ordinal'][options.ordinal])
update(data, options)
}
var toggleDiff = function() {
options.diff = 1 - options.diff
d3.select('#y-diff').attr('value', ['Probability', 'Change'][options.diff])
update(data, options)
}
var toggleGrouped = function() {
options.grouped = 1 - options.grouped
d3.select('#grouped').attr('value', ['Not Grouped', 'Grouped'][options.grouped])
update(data, options)
}
d3.select('#x-order').on('click', toggleOrder)
d3.select('#y-diff').on('click', toggleDiff)
d3.select('#grouped').on('click', toggleGrouped)
options.xTime.domain(d3.extent(data, function(d, i) { return d.dt }));
labelEndsSession(data, function(d) { return d.u});
toggleOrder()
}
data[0] = {pr: [0.50], cs: [0,0], u: 'user:0', dt: 1362002206.244313}
init(data)
</script>
</html>
data = [{"pr": [0.4887518980531451], "q": 198, "p": 100, "u": "user:1", "cs": [0, 198], "dt": 1362002296.244313, "out": 1}, {"pr": [0.4777419929494981], "q": 194, "p": 100, "u": "user:1", "cs": [0, 392], "dt": 1362002297.15593, "out": 1}, {"pr": [0.4669799475900601], "q": 190, "p": 100, "u": "user:1", "cs": [0, 582], "dt": 1362002298.131446, "out": 1}, {"pr": [0.4564740857547256], "q": 186, "p": 100, "u": "user:1", "cs": [0, 768], "dt": 1362002299.053804, "out": 1}, {"pr": [0.4462314413902582], "q": 182, "p": 100, "u": "user:1", "cs": [0, 950], "dt": 1362002299.834792, "out": 1}, {"pr": [0.43620192214251935], "q": 179, "p": 100, "u": "user:1", "cs": [0, 1129], "dt": 1362002300.612833, "out": 1}, {"pr": [0.42639107453895414], "q": 176, "p": 100, "u": "user:1", "cs": [0, 1305], "dt": 1362002301.475644, "out": 1}, {"pr": [0.4168035487806823], "q": 173, "p": 100, "u": "user:1", "cs": [0, 1478], "dt": 1362002302.241639, "out": 1}, {"pr": [0.407443137803258], "q": 170, "p": 100, "u": "user:1", "cs": [0, 1648], "dt": 1362002303.044344, "out": 1}, {"pr": [0.3983128198171432], "q": 167, "p": 100, "u": "user:1", "cs": [0, 1815], "dt": 1362002303.838559, "out": 1}, {"pr": [0.389360766050778], "q": 165, "p": 100, "u": "user:1", "cs": [0, 1980], "dt": 1362002304.66829, "out": 1}, {"pr": [0.3805898296312972], "q": 163, "p": 100, "u": "user:1", "cs": [0, 2143], "dt": 1362002305.419034, "out": 1}, {"pr": [0.3720554203704833], "q": 160, "p": 100, "u": "user:1", "cs": [0, 2303], "dt": 1362002306.294308, "out": 1}, {"pr": [0.3637052339620581], "q": 158, "p": 100, "u": "user:1", "cs": [0, 2461], "dt": 1362002307.00364, "out": 1}, {"pr": [0.3780214784774396], "q": 270, "p": 100, "u": "user:79", "cs": [270, 2461], "dt": 1362097070.392947, "out": 0}, {"pr": [0.39201177524645336], "q": 260, "p": 100, "u": "user:79", "cs": [530, 2461], "dt": 1362097072.847491, "out": 0}, {"pr": [0.405688448673852], "q": 251, "p": 100, "u": "user:79", "cs": [781, 2461], "dt": 1362097074.466855, "out": 0}, {"pr": [0.41907033794980597], "q": 243, "p": 100, "u": "user:79", "cs": [1024, 2461], "dt": 1362097079.664115, "out": 0}, {"pr": [0.4321261306868008], "q": 235, "p": 100, "u": "user:79", "cs": [1259, 2461], "dt": 1362097081.050202, "out": 0}, {"pr": [0.44488397296379667], "q": 228, "p": 100, "u": "user:79", "cs": [1487, 2461], "dt": 1362097081.999775, "out": 0}, {"pr": [0.45737642937902717], "q": 222, "p": 100, "u": "user:79", "cs": [1709, 2461], "dt": 1362097083.52105, "out": 0}, {"pr": [0.46958306002339806], "q": 216, "p": 100, "u": "user:119", "cs": [1925, 2461], "dt": 1362982825.403441, "out": 0}, {"pr": [0.4814857413970002], "q": 210, "p": 100, "u": "user:124", "cs": [2135, 2461], "dt": 1363031950.177088, "out": 0}, {"pr": [0.4931254332354658], "q": 205, "p": 100, "u": "user:124", "cs": [2340, 2461], "dt": 1363031951.993299, "out": 0}, {"pr": [0.5044885157856556], "q": 200, "p": 100, "u": "user:124", "cs": [2540, 2461], "dt": 1363031953.333297, "out": 0}, {"pr": [0.5156199157230156], "q": 196, "p": 100, "u": "user:124", "cs": [2736, 2461], "dt": 1363031954.089252, "out": 0}, {"pr": [0.5265092102186909], "q": 192, "p": 100, "u": "user:124", "cs": [2928, 2461], "dt": 1363031955.47486, "out": 0}, {"pr": [0.5371473344866333], "q": 188, "p": 100, "u": "user:124", "cs": [3116, 2461], "dt": 1363031956.327959, "out": 0}, {"pr": [0.547526538084062], "q": 184, "p": 100, "u": "user:71", "cs": [3300, 2461], "dt": 1363048305.660753, "out": 0}, {"pr": [0.5352256373010753], "q": 218, "p": 100, "u": "user:146", "cs": [3300, 2679], "dt": 1363576413.143957, "out": 1}, {"pr": [0.5232219179306447], "q": 212, "p": 100, "u": "user:149", "cs": [3300, 2891], "dt": 1363656534.298865, "out": 1}, {"pr": [0.5339250688397637], "q": 189, "p": 100, "u": "user:149", "cs": [3489, 2891], "dt": 1363656541.768242, "out": 0}, {"pr": [0.5443716021266337], "q": 185, "p": 100, "u": "user:149", "cs": [3674, 2891], "dt": 1363656545.990622, "out": 0}, {"pr": [0.5321148186183294], "q": 217, "p": 100, "u": "user:149", "cs": [3674, 3108], "dt": 1363656547.323686, "out": 1}, {"pr": [0.5201595199384199], "q": 211, "p": 100, "u": "user:149", "cs": [3674, 3319], "dt": 1363656548.348152, "out": 1}, {"pr": [0.5309263793052534], "q": 190, "p": 100, "u": "user:149", "cs": [3864, 3319], "dt": 1363656549.538274, "out": 0}, {"pr": [0.5414388209791797], "q": 186, "p": 100, "u": "user:149", "cs": [4050, 3319], "dt": 1363656551.647232, "out": 0}, {"pr": [0.5517458796316836], "q": 183, "p": 100, "u": "user:149", "cs": [4233, 3319], "dt": 1363656553.642795, "out": 0}, {"pr": [0.561840835092455], "q": 180, "p": 100, "u": "user:149", "cs": [4413, 3319], "dt": 1363656554.930644, "out": 0}, {"pr": [0.5716622594453787], "q": 176, "p": 100, "u": "user:149", "cs": [4589, 3319], "dt": 1363656555.817018, "out": 0}, {"pr": [0.5588173309729533], "q": 230, "p": 100, "u": "user:149", "cs": [4589, 3549], "dt": 1363656558.168185, "out": 1}, {"pr": [0.5462875400979932], "q": 223, "p": 100, "u": "user:149", "cs": [4589, 3772], "dt": 1363656559.115906, "out": 1}, {"pr": [0.5340381803161751], "q": 217, "p": 100, "u": "user:149", "cs": [4589, 3989], "dt": 1363656560.03778, "out": 1}, {"pr": [0.5444843412876504], "q": 185, "p": 100, "u": "user:149", "cs": [4774, 3989], "dt": 1363656562.17308, "out": 0}, {"pr": [0.554723098218698], "q": 182, "p": 100, "u": "user:118", "cs": [4956, 3989], "dt": 1363897874.809114, "out": 0}, {"pr": [0.564748025859154], "q": 179, "p": 100, "u": "user:118", "cs": [5135, 3989], "dt": 1363897875.753546, "out": 0}, {"pr": [0.5745536304643416], "q": 176, "p": 100, "u": "user:118", "cs": [5311, 3989], "dt": 1363897879.95441, "out": 0}, {"pr": [0.5616170264245689], "q": 232, "p": 100, "u": "user:153", "cs": [5311, 4221], "dt": 1364690240.241184, "out": 1}, {"pr": [0.5489900482546072], "q": 225, "p": 100, "u": "user:146", "cs": [5311, 4446], "dt": 1366248577.421973, "out": 1}, {"pr": [0.5366387549568554], "q": 219, "p": 100, "u": "user:8", "cs": [5311, 4665], "dt": 1366345490.163138, "out": 1}, {"pr": [0.5470760606848712], "q": 185, "p": 100, "u": "user:185", "cs": [5496, 4665], "dt": 1373510535.358066, "out": 0}, {"pr": [0.5572478545985555], "q": 181, "p": 100, "u": "user:1", "cs": [5677, 4665], "dt": 1373510573.170755, "out": 0}, {"pr": [0.5672044918069048], "q": 178, "p": 100, "u": "user:1", "cs": [5855, 4665], "dt": 1373510573.952507, "out": 0}, {"pr": [0.5769407348571504], "q": 175, "p": 100, "u": "user:1", "cs": [6030, 4665], "dt": 1373510574.780651, "out": 0}, {"pr": [0.5864522300256834], "q": 172, "p": 100, "u": "user:1", "cs": [6202, 4665], "dt": 1373510575.570258, "out": 0}, {"pr": [0.595735466962288], "q": 169, "p": 100, "u": "user:1", "cs": [6371, 4665], "dt": 1373510576.371747, "out": 0}, {"pr": [0.6048420564550759], "q": 167, "p": 100, "u": "user:188", "cs": [6538, 4665], "dt": 1374713306.458774, "out": 0}, {"pr": [0.6137148419797863], "q": 164, "p": 100, "u": "user:188", "cs": [6702, 4665], "dt": 1374713308.742282, "out": 0}, {"pr": [0.6224059197809644], "q": 162, "p": 100, "u": "user:188", "cs": [6864, 4665], "dt": 1374713312.441157, "out": 0}, {"pr": [0.6309131806066465], "q": 160, "p": 100, "u": "user:188", "cs": [7024, 4665], "dt": 1374713315.324642, "out": 0}, {"pr": [0.6391826051783509], "q": 157, "p": 100, "u": "user:188", "cs": [7181, 4665], "dt": 1374824970.417369, "out": 0}, {"pr": [0.6472665360900266], "q": 155, "p": 100, "u": "user:188", "cs": [7336, 4665], "dt": 1374824971.537634, "out": 0}, {"pr": [0.6552156919248883], "q": 154, "p": 100, "u": "user:188", "cs": [7490, 4665], "dt": 1374824973.56478, "out": 0}, {"pr": [0.6629773994158226], "q": 152, "p": 100, "u": "user:188", "cs": [7642, 4665], "dt": 1374824976.381457, "out": 0}, {"pr": [0.6705517991954939], "q": 150, "p": 100, "u": "user:188", "cs": [7792, 4665], "dt": 1374925034.327889, "out": 0}, {"pr": [0.6779394010913482], "q": 148, "p": 100, "u": "user:188", "cs": [7940, 4665], "dt": 1374925035.996325, "out": 0}, {"pr": [0.6851900825938111], "q": 147, "p": 100, "u": "user:188", "cs": [8087, 4665], "dt": 1375084246.708778, "out": 0}, {"pr": [0.6922547765894579], "q": 145, "p": 100, "u": "user:188", "cs": [8232, 4665], "dt": 1375084293.981616, "out": 0}, {"pr": [0.6991827237997876], "q": 144, "p": 100, "u": "user:188", "cs": [8376, 4665], "dt": 1375085342.520309, "out": 0}, {"pr": [0.7059265892345513], "q": 142, "p": 100, "u": "user:188", "cs": [8518, 4665], "dt": 1375085343.327875, "out": 0}, {"pr": [0.7125348674101749], "q": 141, "p": 100, "u": "user:188", "cs": [8659, 4665], "dt": 1375090502.690305, "out": 0}]
/*
function groupby(seq, keyfunc){
var lastkey = null
var key, item
var result = []
for (var i = 0; i < seq.length; i++) {
item = seq[i]
key = keyfunc(item)
if (key == lastkey){
result[result.length-1][1].push(item)
}
else {
result.push([key, [item]])
lastkey = key
}
}
return result
}
*/
function labelEndsSession(seq, keyfunc) {
var sessionId = 0;
var last = seq[0];
for (var i = 0; i < (seq.length-1); i++) {
seq[i].sessionId = sessionId;
if (keyfunc(seq[i]) != keyfunc(seq[i+1])) {
seq[i].endsSession = true;
sessionId += 1;
seq[i].last = last;
last = seq[i];
} else {
seq[i].endsSession = false;
}
}
seq[seq.length-1].endsSession = true;
seq[seq.length-1].sessionId = sessionId;
seq[i].last = last;
}
function update(data, o) {
// 1. if it's diff mode, ignore the first observation
// 2. if it's grouped mode, ignore the ones that don't end a session
opacity = function(d, i) {
return (o.diff && i == 0) || (o.grouped && !d.endsSession) ? 1e-6 : 1;
}
if (o.ordinal) {
if (o.grouped) {
// ordinal AND grouped
var ngroups = d3.sum(data, function(d) { return d.endsSession ? 1 : 0 })
o.xOrder.domain([0, ngroups-1])
var xAttr = function(d, i) { return o.xOrder(d.sessionId) }
} else {
// ordinal AND NOT grouped
o.xOrder.domain([0, data.length-1]);
var xAttr = function(d, i) { return o.xOrder(i) }
}
var wAttr = function(d, i) {
return d3.min([15, d3.round(o.w / o.xOrder.domain()[1])])
}
} else {
// time scale
var xAttr = function(d, i) { return o.xTime(d.dt) }
var wAttr = function(d, i) { return 10 }
}
if (o.diff) {
// diffed
diffProb = function(d, i) {
if (d.endsSession && o.grouped) {
var end = d.pr[0]
var start = d.last.pr[0]
return 100*(end - start)
} else if (!o.grouped && i > 0) {
// not grouped
return 100*(d.pr[0] - data[i-1].pr[0])
} else {
// grouped and NOT ends session, so ignore it
return 0
}
}
var yrange = d3.extent(data, diffProb)
o.yDiff.domain([d3.min([yrange[0], -1]), d3.max([yrange[1], 1])])
var yAttr = function(d, i) {
var diff = diffProb(d, i)
return diff > 0 ? o.yDiff(diff) : o.yDiff(0)
}
function abs(d) { return d > 0 ? d : -d }
var hAttr = function(d, i) { return abs(o.yDiff(diffProb(d, i)) - o.yDiff(0)) }
var yTicks = o.yDiff.ticks(5)
} else {
// absolute scale
var yAttr = function(d, i) { return o.yProb(100*d.pr[0]) }
var hAttr = function(d, i) { return 10 }
var yTicks = [25, 50, 75]
}
var y = function(d) { return o.diff ? o.yDiff(d) : o.yProb(d) }
var yLab = function(d) { return (o.diff && d > 0) ? '+' + d : d }
var yRule = o.svg.selectAll('g.y').data(yTicks)
var yTrans = yRule.transition().duration(250)
yTrans.select('line')
.attr('y1', y)
.attr('y2', y)
yTrans.select('text')
.attr('y', y)
.text(function(d) { return d })
var yEnter = yRule.enter()
.append('g')
.attr('class', 'y')
yEnter.append('line')
.attr('class', 'rule')
.attr('x1', o.xOrder.range()[0])
.attr('x2', o.xOrder.range()[1])
.attr('y1', y)
.attr('y2', y)
yEnter.append('text')
.attr("class", "labels")
.attr("x", 0)
.attr("y", y)
.attr("text-anchor", "right")
.text(yLab)
yRule.exit().remove()
var rects = o.svg.selectAll('rect.vote').data(data)
// update existing elements
rects.transition()
.duration(750)
.attr('x', xAttr)
.attr('y', yAttr)
.attr('width', wAttr)
.attr('height', hAttr)
.style("fill-opacity", opacity)
var legend = o.svg.selectAll('g.legend').data([data[data.length-1]])
legend.enter()
.append('g')
.attr('class', 'legend')
.append('text')
.attr('x', 0)
.attr('y', 50)
.text(function(d) {
return d.u + ' -> ' + d.pr[0] + ' at ' + d.dt;
})
rects.enter()
.append('rect')
.attr('class', 'vote')
.attr('width', wAttr)
.attr('height', hAttr)
.attr('x', xAttr)
.attr('y', yAttr)
.style("fill-opacity", opacity)
.attr('fill', function(d) { return o.userColor(d.u) })
rects.exit().remove()
}
function init(data) {
var m = [30, 30, 30, 30], // margins: top right bottom left
w = 538 - m[1] - m[3], // width
h = 400 - m[0] - m[2] // height
// default state
options = {
diff: 0,
grouped: 0,
ordinal: 0, // gets reversed immediately
yProb: d3.scale.linear().range([h, 0]).domain([0,100]),
yDiff: d3.scale.linear().range([h, 0]).domain([-.25,.25]),
xTime: d3.scale.linear().range([20, w-10]),
xOrder: d3.scale.linear().range([20, w-10]),
userColor: d3.scale.category20c(),
svg: d3.select('svg').attr("width", w).attr("height", h),
w: w,
h: h
}
options.svg.append("rect")
.attr("width", w)
.attr("height", h)
.attr("class", "background")
// setup our three toggle buttons
var toggleOrder = function() {
options.ordinal = 1 - options.ordinal
d3.select('#x-order').attr('value', ['Time', 'Ordinal'][options.ordinal])
update(data, options)
}
var toggleDiff = function() {
options.diff = 1 - options.diff
d3.select('#y-diff').attr('value', ['Probability', 'Change'][options.diff])
update(data, options)
}
var toggleGrouped = function() {
options.grouped = 1 - options.grouped
d3.select('#grouped').attr('value', ['Not Grouped', 'Grouped'][options.grouped])
update(data, options)
}
d3.select('#x-order').on('click', toggleOrder)
d3.select('#y-diff').on('click', toggleDiff)
d3.select('#grouped').on('click', toggleGrouped)
options.xTime.domain(d3.extent(data, function(d, i) { return d.dt }));
labelEndsSession(data, function(d) { return d.u});
toggleOrder()
}
data[0] = {pr: [0.50], cs: [0,0], u: 'user:0', dt: 1362002206.244313}
init(data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment