Built with blockbuilder.org
forked from enjalot's block: interviewing.io: mean lines
Built with blockbuilder.org
forked from enjalot's block: interviewing.io: mean lines
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.8.0/d3-legend.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
svg { width:100%; height: 100% } | |
path { | |
fill: none; | |
stroke-width: 3; | |
stroke-opacity: 0.4; | |
} | |
line.legend { | |
stroke: #c4c4c4; | |
stroke-width: 1; | |
} | |
g.cell { | |
cursor: pointer; | |
} | |
circle.node { | |
pointer-events: none; | |
} | |
line.link { | |
stroke: #efefef; | |
stroke-dasharray: 18 2; | |
pointer-events: none; | |
} | |
</style> | |
</head> | |
<body> | |
<script> | |
// Feel free to change or delete any of the code you see! | |
var svg = d3.select("body").append("svg") | |
var chartg = svg.append("g").attr({ | |
transform: "translate(0, -50)" | |
}) | |
var legendX = 50; | |
var legendY = 29; | |
var linesX = 0; | |
var linesY = 134; | |
var squareWidth = 10; | |
var squareHeight = 10; | |
d3.json("interviews.json", function(err, interviewees) { | |
//console.log(interviewees) | |
function ranges(mean) { | |
if(mean <= 4 && mean >= 3.5) { | |
return 4; | |
} else if(mean < 3.5 && mean >= 2.75) { | |
return 3; | |
} else if(mean < 2.75) { | |
return 2; | |
} | |
} | |
var matches = {}; | |
var meanData = [] ; interviewees.forEach(function(interview, index) { | |
var mean = d3.mean(interview); | |
var stddev = d3.deviation(interview); | |
var points = interview.map(function(score,i) { | |
return { | |
score: score, | |
mean: mean, | |
index: index | |
} | |
}) | |
points.mean = mean; | |
points.stddev = stddev; | |
points.index = index; | |
// we have several interviewees | |
var key = (Math.floor(mean*1000)/1000) + "::" + (Math.floor(stddev*1000)/1000); | |
var match = matches[key]; | |
if(!match) matches[key] = 0; | |
matches[key] += 1; | |
points.offset = matches[key]; | |
meanData.push(points) | |
}) | |
var maxMean = d3.max(meanData, function(d) { return d.mean }); | |
//console.log("maxMean", maxMean) | |
var maxStddev = d3.max(meanData, function(d) { return d.stddev }); | |
//console.log("maxStddev", maxStddev) | |
var xScale = d3.scale.linear() | |
.domain([0, maxStddev]) | |
.range([150, 700]) | |
var yScale = d3.scale.linear() | |
.domain([1, 4]) | |
.range([500, 100]) | |
var colorScale = d3.scale.linear() | |
.domain([1, 2, 3, 4]) | |
.range(["#ff0f5f", "#e63ba8", "#ba48d9", "#267fd3"]) | |
svg.append("g") | |
.attr("class", "legendOrdinal") | |
.attr("transform", "translate(" + [legendX, legendY] + ")"); | |
var legendScale = d3.scale.linear() | |
.domain([4,3,2,1]) | |
.range(colorScale.range().reverse()) | |
var legendOrdinal = d3.legend.color() | |
.shapeWidth(40) | |
.shapeHeight(40) | |
.shapePadding(94) | |
.cells(4) | |
.scale(legendScale); | |
svg.select(".legendOrdinal") | |
.call(legendOrdinal); | |
chartg.selectAll("line.legend").data(d3.range(4).reverse()) | |
.enter().append("line").classed("legend", true) | |
.attr({ | |
x1: function(d) { return linesX + xScale.range()[0]}, | |
y1: function(d) { return yScale(d) - linesY}, | |
x2: function(d) { return linesX + xScale.range()[1]}, | |
y2: function(d) { return yScale(d) - linesY}, | |
}) | |
var meanSquares = chartg.selectAll("rect.mean") | |
.data(meanData, function(d) { return d.index}) | |
meanSquares.enter().append("rect").classed("mean", true) | |
meanSquares.attr({ | |
x: function(d) { return xScale(d.stddev ) + d.offset * (1+squareWidth)}, | |
y: function(d) { return yScale(d.mean) - squareHeight/2 }, | |
width: squareWidth, | |
height: squareHeight, | |
fill: function(d) { return colorScale(d.mean)} | |
}).on("click", function(d) { | |
console.log("clicked", d); | |
fade(); | |
unfade(d); | |
clearForce(); | |
addForceNodes(d); | |
}).on("mouseover", function(d) { | |
console.log(d.index, d.mean, d.stddev); | |
fade(); | |
unfade(d); | |
clearForce(); | |
addForceNodes(d); | |
}) | |
.on("mouseout", function(d) { | |
unfade(); | |
clearForce(); | |
}) | |
chartg.append("text") | |
.text("deviants") | |
.attr({ | |
"font-size": "32px", | |
"font-family": "Open Sans, Helvetica, san-serif", | |
x: 495, | |
y: 438, | |
"paint-order":"stroke", | |
stroke: "#cfe0e7", | |
"stroke-width": 8, | |
"stroke-opacity": 0.3, | |
"stroke-linecap": "butt", | |
"stroke-linejoin": "miter", | |
"cursor":"pointer" | |
}).on("mouseover", function() { | |
fade(); | |
clearForce(); | |
chartg.selectAll("rect.mean") | |
.filter(function(d) { return d.stddev > 0.7072}) | |
.attr({ | |
opacity: 0.6, | |
fill: function(c) { return colorScale(c.mean)} | |
}) | |
.each(function(d){ | |
addForceNodes(d); | |
}) | |
}).on("mouseout", function() { | |
unfade(); | |
clearForce(); | |
}) | |
legendOrdinal.on("cellover", function(category){ | |
fade(); | |
chartg.selectAll("rect.mean") | |
.filter(function(d) { | |
var hasCat = false; | |
d.forEach(function(i) { | |
if(i.score == category) hasCat = true; | |
}) | |
return hasCat; | |
}) | |
.attr({ | |
opacity: 0.6, | |
fill: function(c) { return colorScale(c.mean)} | |
}) | |
.each(function(d){ | |
addForceNodes(d); | |
}) | |
}).on("cellout", function() { | |
unfade(); | |
clearForce(); | |
}) | |
function unfade(d) { | |
var selection = chartg.selectAll("rect.mean") | |
if(!d) { | |
selection.attr({ | |
opacity: 1, | |
fill: function(c) { return colorScale(c.mean)} | |
}) | |
} else { | |
selection.filter(function(f) { | |
if(f === d) { | |
d3.select(this).attr({ | |
opacity: 0.6, | |
fill: function(c) { return colorScale(c.mean)} | |
}) | |
} | |
}) | |
} | |
} | |
function fade() { | |
chartg.selectAll("rect.mean").attr({ | |
fill: "#b7b7b7" | |
}) | |
} | |
var xrange = xScale.range() | |
var width = Math.abs(xrange[1] - xrange[0]); | |
var yrange = yScale.range(); | |
var height = Math.abs(yrange[0] - yrange[1]); | |
var forceg = chartg.append("g") | |
.attr("transform", "translate(" + [0,0] + ")") | |
var force = d3.layout.force() | |
.size([width, height]) | |
.gravity(0.0) | |
.friction(0.89) | |
.charge(-5) | |
.linkStrength(0) | |
.nodes([]) | |
.links([]) | |
force.start() | |
force.on("tick", function(e) { | |
var k = 0.36 * e.alpha | |
var nodes = force.nodes(); | |
nodes.forEach(function(t,i) { | |
// console.log(t.x, t.targetX) | |
t.x += (-t.x + t.targetX) * k; | |
t.y += (-t.y + t.targetY) * k; | |
if(t.interviewee){ | |
t.x = t.targetX + squareWidth/2; | |
t.y = t.targetY; | |
} | |
}) | |
forceg.selectAll("circle.node") | |
.attr({ | |
cx: function(d) { return d.x }, | |
cy: function(d) { return d.y } | |
}) | |
forceg.selectAll("line.link") | |
.attr({ | |
x1: function(d) { return d.source.x }, | |
y1: function(d) { return d.source.y }, | |
x2: function(d) { return d.target.x }, | |
y2: function(d) { return d.target.y }, | |
}) | |
}) | |
var removeDelay = 100; | |
function clearForce() { | |
forceg.selectAll("circle.node") | |
//.transition().duration(removeDelay) | |
//.attr({opacity: 0}) | |
.remove(); | |
forceg.selectAll("line.link") | |
//.transition().duration(removeDelay) | |
//.attr({opacity: 0}) | |
.remove(); | |
force.links([]) | |
force.nodes([]) | |
} | |
function addForceNodes(interviewee) { | |
var nodes = force.nodes(); | |
var links = force.links(); | |
console.log("links", links) | |
var x = xScale(interviewee.stddev) + interviewee.offset * (1+squareWidth) | |
var y = yScale(interviewee.mean) | |
//console.log("X,Y", x,y) | |
//generate links between mean "node" and | |
var source = { | |
index: interviewee.index, | |
x: x, | |
y: y, | |
px: x, | |
py: y, | |
targetX: x, | |
targetY: y, | |
mean: interviewee.mean, | |
stddev: interviewee.stddev, | |
interviewee: true, | |
opacity: 0, | |
} | |
nodes.push(source) | |
interviewee.forEach(function(d,i) { | |
var sx = x + 10 * Math.random() + Math.random(); | |
var sy = y + 10 * Math.random() + Math.random(); | |
var node = { | |
index: interviewee.index + "-" + i, | |
px: sx, | |
py: sy, | |
x: sx, | |
y: sy, | |
targetX: x, | |
targetY: yScale(+d.score), | |
mean: interviewee.mean | |
} | |
nodes.push(node) | |
links.push({ | |
index: source.index + "-" + node.index, | |
source: source, | |
//source: 0, | |
target: node | |
}) | |
}) | |
//console.log("nodes", nodes); | |
var lines = forceg.selectAll("line.link") | |
.data(links) | |
lines.enter().append("line").classed("link", true) | |
var circles = forceg.selectAll("circle.node") | |
.data(nodes, function(d) { return d.index }) | |
circles.enter().append("circle").classed("node", true) | |
circles.attr({ | |
"pointer-events": "none", | |
r: 4, | |
opacity: function(d) { | |
if(d.opacity || d.opacity === 0) return d.opacity; | |
return 1; | |
}, | |
fill: function(d) { return colorScale(d.mean)} | |
}) | |
force.links(links) | |
force.nodes(nodes); | |
force.start() | |
circles.exit().remove(); | |
} | |
//addForceNodes(meanData[1]) | |
}) | |
</script> | |
</body> |
[[3,3,4],[3,3,3,3,3,3,4,3],[2,3,3,3,3],[3,3],[2,3,3],[4,2,4],[3,2,3,4,3,2],[3,2,4,3,2,3,2,3,4,3,3,2,4,3,4,4,3],[3,4,3,1,4,3,4,3],[4,4,3],[2,3,2],[3,3,4,3,3,4,4,2,3,4,4],[3,2,2],[2,1,3,3,2,3,2,3],[4,3,3,4,4,4],[3,4,4,3,3,3,4,3,4,2,4],[3,4,3,3,4],[3,2,3,3,2],[2,2,3,3,2,2,4,4,2,3,3],[3,3,4,3,4,4,4],[2,3],[3,3,2,3],[2,2],[3,3,3,3,3,3,2,3,3,3,4,3,2],[1,2,3,2,3,2],[1,3,3,2],[2,2,3,3],[4,3],[3,3],[3,2,2,2,3,3,3,2,2],[4,3,3],[4,3,4],[3,3,3,2,2,3,4],[3,3,4,3,2,4,4,3],[2,4,2,3],[3,4,3],[3,3,4,4,3,2,4],[4,4,4,4],[3,3,3],[4,4],[3,2,4,4,4],[3,4],[3,3,2],[4,4,4],[3,3,3,3,3,3],[3,2],[3,3,2],[3,3],[2,2,3,3,3],[3,4,3,3],[3,3],[2,2],[2,2],[3,4,3,3,3],[2,2],[3,4],[4,4,4,4,4],[4,3],[2,3],[2,2,3],[3,2,4],[2,4,3,3],[3,3],[3,3],[2,3],[4,3],[4,3]] |