Stephan Vierkant
Stephan Vierkant

Reputation: 10144

Stacked bar chart with (computed) average line in Plotly.js

I've got a (stacked) bar chart and I want an average line plotted on my chart.

Let's take this example:

var trace1 = {
  x: ['giraffes', 'orangutans', 'monkeys'],
  y: [20, 14, 23],
  name: 'SF Zoo',
  type: 'bar'
};

var trace2 = {
  x: ['giraffes', 'orangutans', 'monkeys'],
  y: [12, 18, 29],
  name: 'LA Zoo',
  type: 'bar'
};

var data = [trace1, trace2];

var layout = {barmode: 'stack'};

Plotly.newPlot('myDiv', data, layout, {showSendToCloud:true});

Result: result chart

Expected output: enter image description here

I've found a similar question, but in that case it was pretty easy to add a line with a 'fixed' value. In this case I've got a stacked bar chart nicolaskruchten/pivottable, so the user can easily drag and drop columns. That makes computing the average harder.

I can loop through all results and compute the average value, but since Plotly is very powerful and has something like aggregate functions, I feel like there should be a better way.

How can I add a (computed) average line to my (stacked) bar chart?

Upvotes: 0

Views: 2676

Answers (1)

Md Nasir Fardoush
Md Nasir Fardoush

Reputation: 820

Plotly.js not provided any direct options for drawing average line.
But you can do this simple way.

//Find average value for Y
function getAverageY() {
    allYValues = trace1.y.map(function (num, idx) {
        return num + trace2.y[idx];
    });
    if (allYValues.length) {
        sum = allYValues.reduce(function (a, b) {
            return a + b;
        });
        avg = sum / allYValues.length;
    }
    return avg;
}

//Create average line in shape
var layout = {
    barmode: 'stack',
    shapes: [{
        type: 'line',
        xref: 'paper',
        x0: 0,
        y0: getAverageY(),
        x1: 1,
        y1: getAverageY(),
        line: {
            color: 'green',
            width: 2,
            dash: 'dot'
        }
    }]
};

Updated:

You need to update your graph after loading this drawing a average line for any numbers of trace.

//Check graph is loaded    
if (document.getElementById('myDiv')) {
    //draw average line
    drawAvgLine(document.getElementById('myDiv'))
}

function drawAvgLine(graph) {
    var graphData = graph.data; //Loaded traces
    //making new layout
    var newLayout = {
        barmode: 'stack',
        shapes: [{
            type: 'line',
            xref: 'paper',
            x0: 0,
            y0: getAverageY(graphData),
            x1: 1,
            y1: getAverageY(graphData),
            line: {
                color: 'green',
                width: 2,
                dash: 'dot'
            }
        }]
    };
    //Update plot pass existing data
    Plotly.update('myDiv', graphData, newLayout)
}
//Calculate avg value
function getAverageY(graphData) {
    var total = [],
        undefined;
    for (var i = 0, n = graphData.length; i < n; i++) {
        var arg = graphData[i].y
        for (var j = 0, n1 = arg.length; j < n1; j++) {
            total[j] = (total[j] == undefined ? 0 : total[j]) + arg[j];
        }
    }
    return total.reduce(function (a, b) {
        return a + b;
    }) / total.length;
}

Upvotes: 2

Related Questions