A quick statistics library for Arduino.

Last Modified: January 2, 2017, at 010:57 AM
By: dndubins
Platforms: Built on Arduino IDE 1.6.6 (not tested with earlier platforms)

QuickStats is a library that provides simple descriptive statistics for elements in float arrays, in Arduino.

Link to library: https://github.com/dndubins/QuickStats


Introduction

I developed this library to help quickly accomplish median and mode filtering when collecting sensor data. Functions in this library operate on an array of float variables, of dimension "m", and return the corresponding statistic. This library was originally created for a data smoothing strategy for float variables. Using a median or mode filtering strategy (opposed to mean filtering) is better at removing spikes from aberrant readings. The other functions (stdev, CV, etc.) were included for fun.

A bubble sort algorithm is also contained in this library which was necessary to calculate median and mode.

The functions available in the library include:

 average(samples[],m);     // the average of elements in samples[m]
 g_average(samples[],m);   // the geometric mean of elements in samples[m]
 minimum(samples[],m);     // the minimum value in samples[m]
 maximum(samples[],m);     // the maximum value in samples[m]
 stdev(samples[],m);       // the sample standard deviation of elements in samples[m]
 stderror(samples[],m);    // the standard error of elements in samples[m] 
 CV(samples[],m);          // the coefficient of variation in samples[m] in percent
 bubbleSort(samples[],m);  // sorting algorithm to arrange the elements in samples[m]
 fabs(sample);             // absolute value of a float, used in mode()
 median(samples[],m);       // the median of elements in samples[m]
 mode(samples[],m,epsilon); // the mode of elements in samples[m] (returns 0 if no mode)

Usage

This sketch shows an example sketch of how the library may be used.

  1. // Example program for use with QuickStats.h
  2.  
  3. #include "QuickStats.h"
  4.  
  5. int numreadings = 9;
  6. float readings[]={1.0,2.2,4.8,3.3,6.1,2.2,3.8,7.0,2.2};
  7.  
  8. QuickStats stats; //initialize an instance of this class
  9.  
  10. void setup()
  11. {
  12.   Serial.begin(9600);
  13.   Serial.println("Descriptive Statistics");
  14.   Serial.print("Average: ");
  15.   Serial.println(stats.average(readings,numreadings));
  16.   Serial.print("Geometric mean: ");
  17.   Serial.println(stats.g_average(readings,numreadings));
  18.   Serial.print("Minimum: ");
  19.   Serial.println(stats.minimum(readings,numreadings));
  20.   Serial.print("Maximum: ");
  21.   Serial.println(stats.maximum(readings,numreadings));
  22.   Serial.print("Standard Deviation: ");
  23.   Serial.println(stats.stdev(readings,numreadings));
  24.   Serial.print("Standard Error: ");
  25.   Serial.println(stats.stderror(readings,numreadings));
  26.   Serial.print("Coefficient of Variation (%): ");
  27.   Serial.println(stats.CV(readings,numreadings));
  28.   Serial.print("Median: ");
  29.   Serial.println(stats.median(readings,numreadings));
  30.   Serial.print("Mode: ");
  31.   Serial.println(stats.mode(readings,numreadings,0.00001));
  32. }
  33.  
  34. void loop()
  35. {
  36. }
  37. //END OF FILE

Notes

To use the library, make a folder in your SKETCHBOOKPATH\libraries with the name QuickStats and put the .h and .cpp there.

To do

Looking at creating a version for integers.

QuickStats.h file

QuickStats.h:

  1. /*  QuickStats.h - Library for quick descriptive statistics of an array samples[] of size m,
  2.  *  assuming a normal distribution.
  3.  *  Created by David Dubins, January 10th, 2016.
  4.  *  Released into the public domain.
  5.  */
  6.  
  7. #ifndef QuickStats_h
  8. #define QuickStats_h
  9.  
  10. #include <Arduino.h>
  11.  
  12. class QuickStats {
  13.   public:
  14.     QuickStats();
  15.     ~QuickStats();
  16.     float average(float samples[],int m);
  17.     float g_average(float samples[],int m);
  18.     float minimum(float samples[],int m);
  19.     float maximum(float samples[],int m);      
  20.     float stdev(float samples[],int m);
  21.     float stderror(float samples[],int m);
  22.     float CV(float samples[],int m);
  23.     void bubbleSort(float A[],int len);
  24.     float median(float samples[],int m);
  25.     float mode(float samples[],int m,float epsilon);
  26. };
  27.  
  28. #endif
  29. //END OF FILE

QuickStats.cpp

  1. /* QuickStats.cpp - Library for quick descriptive statistics of an array samples[] of size m
  2.  *  Created by David Dubins, January 10th, 2016.
  3.  *  Released into the public domain.
  4.  *  Requires Arduino 1.6.6 or greater.
  5.  *  http://pb435.pbworks/com
  6.  */
  7.  
  8. #include "Arduino.h"
  9. #include "QuickStats.h"
  10. #include <math.h>
  11.  
  12. QuickStats::QuickStats(){/*nothing to construct*/}
  13. QuickStats::~QuickStats(){/*nothing to destruct*/}
  14.  
  15. float QuickStats::average(float samples[],int m)
  16. {
  17.   float total1=0.0;
  18.   for(int i=0;i<m;i++){
  19.     total1=total1+samples[i];
  20.   }
  21.   return total1/(float)m;
  22. }
  23.  
  24. float QuickStats::g_average(float samples[],int m)
  25. {
  26.   float total1=0.0;
  27.   for(int i=0;i<m;i++){
  28.     total1=total1+log(samples[i]);
  29.   }
  30.   return exp(total1/(float)m);
  31. }
  32.  
  33. float QuickStats::stdev(float samples[],int m)
  34. {
  35.   float avg=0.0;
  36.   float total2=0.0;
  37.   avg=average(samples,m);
  38.   for(int i=0;i<m;i++){
  39.     total2 = total2 + pow(samples[i] - avg,2);
  40.   }
  41.   return sqrt(total2/((float)m-1));
  42. }
  43.  
  44. float QuickStats::minimum(float samples[],int m)
  45. {
  46.   float sorted[m];   //Define and initialize sorted array
  47.   for(int i=0;i<m;i++){
  48.     sorted[i]=samples[i];
  49.   }
  50.   bubbleSort(sorted,m);  // Sort the values
  51.   return(sorted[0]); // first element is the minimum
  52. }
  53.  
  54. float QuickStats::maximum(float samples[],int m)
  55. {
  56.   float sorted[m];   //Define and initialize sorted array
  57.   for(int i=0;i<m;i++){
  58.     sorted[i]=samples[i];
  59.   }
  60.   bubbleSort(sorted,m);  // Sort the values
  61.   return(sorted[m-1]);   // last element is the maximum
  62. }
  63.  
  64. float QuickStats::stderror(float samples[],int m) {
  65.   float temp1=0.0;
  66.   temp1=stdev(samples,m);
  67.   return (temp1/sqrt((float)m));
  68. }
  69.  
  70. float QuickStats::CV(float samples[],int m)  //Coefficient of variation (%RSD, or relative stdev)
  71. {
  72.   float avg=0.0;
  73.   float sd=0.0;
  74.   avg=average(samples,m);
  75.   sd=stdev(samples,m);
  76.   return 100.0*sd/avg;
  77. }
  78.  
  79. void QuickStats::bubbleSort(float A[],int len) {
  80.   unsigned long newn;
  81.   unsigned long n=len;
  82.   float temp=0.0;
  83.   do {
  84.     newn=1;
  85.     for(int p=1;p<len;p++){
  86.       if(A[p-1]>A[p]){
  87.         temp=A[p];           //swap places in array
  88.         A[p]=A[p-1];
  89.         A[p-1]=temp;
  90.         newn=p;
  91.       } //end if
  92.     } //end for
  93.     n=newn;
  94.   } while(n>1);
  95. }
  96.  
  97. float QuickStats::fabs(float sample) // calculate absolute value
  98. {
  99.   if(sample<0.f){
  100.     return -sample;
  101.   }else{
  102.     return sample;
  103.   }
  104. }
  105.  
  106. float QuickStats::median(float samples[],int m) //calculate the median
  107. {
  108.   //First bubble sort the values: https://en.wikipedia.org/wiki/Bubble_sort
  109.   float sorted[m];   //Define and initialize sorted array.
  110.   float temp=0.0;      //Temporary float for swapping elements
  111.   /*Serial.println("Before:");
  112.   for(int j=0;j<m;j++){
  113.     Serial.println(samples[j]);
  114.   }*/
  115.   for(int i=0;i<m;i++){
  116.     sorted[i]=samples[i];
  117.   }
  118.   bubbleSort(sorted,m);  // Sort the values
  119.   /*Serial.println("After:");
  120.   for(int i=0;i<m;i++){
  121.     Serial.println(sorted[i]);
  122.   }*/
  123.   if (bitRead(m,0)==1) {  //If the last bit of a number is 1, it's odd. This is equivalent to "TRUE". Also use if m%2!=0.
  124.     return sorted[m/2]; //If the number of data points is odd, return middle number.
  125.   } else {    
  126.     return (sorted[(m/2)-1]+sorted[m/2])/2; //If the number of data points is even, return avg of the middle two numbers.
  127.   }
  128. }
  129.  
  130. float QuickStats::mode(float samples[],int m,float epsilon) //calculate the mode.
  131. //epsilon is the tolerance for two measurements to be equivalent.
  132. {
  133.   //First bubble sort the values: https://en.wikipedia.org/wiki/Bubble_sort
  134.   float sorted[m];   //Temporary array to sort values.
  135.   float temp=0;      //Temporary float for swapping elements
  136.   float unique[m];   //Temporary array to store unique values
  137.   int uniquect[m]; //Temporary array to store unique counts
  138.   /*Serial.println("Before:");
  139.   for(int i=0;i<m;i++){
  140.     Serial.println(samples[i]);
  141.   }*/
  142.   for(int i=0;i<m;i++){
  143.     sorted[i]=samples[i];
  144.   }
  145.   bubbleSort(sorted,m);  // Sort the values
  146.   /*Serial.println("Sorted:");
  147.   for(int i=0;i<m;i++){
  148.     Serial.println(sorted[i]);
  149.   }*/
  150.   // Now count the number of times each unique number appears in the sorted array.
  151.   unique[0]=sorted[0];
  152.   uniquect[0]=1;
  153.   int p=0; // counter for # unique numbers
  154.   int maxp=0;
  155.   int maxidx=0;
  156.   for(int i=1;i<m;i++){
  157.     if(fabs(sorted[i]-sorted[i-1])<epsilon){
  158.       uniquect[p]++;  //if same number again, add to count
  159.       if(uniquect[p]>maxp){
  160.         maxp=uniquect[p];
  161.         maxidx=p;      
  162.       }
  163.     } else {
  164.       p++;
  165.       unique[p]=sorted[i];
  166.       uniquect[p]=1;
  167.     }
  168.   }
  169.   /*for(int i=0;i<p+1;i++){
  170.     Serial.println("Num: " + (String)unique[i] +"   Count: " + (String)uniquect[i]);
  171.   }*/  
  172.   if (maxp>1) {    
  173.     return unique[maxidx]; //If there is more than one mode, return the lowest one.
  174.   } else {
  175.     return 0.0; //If there is no mode, return a zero.
  176.   }
  177. }
  178. //END OF FILE

Share