Show the sum of data values on top of stacked bar chart using Baidu ECharts
Asked Answered
H

2

4

I've created a vertical stacked bar chart with Baidu ECharts.

Is it possible to show the sum of all values on top of the stack?

Edited : i have edited my example using the (very well explained) answer from @jeffrey but i get Uncaught TypeError: Cannot read property 'forEach' of undefined error. What am i doing wrong here ?

<!DOCTYPE html>
<html style="height: 100%">
   <head>
       <meta charset="utf-8">
   </head>
   <body style="height: 100%; margin: 0">
       <div id="container" style="height: 100%"></div>
       <script type="text/javascript" src="echarts-en.min.js"></script>
       <script type="text/javascript">
var     mySeries = [
            {
                name: 'Department 1',
                type: 'bar',
                stack: 'stack1',
                data: [320, 302, 301, 334, 390, 330, 320]
            },
            {
                name: 'Department 2',
                type: 'bar',
                stack: 'stack1',
                data: [120, 132, 101, 134, 90, 230, 210]
            },
            {
                name: 'Department 3',
                type: 'bar',
                stack: 'stack1',
                data: [220, 182, 191, 234, 290, 330, 310]
            },
            {
                name: 'Department 4',
                type: 'bar',
                stack: 'stack1',
                data: [150, 212, 201, 154, 190, 330, 410]
            },
            {
                name: 'Department 5',
                type: 'bar',
                stack: 'stack1',
                data: [185, 120, 55, 66, 77, 88, 40],
                label: {
                            normal: {
                                show: true,
                                position: 'top',
                                formatter: (params) => {
                                    let total = 0;
                                    this.series.forEach(serie => {
                                       total += serie.data[params.dataIndex];
                                    })
                                    return total;
                                }
                            }
                        }
            }
        ];

        var dom = document.getElementById("container");
        var myChart = echarts.init(dom);
        var option = null;

        option = {
            tooltip : {
                trigger: 'axis',
                axisPointer : {        
                    type : 'shadow'       
                }
            },
            legend: {
                data: ['Department 1', 'Department 2','Department 3','Department 4','Department 5'],
            },
            toolbox: {
                show: true,
                orient: 'vertical',
                left: 'right',
                top: 'center',
                feature: {
                    dataView: {show: false, readOnly: false},
                    magicType: {show: true, type: ['line', 'bar', 'stack', 'tiled']},
                    restore: {show: true},
                    saveAsImage: {show: true}
                }
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            yAxis:  {
                type: 'value'
            },
            xAxis: {
                type: 'category',
                data: ["Jen\n2018", "Feb\n2018", "Mar\n2018", "Apr\n2018", "May\n2018", "Jun\n2018", "Jul\n2018"]
            }
        };;

        myChart.setOption(option, true);

        myChart.setOption({
            series: mySeries
        })
        </script>
   </body>
</html> 
Heliotrope answered 5/3, 2018 at 15:57 Comment(6)
You get an error because you've created var mySeries, but in the formatter, you reference it with this.mySeries, therefore it's undefined.Hairsplitter
@Jeffrey : It doesn't work even if i define it as this.mySeries. i've made a jsfiddle to test it here : linkHeliotrope
I checked, and I made a mistake (sorry!). The forEach should've been this.mySeries.forEach(). Now your fiddle works :)Hairsplitter
Yes! Thank you @Jeffrey for the solution! I am sorry thought i cannot mark this solution as OK since i have less of 15 reputation :-(Heliotrope
You're welcome! Ah, that explains why new users often not mark an answer as accepted! Maybe you can +1 for now, and mark as accepted later :)Hairsplitter
@Heliotrope There is no reputation threshold to accept an answer. You need to wait 15 minutes after asking, but that's it. See How does accepting an answer work?Mauretania
H
9

To get a custom serie label you should use the label formatter.

When you define your series data outside of your chart object, and map it to this, you can access the data in the formatter. It is important to use the formatter, so the original label and values will not be altered (such as tooltips and legends).


First, we define your series. The formatter should be added to the last serie. This will be the serie on top of the stack, showing the label with the total value:

this.mySeries = [
    {
        name:'Dataset 1',
        type:'bar',
        stack: 'Stack 1',
        data: [120, 132, 101, 134, 90, 230, 210]
    },
    {
        name:'Dataset 2',
        type:'bar',
        stack: 'Stack 1',
        data: [220, 182, 191, 234, 290, 330, 310]
    },
    {
        name:'Dataset 3',
        type:'bar',
        stack: 'Stack 1',
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        label: {
            normal: {
                show: true,
                position: 'top',
                formatter: (params) => {
                    let total = 0;
                    this.mySeries.forEach(serie => {
                       total += serie.data[params.dataIndex];
                    })
                    return total;
                }
            }
        }
    }
];

Here is the formatter again with annotations, so you can understand how this works:

label: {

    // You can choose from 'normal' (always visible )
    // and 'emphasis' (only visible on stack hover)
    normal: {

        // Enable the label
        show: true,

        // Position it on top of the stack
        position: 'top',

        // Add a label formatter
        // (params) holds all the data of the current serie and datapoint
        // You can use console.log(params) to check all available values
        formatter: (params) => {
            let total = 0;

            // Now we iterate over each of the series, and get the value of
            // the current xAxis datapoint by using the serie dataIndex
            this.mySeries.forEach(serie => {
               total += serie.data[params.dataIndex];
            })

            // The return value will be shown on top of your stack
            return total;
        }
    }
}

Now, all you have to do is map the series data to your chart object:

myChart.setOption({
    series: this.mySeries
})
Hairsplitter answered 5/3, 2018 at 16:45 Comment(1)
Is it possible to have 2 labels on one series? On this same example having labels on center of the series and on the third dataset; having one on center and another one on topIntern
K
1

Please see this answer, it helped me a lot, and I did a little simplification in the code, nothing relevant. I found the explanation easier. Basically you will use the formatter attribute inside the label, with some specifications

serie example

let series = [
{
    name: 'Reclamação',
    data: this.relatos_tipo_y.R,
},
{
    name: 'Elogio',
    data: this.relatos_tipo_y.E,
},
{
    name: 'Sugestão',
    data: this.relatos_tipo_y.S,
},
{
    name: 'Denúncia',
    data: this.relatos_tipo_y.D,
},

];

function for formatter

genFormatter = (series) => {
return (param) => {
    console.log(param);
    let sum = 0;
    series.forEach(item => {
        sum += item.data[param.dataIndex];
    });
    return sum
}

};

inside your chart options place this

series: series.map((item, index) => Object.assign(item, {
     type: 'bar',
     stack: true,
     label: {
         show: index === series.length - 1,
         formatter: genFormatter(series),
         fontSize: 20,
         color: 'black',
         position: 'top'
     },
})),

Click Here

It's very similar to the other answer, but I find it easier to understand the code.

Karp answered 23/7, 2019 at 11:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.