A quick statistics library for Arduino.

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.

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.
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: ");
16.   Serial.print("Geometric mean: ");
18.   Serial.print("Minimum: ");
20.   Serial.print("Maximum: ");
22.   Serial.print("Standard Deviation: ");
24.   Serial.print("Standard Error: ");
26.   Serial.print("Coefficient of Variation (%): ");
28.   Serial.print("Median: ");
30.   Serial.print("Mode: ");
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.   }
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