jqPlot: the mean, the mode, the median in a bar chart

In this article we will see how to automatically calculate and draw the mean (average), the median, the standard deviation and the mode of a barchart. For doing this we will use the jqPlot library.

stats_barcharts
Fig.1: a barchart showing the mean, the mode and the median.

First, let’s start with defining the sequence of input data. Generally this data comes from an external data source and it is not specified within the web page. Often this data is the result of a SQL query on a database, or reading a file (such as .txt, .csv, etc), or an acquisition from a device, or they are the data from a monitoring station or more generally from a sensor. For the sake of clarity, in this example we will directly refer to a sequence of already defined data within an array which we call data.

var data = [15.2,13.0,11.7,12.9,16.9,22.7,26.0,22.7,
            29.7,31.7,21.3,16.7,11.4,6.6,2.7,-5.0,-7.3,-11.3];

In real situations, it is up to us to implement the function, or use functions from other libraries, which will allow us to read data from external sources, making it a parsing to select only the data we are interested in, and if it is necessary, converting it to an appropriate format (often from string to numeric).

libro

If you are interested to know more, inside the book  Beginning JavaScript Charts various methods of data acquisition are discussed through several examples, such as the extraction of data from a table in a database (with PHP), such as the reading and parsing the content of external file for using specific data. Moreover, the  JSON format is widely discussed, showing the reasons why this format is so widely used.

Furthermore there are various chapters about barcharts along with several examples which explain how to develop a barchart with many different configurations – with horizontal or vertical bars, with stacked or grouped bar, and so on.

But going back to the example of this article, let’s write a web page integrating the JavaScript functions which refer to jqPlot library. With this code we will obtain the barchart as shown in Fig.2.

<html> 
<head> 
<title>Statistics on a bar chart</title> 
<!--[if lt IE 9]><script type="text/javascript" src="../src/excanvas.js"></script> <![endif]-->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.js"></script> 
<link href="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.css" rel="stylesheet" type="text/css" /> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisTickRenderer.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.categoryAxisRenderer.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.barRenderer.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasTextRenderer.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.pointLabels.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisLabelRenderer.min.js"> 
</script><script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasOverlay.js"></script> 
<script type="text/javascript" src="arrays.js"></script>
<script class="code" type="text/javascript"> $(document).ready(function(){  
var data = [15.2,13.0,11.7,12.9,16.9,22.7,26.0,22.7,
            29.7,31.7,21.3,16.7,11.4,6.6,2.7,-5.0,-7.3,-11.3]; 
var options= {                 
    title: 'Statistics on a Bar Charts',
    seriesColors: ['darkgreen'],
    series:[{                  
         renderer: $.jqplot.BarRenderer,
         rendererOptions: { 
              barMargin: 5,
              fillToZero: true
         },                  
         pointLabels: {                      
              show: true,
              formatString: '%.1f',
              seriesLabelIndex:1,
              hideZeros:false                  
         }                 
    }],
    axes: {                       
         xaxis: { 
              tickRenderer: $.jqplot.CanvasAxisTickRenderer,
              renderer: $.jqplot.CategoryAxisRenderer,    
              label: 'Sample #'                 
         },
         yaxis: {                        
              labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
              padMin: 0,                     
              pad: 1.1, 
              label: 'Temperatures (°C)',
              rendererOptions: { forceTickAt0: true}                 
         }              
    },                 
    negativeSeriesColors:['darkred'],
    grid:{                 
        background: '#f8f8f8'             
    }                    
};   
$.jqplot('graph', [data], options ); }); 
</head> 
<body> 
<div id="graph" style="height: 400px; width: 700px;"></div> 
</body>
</html>

And this is the barchart:

stats_barcharts01
Fig.2: diagramma a barre realizzato con la libreria jqPlot

Now our aim is to add the following values to the above barchart:

  • mean
  • median
  • mode

In addition we will show the standard deviation as a shaded area centered on the mean. Finally we will see how to to add a line representing the mean calculated point by point.

Before moving on to the graphics we need to implement a set of functions that carry out statistical calculation on the elements cointained within an array. To this purpose, we write a little library arrays.js, containing all the functions which we will use in the web page. These functions extend the functionalities of the Array object in JavaScript.

Array.prototype.average=function(){     
    var sum=0;     
    var j=0;     
    for(var i=0;i<this.length;i++){         
        if(isFinite(this[i])){           
            sum=sum+parseFloat(this[i]);            
            j++;         
        }     
    }     
    if(j===0){         
        return 0;     
    }else{         
        return sum/j;     
    } 
} 

Array.prototype.stddev=function(){     
    var n = 0;     
    var sum = 0.0;     
    var sum_sq = 0.0;     
    for(var i=0;i<this.length;i++){         
        n++;         
        sum += this[i];         
        sum_sq += this[i]*this[i];     
    }     
    return Math.sqrt( (sum_sq /n) - Math.pow(sum /n, 2));     
} 

Array.prototype.median=function(){     
    var median = 0,         
    sorted = [],         
    numsLen = this.length;     
    sorted = this.slice();     
    sorted.sort();     
    if (numsLen % 2 === 0) {          
        median = (sorted[numsLen / 2 - 1] + sorted[numsLen / 2]) / 2;     
    } else {          
        median = sorted[(numsLen - 1) / 2];     
    }     
    return median; 
} 

Array.prototype.mode=function(){ 
    //it returns an array. If modal it has only 1 element 
    // if multimodal it has n elements     
    var modes = [],         
    count = [],         
    i,         
    number,         
    maxIndex = 0;      
    for (i = 0; i < this.length; i += 1) {
         number = this[i];
         count[number] = (count[number] || 0) + 1;  
         if (count[number] > maxIndex) {           
              maxIndex = count[number];         
         }     
    }     
    for (i in count) 
        if (count.hasOwnProperty(i)){         
            if (count[i] === maxIndex) {             
                modes.push(Number(i));         
            }     
        }     
    return modes; 
} 

Array.prototype.average_t=function(){     
    var sum=0;     
    var j=0;     
    var average_t = [];     
    for(var i=0;i<this.length;i++){         
        if(isFinite(this[i])){           
             sum=sum+parseFloat(this[i]);           
             j++;           
             average_t[i] = sum/j         
        }     
    }     
    if(j===0){         
        return 0;     
    }else{         
        return average_t;     
    } 
}

Once we have written the library, we need to include it in our web page. Thus, we will add this row to the code.

<script type="text/javascript" src="arrays.js"></script>

and let’s make some changes to the JavaScript code.

$(document).ready(function(){  
     var data = [15.2,13.0,11.7,12.9,16.9,22.7,26.0,22.7,29.7,
                 31.7,21.3,16.7,11.4,6.6,2.7,-5.0,-7.3,-11.3]; 
     var line = data.average_t(); 
     var options= {        
         title: 'Statistics on a Bar Chart',          
         seriesColors: ['darkgreen','orange'],    
         series:[{       
             renderer: $.jqplot.BarRenderer,
             rendererOptions: {                    
                 barMargin: 5,                     
                 fillToZero: true
             },       
             pointLabels: {          
                 show: true,          
                 formatString: '%.1f',          
                 seriesLabelIndex:1,          
                 hideZeros:false       
             }    
         }],    
         axes: {   
              xaxis: {                    
                 tickRenderer: $.jqplot.CanvasAxisTickRenderer,
                 renderer: $.jqplot.CategoryAxisRenderer,
                 label: 'Sample #'       
              },             
              yaxis: {             
                 labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
                 padMin: 0,          
                 pad: 1.1,          
                 label: 'Temperatures (°C)',          
                 rendererOptions: { forceTickAt0: true}       
              }     
          },        
          negativeSeriesColors:['darkred'],       
          grid:{       background: '#f8f8f8'    },       
          canvasOverlay: {       
              show: true,       
              objects: [          
                   {dashedHorizontalLine: {             
                          name: 'average',             
                          y: data.average(),             
                          lineWidth: 3,             
                          color: 'black',             
                          shadow: false          
                   }},          
                   {dashedHorizontalLine: {             
                          name: 'mode',             
                          y: data.mode()[0],             
                          lineWidth: 3,             
                          color: 'darkred',             
                          shadow: false          
                   }},          
                   {dashedHorizontalLine: {             
                          name: 'median',             
                          y: data.median(),             
                          lineWidth: 3,             
                          color: 'blue',             
                          shadow: false          
                   }}                           
            ]       
        }           
    };   
    $.jqplot('graph', [data,line], options ); 
});

Now, three new dashed lines are shown in the bar chart (see Fig.3): the median is the blue line, the mean is the balck line and the mode is the red line. The yellow line which floats over the graph is the mean calculated in each bar, in which only the previous points (on its left) are evaluated in the caluclation. At the end, this line will tend to the black dashed line, that is the mean calculated considering all values.

stats_barcharts02
Fig.3. The mean, the mode and the median are now represented by three dashed lines.

By the end, we can also show the standard deviation as a shaded area centered on the mean line. Let’s add tje following rows of code within the canvasOverlay property.

{rectangle: {    
       name: 'stddev',    
       ymin: data.average()-data.stddev(),    
       ymax: data.average()+data.stddev(), 
       color: 'rgba(150,150,150,0.3)',    
       shadow: false  
}}

Disabling the drawing of the median and mode lines we obtain the following chart:

stats_barcharts03
Fig.4: a bar chart showing the standard deviation as a shaded area centered on the mean line.

Leave a Reply