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]] |