3
votes

I've got a working Stacked Area Chart using NVD3.js: working jsfiddle here

var volumeData = [{"key":"Hit","values":[[1.3781628E12,12],[1.3782492E12,9],[1.3783356E12,9],[1.378422E12,4],[1.3785084E12,2],[1.3785948E12,3],[1.3786812E12,6],[1.3787676E12,5],[1.378854E12,1],[1.3789404E12,5],[1.3790268E12,1],[1.3791132E12,3],[1.3791996E12,0],[1.379286E12,2],[1.3793724E12,0]]},{"key":"Miss","values":[[1.3781628E12,3],[1.3782492E12,3],[1.3783356E12,1],[1.378422E12,12],[1.3785084E12,4],[1.3785948E12,7],[1.3786812E12,10],[1.3787676E12,13],[1.378854E12,14],[1.3789404E12,8],[1.3790268E12,5],[1.3791132E12,2],[1.3791996E12,3],[1.379286E12,11],[1.3793724E12,6]]}];


(function(data){
    var colors = d3.scale.category20();
    keyColor = function(d, i) {return colors(d.key)};

    var chart;
    nv.addGraph(function() {
        chart = nv.models.stackedAreaChart()
        .x(function(d) { return d[0] })
        .y(function(d) { return d[1] })
        .color(keyColor);

        chart.xAxis.tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });

        chart.yAxis.tickFormat(d3.format('d'));

        d3.select('#graph svg')
        .datum(data)
        .transition().duration(0)
        .call(chart);

        //nv.utils.windowResize(chart.update);

        return chart;
    });
})(volumeData);

What I'd like to do is add an "average" line for each series over the visible x-range. I'll have no trouble calculating the average for each series, I just don't know how to get the line to show up on the graph.

Is it possible with nvd3.js, or would I have to drop down to d3 to do it?

3
You have to use D3 to do this. NVD3 doesn't offer this functionality. - Lars Kotthoff
Fair enough. Could you explain how? - Adam Tuttle
You probably want to modify the source of NVD3 so you have access to all the scales etc. Alternatively, you could go pure D3. You can of course also simply add the average as a separate data series. - Lars Kotthoff
Adding it as a separate data series might work in a normal line graph, but in a stacked area chart, doing so would (at least, I think) add to the total area covered, not overlay it. - Adam Tuttle

3 Answers

8
votes

Two other options:
1. Quick but a little hacky, create another line in your data for each average line by setting the same y value for each x value. You'll get a straight line although each x tick will have a tooltip with the same average value.
2. Draw a fixed line across the chart from the point where the average is on the yAxis:

//margin from chart declaration
var margin = { top: 30, right: 60, bottom: 60, left: 100 };
//calculate the yScale
var yScale = chart.yAxis.scale();
//call generic function...since you'll want this on potentially multiple types of charts
drawFixedLineAndText(chartID, 960, margin, <your average value goes here>, yScale, <your average label text goes here>);

function drawFixedLineAndText(chartName, width, margin, yValue, yValueScale, text) {
var svg = d3.select("#" + chartName + " svg");
svg.append("line")
    .style("stroke", "#FF7F0E")
    .style("stroke-width", "2.5px")
    .attr("x1", margin.left)
    .attr("y1", yValueScale(yValue) + margin.top)
    .attr("x2", width - margin.right)
  .attr("y2", yValueScale(yValue) + margin.top);


//add text to fixed line
d3.select("#" + chartName + " svg")
    .append("text")
    .attr("x", width - margin.right / 2)
    .attr("y", yValueScale(yValue) + margin.top)
    .attr("text-anchor", "middle")
    .text(text);
//end fixed line
}
2
votes

It can be done in D3. Calculate average for the line while processing area data:

var line = d3.svg.line().interpolate("basis").x(function(d) {
    return x(d.x_axis);
}).y(function(d) {
    return y(d.y_axis);
});
var stacked_data = [];
var line_data = [];
data.forEach(function(d) {
    var total = 0;
    for (var i = 0; i < AREAS.length; i++) {
        var nd = {};
        nd.date = X_DATA_PARSE(d.date);
        nd.key = AREAS[i];
        nd.value = +d[AREAS[i]];
        stacked_data.push(nd);
        total = total + nd.value;
    }
    var ld = {};
    ld.x_axis = X_DATA_PARSE(d.date);
    ld.y_axis = total / AREAS.length;
    line_data.push(ld);
});

Then draw the line like normal line chart:

svg.append("path")
  .datum(line_data)
  .attr("class", "line")
  .attr("d", line);

Here's a complete example:

http://vida.io/documents/jXKmv3j3gF9LmtiyW