Skip to content

Commit b53b1ea

Browse files
authored
Fix #33, getCoefficientOfVariation (#35)
- Fix #33, add **float getCoefficientOfVariation()** - update readme.md - update keywords.txt
1 parent 7ab7146 commit b53b1ea

9 files changed

Lines changed: 154 additions & 61 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.4.7] - 2024-08-12
10+
- Fix #33, add **float getCoefficientOfVariation()**
11+
- update readme.md
12+
- update keywords.txt
13+
914
## [0.4.6] - 2024-06-15
1015
- Fix #30, add **float getSum()** (thanks to heidnerd)
1116
- Fix #31, add **float getStandardDeviationLast(uint16_t count)** (thanks to alvaro-oliver)

README.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ update the internal **\_sum**.
4141
- https://github.com/RobTillaart/RunningMedian
4242
- https://github.com/RobTillaart/statHelpers - combinations & permutations
4343
- https://github.com/RobTillaart/Statistic
44+
- https://github.com/RobTillaart/Student
4445

4546

4647
## Interface
@@ -83,8 +84,14 @@ Updates the variables used by **getFastAverage()** to improve its accuracy again
8384
- **float getStandardDeviation()** returns the standard deviation of the current content.
8485
Needs more than one element to be calculable.
8586
- **float getStandardError()** returns the standard error of the current content.
86-
- **float getMin()** returns minimum since last clear, does not need to be in the buffer any more.
87-
- **float getMax()** returns maximum since last clear, does not need to be in the buffer any more.
87+
- **float getCoefficientOfVariation()** returns coefficient of variation.
88+
This is defined as standardDeviation / Average.
89+
It indicates if the distribution is relative small ( < 1) or relative wide ( > 1).
90+
Note it has no meaning when the average is zero (or close to zero).
91+
- **float getMin()** returns minimum since last call to clear().
92+
The returned value does not need to be in the buffer any more.
93+
- **float getMax()** returns maximum since last call to clear().
94+
The returned value does not need to be in the buffer any more.
8895
- **float getMinInBuffer()** returns minimum in the internal buffer.
8996
- **float getMaxInBuffer()** returns maximum in the internal buffer.
9097
- **float getSum()** returns sum of values in the internal buffer.
@@ -94,7 +101,7 @@ Needs more than one element to be calculable.
94101

95102
- **bool bufferIsFull()** returns true if buffer is full.
96103
- **float getElement(uint16_t index)** get element directly from internal buffer at index. (debug)
97-
- **uint16_t getSize()** returns the size of the internal array.
104+
- **uint16_t getSize()** returns the size of the internal array as set in constructor.
98105
- **uint16_t getCount()** returns the number of slots used of the internal array.
99106

100107

@@ -114,6 +121,7 @@ Returns NAN if there are no elements and it will reduce count if there are less
114121
count elements in the buffer.
115122

116123
- **float getAverageLast(uint16_t count)** get the average of the last count elements.
124+
- **float getStandardDeviationLast(uint16_t count)** get the stddev of the last count elements.
117125
- **float getMinInBufferLast(uint16_t count)** get the minimum of the last count elements.
118126
- **float getMaxInBufferLast(uint16_t count)** get the maximum of the last count elements.
119127

@@ -174,22 +182,14 @@ See examples
174182
#### Should
175183

176184
- check for optimizations.
177-
- divide by count happens often
178-
- fillValue can be optimized (See #13)
179-
- ```temp = sqrt(temp/(_count - 1));``` is this correct STDDEV?
180-
- divide by count or count - 1?
181-
- https://www.zaner.com/3.0/education/technicalstudies/MSD.asp
182-
185+
- divide by count (-1) happens often.
186+
- fillValue can be optimized (See #13).
187+
183188
#### Could
184189

185190
- create a double based derived class?
186191
- Template class?
187192
- add error handling (important?).
188-
- investigate **modus()** most frequently occurring value.
189-
- difficult with floats ?
190-
- what to do when on two or more values are on par? (no modus?)
191-
- **int getUniqueValuesInBuffer()** O(n^2)
192-
193193

194194
#### Wont
195195

@@ -198,6 +198,10 @@ See examples
198198
- clear(bool zero = true) to suppress setting all to 0. ?
199199
- makes **addValue()** slightly more complex
200200
- could introduce conflicts due to randomness data?
201+
- investigate **modus()** most frequently occurring value.
202+
- difficult with floats ?
203+
- what to do when on two or more values are on par? (no modus?)
204+
- **int getUniqueValuesInBuffer()** O(n^2).
201205

202206

203207
## Support

RunningAverage.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// FILE: RunningAverage.cpp
33
// AUTHOR: Rob Tillaart
4-
// VERSION: 0.4.6
4+
// VERSION: 0.4.7
55
// DATE: 2011-01-30
66
// PURPOSE: Arduino library to calculate the running average by means of a circular buffer
77
// URL: https://github.com/RobTillaart/RunningAverage
@@ -140,7 +140,7 @@ float RunningAverage::getMaxInBuffer() const
140140

141141

142142
// returns the value of an element if exist, NAN otherwise
143-
float RunningAverage::getElement(uint16_t index) const
143+
float RunningAverage::getElement(const uint16_t index) const
144144
{
145145
if ((_count == 0) || (_array == NULL))
146146
{
@@ -166,7 +166,9 @@ float RunningAverage::getStandardDeviation() const
166166
{
167167
temp += pow((_array[i] - average), 2);
168168
}
169-
// TODO: when to divide by count || count-1?
169+
// when to divide by count || count-1?
170+
// - divide by count: whole set
171+
// - divide by count - 1: sample of larger set (which run avg is)
170172
temp = sqrt(temp/(_count - 1));
171173
return temp;
172174
// see issue #13
@@ -191,6 +193,19 @@ float RunningAverage::getStandardError() const
191193
}
192194

193195

196+
// Return coefficient of variation.
197+
// If buffer is empty or has only one element or zero average, return NAN.
198+
float RunningAverage::getCoefficientOfVariation() const
199+
{
200+
float temp = getStandardDeviation();
201+
if (temp == NAN) return NAN;
202+
if (_sum == 0) return NAN;
203+
204+
float cv = temp * _count / _sum;
205+
return cv;
206+
}
207+
208+
194209
// fill the average with the same value number times. (weight)
195210
// This is maximized to size times.
196211
// no need to fill the internal buffer over 100%
@@ -256,7 +271,7 @@ bool RunningAverage::setPartial(const uint16_t partial)
256271
}
257272

258273

259-
float RunningAverage::getAverageLast(uint16_t count)
274+
float RunningAverage::getAverageLast(const uint16_t count)
260275
{
261276
uint16_t cnt = count;
262277
if (cnt > _count) cnt = _count;
@@ -274,7 +289,7 @@ float RunningAverage::getAverageLast(uint16_t count)
274289
}
275290

276291

277-
float RunningAverage::getStandardDeviationLast(uint16_t count)
292+
float RunningAverage::getStandardDeviationLast(const uint16_t count)
278293
{
279294
uint16_t cnt = count;
280295
if (cnt > _count) cnt = _count;
@@ -296,7 +311,7 @@ float RunningAverage::getStandardDeviationLast(uint16_t count)
296311
}
297312

298313

299-
float RunningAverage::getMinInBufferLast(uint16_t count)
314+
float RunningAverage::getMinInBufferLast(const uint16_t count)
300315
{
301316
uint16_t cnt = count;
302317
if (cnt > _count) cnt = _count;
@@ -316,7 +331,7 @@ float RunningAverage::getMinInBufferLast(uint16_t count)
316331
}
317332

318333

319-
float RunningAverage::getMaxInBufferLast(uint16_t count)
334+
float RunningAverage::getMaxInBufferLast(const uint16_t count)
320335
{
321336
uint16_t cnt = count;
322337
if (cnt > _count) cnt = _count;
@@ -336,7 +351,7 @@ float RunningAverage::getMaxInBufferLast(uint16_t count)
336351
}
337352

338353

339-
float RunningAverage::getAverageSubset(uint16_t start, uint16_t count)
354+
float RunningAverage::getAverageSubset(const uint16_t start, const uint16_t count)
340355
{
341356
if ((_count == 0) || (_array == NULL))
342357
{

RunningAverage.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// FILE: RunningAverage.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.4.6
5+
// VERSION: 0.4.7
66
// DATE: 2011-01-30
77
// PURPOSE: Arduino library to calculate the running average by means of a circular buffer
88
// URL: https://github.com/RobTillaart/RunningAverage
@@ -14,7 +14,7 @@
1414
#include "Arduino.h"
1515

1616

17-
#define RUNNINGAVERAGE_LIB_VERSION (F("0.4.6"))
17+
#define RUNNINGAVERAGE_LIB_VERSION (F("0.4.7"))
1818

1919

2020
class RunningAverage
@@ -36,6 +36,7 @@ class RunningAverage
3636
// return statistical characteristics of the running average
3737
float getStandardDeviation() const;
3838
float getStandardError() const;
39+
float getCoefficientOfVariation() const;
3940

4041
// returns min/max added to the data-set since last clear
4142
float getMin() const { return _min; };
@@ -49,7 +50,7 @@ class RunningAverage
4950
// return true if buffer is full
5051
bool bufferIsFull() const { return _count == _size; };
5152

52-
float getElement(uint16_t index) const;
53+
float getElement(const uint16_t index) const;
5354

5455
uint16_t getSize() const { return _size; }
5556
uint16_t getCount() const { return _count; }
@@ -61,13 +62,13 @@ class RunningAverage
6162

6263

6364
// get some stats from the last count additions.
64-
float getAverageLast(uint16_t count);
65-
float getStandardDeviationLast(uint16_t count);
66-
float getMinInBufferLast(uint16_t count);
67-
float getMaxInBufferLast(uint16_t count);
65+
float getAverageLast(const uint16_t count);
66+
float getStandardDeviationLast(const uint16_t count);
67+
float getMinInBufferLast(const uint16_t count);
68+
float getMaxInBufferLast(const uint16_t count);
6869

6970
// Experimental 0.4.3
70-
float getAverageSubset(uint16_t start, uint16_t count);
71+
float getAverageSubset(const uint16_t start, const uint16_t count);
7172

7273

7374
protected:
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// FILE: ra_getCoefficientOfVariation.ino
3+
// AUTHOR: Rob Tillaart
4+
// PURPOSE: show working of runningAverage
5+
// URL: https://github.com/RobTillaart/RunningAverage
6+
7+
8+
#include "RunningAverage.h"
9+
10+
11+
RunningAverage myRA(10);
12+
int samples = 0;
13+
14+
15+
RunningAverage A(60);
16+
RunningAverage B(60);
17+
RunningAverage C(60);
18+
19+
20+
void setup(void)
21+
{
22+
Serial.begin(115200);
23+
Serial.println();
24+
Serial.println(__FILE__);
25+
Serial.print("RUNNINGAVERAGE_LIB_VERSION: ");
26+
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
27+
28+
myRA.clear(); // explicitly start clean
29+
30+
for (int i = 0; i < 10; i++)
31+
{
32+
myRA.add(i * 0.01 + 1 );
33+
Serial.print(myRA.getCount());
34+
Serial.print("\t");
35+
Serial.print(myRA.getAverage(), 3);
36+
Serial.print("\t");
37+
Serial.print(myRA.getStandardDeviation(), 3);
38+
Serial.print("\t");
39+
Serial.print(myRA.getStandardDeviation() / myRA.getAverage(), 3);
40+
Serial.print("\t");
41+
Serial.println(myRA.getCoefficientOfVariation(), 3);
42+
}
43+
Serial.println();
44+
45+
46+
Serial.println();
47+
Serial.println("Show effect of changing average and standard deviation.");
48+
Serial.println("- A is reference");
49+
Serial.println("- B has same stdev, distribution is 'smaller' (sharper)");
50+
Serial.println("- C has same average, distribution is 'wider'");
51+
Serial.println();
52+
53+
A.clear();
54+
B.clear();
55+
C.clear();
56+
57+
for (int i = 0; i <= 50; i++)
58+
{
59+
A.add((i - 25) * 1.0 + 100); // reference
60+
B.add((i - 25) * 1.0 + 200); // change average
61+
C.add((i - 25) * 3.0 + 100); // change stddev
62+
}
63+
Serial.println("\tA\tB\tC");
64+
Serial.print("AVG\t");
65+
Serial.print(A.getAverage(), 3);
66+
Serial.print("\t");
67+
Serial.print(B.getAverage(), 3);
68+
Serial.print("\t");
69+
Serial.println(C.getAverage(), 3);
70+
71+
Serial.print("STDEV\t");
72+
Serial.print(A.getStandardDeviation(), 3);
73+
Serial.print("\t");
74+
Serial.print(B.getStandardDeviation(), 3);
75+
Serial.print("\t");
76+
Serial.println(C.getStandardDeviation(), 3);
77+
78+
Serial.print("COFVAR\t");
79+
Serial.print(A.getCoefficientOfVariation(), 3);
80+
Serial.print("\t");
81+
Serial.print(B.getCoefficientOfVariation(), 3);
82+
Serial.print("\t");
83+
Serial.println(C.getCoefficientOfVariation(), 3);
84+
85+
86+
delay(10000);
87+
}
88+
89+
90+
void loop(void)
91+
{
92+
}
93+
94+
95+
// -- END OF FILE --

keywords.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ getAverage KEYWORD2
1616
getFastAverage KEYWORD2
1717
getStandardDeviation KEYWORD2
1818
getStandardError KEYWORD2
19+
getCoefficientOfVariation KEYWORD2
1920

2021
getMin KEYWORD2
2122
getMax KEYWORD2
2223
getMinInBuffer KEYWORD2
2324
getMaxInBuffer KEYWORD2
2425
getSum KEYWORD2
2526

26-
bufferIsFull() KEYWORD2
27+
bufferIsFull KEYWORD2
2728
getElement KEYWORD2
2829
getSize KEYWORD2
2930
getCount KEYWORD2

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"type": "git",
1616
"url": "https://github.com/RobTillaart/RunningAverage.git"
1717
},
18-
"version": "0.4.6",
18+
"version": "0.4.7",
1919
"license": "MIT",
2020
"frameworks": "*",
2121
"platforms": "*",

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=RunningAverage
2-
version=0.4.6
2+
version=0.4.7
33
author=Rob Tillaart <rob.tillaart@gmail.com>
44
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
55
sentence=The library stores the last N individual values in a circular buffer to calculate the running average.

performance.txt

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)