diff --git a/CHANGELOG.md b/CHANGELOG.md index 6357aeb..0181ec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.7] - 2024-08-12 +- Fix #33, add **float getCoefficientOfVariation()** +- update readme.md +- update keywords.txt + ## [0.4.6] - 2024-06-15 - Fix #30, add **float getSum()** (thanks to heidnerd) - Fix #31, add **float getStandardDeviationLast(uint16_t count)** (thanks to alvaro-oliver) diff --git a/README.md b/README.md index e7bbfc2..ff06d81 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ update the internal **\_sum**. - https://github.com/RobTillaart/RunningMedian - https://github.com/RobTillaart/statHelpers - combinations & permutations - https://github.com/RobTillaart/Statistic +- https://github.com/RobTillaart/Student ## Interface @@ -83,8 +84,14 @@ Updates the variables used by **getFastAverage()** to improve its accuracy again - **float getStandardDeviation()** returns the standard deviation of the current content. Needs more than one element to be calculable. - **float getStandardError()** returns the standard error of the current content. -- **float getMin()** returns minimum since last clear, does not need to be in the buffer any more. -- **float getMax()** returns maximum since last clear, does not need to be in the buffer any more. +- **float getCoefficientOfVariation()** returns coefficient of variation. +This is defined as standardDeviation / Average. +It indicates if the distribution is relative small ( < 1) or relative wide ( > 1). +Note it has no meaning when the average is zero (or close to zero). +- **float getMin()** returns minimum since last call to clear(). +The returned value does not need to be in the buffer any more. +- **float getMax()** returns maximum since last call to clear(). +The returned value does not need to be in the buffer any more. - **float getMinInBuffer()** returns minimum in the internal buffer. - **float getMaxInBuffer()** returns maximum in the internal buffer. - **float getSum()** returns sum of values in the internal buffer. @@ -94,7 +101,7 @@ Needs more than one element to be calculable. - **bool bufferIsFull()** returns true if buffer is full. - **float getElement(uint16_t index)** get element directly from internal buffer at index. (debug) -- **uint16_t getSize()** returns the size of the internal array. +- **uint16_t getSize()** returns the size of the internal array as set in constructor. - **uint16_t getCount()** returns the number of slots used of the internal array. @@ -114,6 +121,7 @@ Returns NAN if there are no elements and it will reduce count if there are less count elements in the buffer. - **float getAverageLast(uint16_t count)** get the average of the last count elements. +- **float getStandardDeviationLast(uint16_t count)** get the stddev of the last count elements. - **float getMinInBufferLast(uint16_t count)** get the minimum of the last count elements. - **float getMaxInBufferLast(uint16_t count)** get the maximum of the last count elements. @@ -174,22 +182,14 @@ See examples #### Should - check for optimizations. - - divide by count happens often - - fillValue can be optimized (See #13) -- ```temp = sqrt(temp/(_count - 1));``` is this correct STDDEV? - - divide by count or count - 1? - - https://www.zaner.com/3.0/education/technicalstudies/MSD.asp - + - divide by count (-1) happens often. + - fillValue can be optimized (See #13). + #### Could - create a double based derived class? - Template class? - add error handling (important?). -- investigate **modus()** most frequently occurring value. - - difficult with floats ? - - what to do when on two or more values are on par? (no modus?) -- **int getUniqueValuesInBuffer()** O(n^2) - #### Wont @@ -198,6 +198,10 @@ See examples - clear(bool zero = true) to suppress setting all to 0. ? - makes **addValue()** slightly more complex - could introduce conflicts due to randomness data? +- investigate **modus()** most frequently occurring value. + - difficult with floats ? + - what to do when on two or more values are on par? (no modus?) +- **int getUniqueValuesInBuffer()** O(n^2). ## Support diff --git a/RunningAverage.cpp b/RunningAverage.cpp index 795dcb4..92afbc7 100644 --- a/RunningAverage.cpp +++ b/RunningAverage.cpp @@ -1,7 +1,7 @@ // // FILE: RunningAverage.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.4.6 +// VERSION: 0.4.7 // DATE: 2011-01-30 // PURPOSE: Arduino library to calculate the running average by means of a circular buffer // URL: https://github.com/RobTillaart/RunningAverage @@ -140,7 +140,7 @@ float RunningAverage::getMaxInBuffer() const // returns the value of an element if exist, NAN otherwise -float RunningAverage::getElement(uint16_t index) const +float RunningAverage::getElement(const uint16_t index) const { if ((_count == 0) || (_array == NULL)) { @@ -166,7 +166,9 @@ float RunningAverage::getStandardDeviation() const { temp += pow((_array[i] - average), 2); } - // TODO: when to divide by count || count-1? + // when to divide by count || count-1? + // - divide by count: whole set + // - divide by count - 1: sample of larger set (which run avg is) temp = sqrt(temp/(_count - 1)); return temp; // see issue #13 @@ -191,6 +193,19 @@ float RunningAverage::getStandardError() const } +// Return coefficient of variation. +// If buffer is empty or has only one element or zero average, return NAN. +float RunningAverage::getCoefficientOfVariation() const +{ + float temp = getStandardDeviation(); + if (temp == NAN) return NAN; + if (_sum == 0) return NAN; + + float cv = temp * _count / _sum; + return cv; +} + + // fill the average with the same value number times. (weight) // This is maximized to size times. // no need to fill the internal buffer over 100% @@ -256,7 +271,7 @@ bool RunningAverage::setPartial(const uint16_t partial) } -float RunningAverage::getAverageLast(uint16_t count) +float RunningAverage::getAverageLast(const uint16_t count) { uint16_t cnt = count; if (cnt > _count) cnt = _count; @@ -274,7 +289,7 @@ float RunningAverage::getAverageLast(uint16_t count) } -float RunningAverage::getStandardDeviationLast(uint16_t count) +float RunningAverage::getStandardDeviationLast(const uint16_t count) { uint16_t cnt = count; if (cnt > _count) cnt = _count; @@ -296,7 +311,7 @@ float RunningAverage::getStandardDeviationLast(uint16_t count) } -float RunningAverage::getMinInBufferLast(uint16_t count) +float RunningAverage::getMinInBufferLast(const uint16_t count) { uint16_t cnt = count; if (cnt > _count) cnt = _count; @@ -316,7 +331,7 @@ float RunningAverage::getMinInBufferLast(uint16_t count) } -float RunningAverage::getMaxInBufferLast(uint16_t count) +float RunningAverage::getMaxInBufferLast(const uint16_t count) { uint16_t cnt = count; if (cnt > _count) cnt = _count; @@ -336,7 +351,7 @@ float RunningAverage::getMaxInBufferLast(uint16_t count) } -float RunningAverage::getAverageSubset(uint16_t start, uint16_t count) +float RunningAverage::getAverageSubset(const uint16_t start, const uint16_t count) { if ((_count == 0) || (_array == NULL)) { diff --git a/RunningAverage.h b/RunningAverage.h index dd244ca..d9f11ff 100644 --- a/RunningAverage.h +++ b/RunningAverage.h @@ -2,7 +2,7 @@ // // FILE: RunningAverage.h // AUTHOR: Rob Tillaart -// VERSION: 0.4.6 +// VERSION: 0.4.7 // DATE: 2011-01-30 // PURPOSE: Arduino library to calculate the running average by means of a circular buffer // URL: https://github.com/RobTillaart/RunningAverage @@ -14,7 +14,7 @@ #include "Arduino.h" -#define RUNNINGAVERAGE_LIB_VERSION (F("0.4.6")) +#define RUNNINGAVERAGE_LIB_VERSION (F("0.4.7")) class RunningAverage @@ -36,6 +36,7 @@ class RunningAverage // return statistical characteristics of the running average float getStandardDeviation() const; float getStandardError() const; + float getCoefficientOfVariation() const; // returns min/max added to the data-set since last clear float getMin() const { return _min; }; @@ -49,7 +50,7 @@ class RunningAverage // return true if buffer is full bool bufferIsFull() const { return _count == _size; }; - float getElement(uint16_t index) const; + float getElement(const uint16_t index) const; uint16_t getSize() const { return _size; } uint16_t getCount() const { return _count; } @@ -61,13 +62,13 @@ class RunningAverage // get some stats from the last count additions. - float getAverageLast(uint16_t count); - float getStandardDeviationLast(uint16_t count); - float getMinInBufferLast(uint16_t count); - float getMaxInBufferLast(uint16_t count); + float getAverageLast(const uint16_t count); + float getStandardDeviationLast(const uint16_t count); + float getMinInBufferLast(const uint16_t count); + float getMaxInBufferLast(const uint16_t count); // Experimental 0.4.3 - float getAverageSubset(uint16_t start, uint16_t count); + float getAverageSubset(const uint16_t start, const uint16_t count); protected: diff --git a/examples/ra_getCoefficientOfVariation/ra_getCoefficientOfVariation.ino b/examples/ra_getCoefficientOfVariation/ra_getCoefficientOfVariation.ino new file mode 100644 index 0000000..533079d --- /dev/null +++ b/examples/ra_getCoefficientOfVariation/ra_getCoefficientOfVariation.ino @@ -0,0 +1,95 @@ +// +// FILE: ra_getCoefficientOfVariation.ino +// AUTHOR: Rob Tillaart +// PURPOSE: show working of runningAverage +// URL: https://github.com/RobTillaart/RunningAverage + + +#include "RunningAverage.h" + + +RunningAverage myRA(10); +int samples = 0; + + +RunningAverage A(60); +RunningAverage B(60); +RunningAverage C(60); + + +void setup(void) +{ + Serial.begin(115200); + Serial.println(); + Serial.println(__FILE__); + Serial.print("RUNNINGAVERAGE_LIB_VERSION: "); + Serial.println(RUNNINGAVERAGE_LIB_VERSION); + + myRA.clear(); // explicitly start clean + + for (int i = 0; i < 10; i++) + { + myRA.add(i * 0.01 + 1 ); + Serial.print(myRA.getCount()); + Serial.print("\t"); + Serial.print(myRA.getAverage(), 3); + Serial.print("\t"); + Serial.print(myRA.getStandardDeviation(), 3); + Serial.print("\t"); + Serial.print(myRA.getStandardDeviation() / myRA.getAverage(), 3); + Serial.print("\t"); + Serial.println(myRA.getCoefficientOfVariation(), 3); + } + Serial.println(); + + + Serial.println(); + Serial.println("Show effect of changing average and standard deviation."); + Serial.println("- A is reference"); + Serial.println("- B has same stdev, distribution is 'smaller' (sharper)"); + Serial.println("- C has same average, distribution is 'wider'"); + Serial.println(); + + A.clear(); + B.clear(); + C.clear(); + + for (int i = 0; i <= 50; i++) + { + A.add((i - 25) * 1.0 + 100); // reference + B.add((i - 25) * 1.0 + 200); // change average + C.add((i - 25) * 3.0 + 100); // change stddev + } + Serial.println("\tA\tB\tC"); + Serial.print("AVG\t"); + Serial.print(A.getAverage(), 3); + Serial.print("\t"); + Serial.print(B.getAverage(), 3); + Serial.print("\t"); + Serial.println(C.getAverage(), 3); + + Serial.print("STDEV\t"); + Serial.print(A.getStandardDeviation(), 3); + Serial.print("\t"); + Serial.print(B.getStandardDeviation(), 3); + Serial.print("\t"); + Serial.println(C.getStandardDeviation(), 3); + + Serial.print("COFVAR\t"); + Serial.print(A.getCoefficientOfVariation(), 3); + Serial.print("\t"); + Serial.print(B.getCoefficientOfVariation(), 3); + Serial.print("\t"); + Serial.println(C.getCoefficientOfVariation(), 3); + + + delay(10000); +} + + +void loop(void) +{ +} + + +// -- END OF FILE -- diff --git a/keywords.txt b/keywords.txt index f15bef1..8d71be0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -16,6 +16,7 @@ getAverage KEYWORD2 getFastAverage KEYWORD2 getStandardDeviation KEYWORD2 getStandardError KEYWORD2 +getCoefficientOfVariation KEYWORD2 getMin KEYWORD2 getMax KEYWORD2 @@ -23,7 +24,7 @@ getMinInBuffer KEYWORD2 getMaxInBuffer KEYWORD2 getSum KEYWORD2 -bufferIsFull() KEYWORD2 +bufferIsFull KEYWORD2 getElement KEYWORD2 getSize KEYWORD2 getCount KEYWORD2 diff --git a/library.json b/library.json index e01570e..2c03001 100644 --- a/library.json +++ b/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/RunningAverage.git" }, - "version": "0.4.6", + "version": "0.4.7", "license": "MIT", "frameworks": "*", "platforms": "*", diff --git a/library.properties b/library.properties index 64fc2f9..6683ffa 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RunningAverage -version=0.4.6 +version=0.4.7 author=Rob Tillaart maintainer=Rob Tillaart sentence=The library stores the last N individual values in a circular buffer to calculate the running average. diff --git a/performance.txt b/performance.txt deleted file mode 100644 index c725453..0000000 --- a/performance.txt +++ /dev/null @@ -1,28 +0,0 @@ - --------------------------------------- - -Arduino UNO -IDE 1.8.12 - -Performance RunningAverage lib: 0.3.0 - - clear : 80 - addValue : 24 - fillValue : 1548 - getValue : 4 - getAverage : 512 - getFastAverage : 36 - getStandardDeviation : 1832 - getStandardError : 1896 - getMin : 4 - getMax : 4 - getMinInBuffer : 212 - getMaxInBuffer : 204 - bufferIsFull : 8 - getElement : 4 - getSize : 8 - getCount : 8 - -done... - --------------------------------------- \ No newline at end of file