Transforming Bar Graph

by eprouver

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.1/underscore-min.js"></script>
<div>
    <p>The yellow dots show the performance of the 'average' student.</p>
    <p>'Work by Day' shows all of the exercises attempted in that day, and what the performance of an average student would be had they attempted those same exercises.</p>
    <p>'Scores by Exercise' shows the breakdown per exercise with the corresponding average score.</p>
    <p>I'll zoom this in to show only a selected day, and I'd like to color the dots to show if the student is above or below average.</p>
    <p>Refresh to see new demo data</p>
</div>
<hr/>
<form>
    <label>
        <input type="radio" name="mode" value="grouped">Scores by Exercise</label>
    <label>
        <input type="radio" name="mode" value="stacked" checked="">Work By Day</label>
</form>

CSS

*{
    font-family: Arial;
}

text{
    font-size: 12px;
}

g.avg circle{
    fill:yellow;
    stroke: black;
    stroke-width: 1px;
}

JavaScript

var days = d3.range(7);
var offsets = d3.range(7).map(function () {
    return 0;
});
var stack = d3.layout.stack().out(function (d, y0, y) {
    d.y0 = offsets[d.x];
    offsets[d.x] += y
    d.y = y;
});
var m = 7;
var maxTopics = 8;

days = _.map(days, function (v, i) {
    return _.map(d3.range(maxTopics), function () {
        var score = Math.random();
        //Fake Data
        return {
            x: i, //Date
            id: Math.random(),
            y: score, //Score,
            avg: Math.max(Math.sin(score) + (Math.random() * 0.5 - 0.25), 0)
        }
    }).sort(function (b, a) {
        return b.y - a.y
    })
});

//Sum of Averages for each day
var avgs = _.map(days, function (v) {
    return _.reduce(v, function (i, v) {
        return i + v.avg;
    }, 0)
});

//Convert to Stack Layout
var layers = stack(days)

var yGroupMax = d3.max(layers, function (layer) {
    return d3.max(layer, function (d) {
        return d.y;
    });
}),
    yStackMax = d3.max(layers, function (layer) {
        return d3.max(layer, function (d) {
            return d.y0 + d.y;
        });
    });

var margin = {
    top: 50,
    right: 10,
    bottom: 20,
    left: 10
},
width = 860 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .domain(d3.range(7))
    .rangeRoundBands([0, width], 0.12);

var y = d3.scale.linear()
    .domain([0, yStackMax])
    .range([height, 0]);

var color = d3.scale.linear()
    .domain([0, 1])
    .range(["#ff0000", "#00ff00"]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickSize(0)
    .tickPadding(6)
    .orient("bottom");

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var layer = svg.selectAll(".layer")
    .data(layers)
    .enter().append("g")
    .attr("class", "layer")

var...