2013-09-29 13:28:34 -04:00
|
|
|
//
|
2017-07-16 02:04:20 -04:00
|
|
|
// FILE: Histogram.cpp
|
2013-09-29 15:55:02 -04:00
|
|
|
// AUTHOR: Rob Tillaart
|
2021-11-04 07:32:04 -04:00
|
|
|
// VERSION: 0.3.0
|
2013-09-29 13:28:34 -04:00
|
|
|
// PURPOSE: Histogram library for Arduino
|
2013-09-29 15:55:02 -04:00
|
|
|
// DATE: 2012-11-10
|
|
|
|
//
|
2021-01-29 06:31:58 -05:00
|
|
|
// HISTORY:
|
|
|
|
// 0.1.0 2012-11-10 initial version
|
|
|
|
// 0.1.1 2012-11-10 added PMF() and CDF()
|
|
|
|
// 0.1.2 2012-12-23 changed float to double; some comments
|
|
|
|
// 0.1.3 2013-09-29 testing a lot & refactoring
|
|
|
|
// 0.1.4 2015-03-06 stricter interface
|
2021-11-04 07:32:04 -04:00
|
|
|
// 0.1.5 2017-07-16 refactor, support for > 256 buckets; prevent alloc() errors
|
2021-01-29 06:31:58 -05:00
|
|
|
// 0.1.6 2017-07-27 revert double to float (issue #33)
|
|
|
|
// 0.2.0 2020-06-12 #pragma once, removed pre 1.0 support
|
2021-11-04 07:32:04 -04:00
|
|
|
// 0.2.1 2020-12-24 Arduino-CI + unit tests
|
|
|
|
// 0.3.0 2021-11-02 update build-CI, add badges
|
|
|
|
// refactor readability
|
|
|
|
// add parameter for clear(value = 0)
|
|
|
|
// add findMin(), findMax()
|
|
|
|
// add countAbove(), countLevel(), countBelow().
|
|
|
|
// add setBucket(),
|
|
|
|
// change length to uint16_t ==> 65534
|
2013-09-29 13:28:34 -04:00
|
|
|
|
|
|
|
#include "histogram.h"
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
|
|
|
Histogram::Histogram(const uint16_t length, float *bounds)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2017-07-16 02:04:20 -04:00
|
|
|
_bounds = bounds;
|
2021-11-04 07:32:04 -04:00
|
|
|
_length = length + 1;
|
|
|
|
_data = (int32_t *) malloc((_length) * sizeof(int32_t));
|
2017-07-16 02:04:20 -04:00
|
|
|
if (_data) clear();
|
2021-11-04 07:32:04 -04:00
|
|
|
else _length = 0;
|
|
|
|
_count = 0;
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:28:34 -04:00
|
|
|
Histogram::~Histogram()
|
|
|
|
{
|
2017-07-16 02:04:20 -04:00
|
|
|
if (_data) free(_data);
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
|
|
|
// resets all counters to value (default 0)
|
|
|
|
void Histogram::clear(int32_t value)
|
2013-09-29 15:55:02 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
for (uint16_t i = 0; i < _length; i++) _data[i] = value;
|
|
|
|
_count = 0;
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:28:34 -04:00
|
|
|
// adds a new value to the histogram - increasing
|
2021-11-04 07:32:04 -04:00
|
|
|
void Histogram::add(const float value)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if (_length > 0)
|
2017-07-16 02:04:20 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
uint16_t index = find(value);
|
|
|
|
_data[index]++;
|
|
|
|
_count++;
|
2017-07-16 02:04:20 -04:00
|
|
|
}
|
2021-11-04 07:32:04 -04:00
|
|
|
// could return index or count.
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:28:34 -04:00
|
|
|
// adds a new value to the histogram - decreasing
|
2021-11-04 07:32:04 -04:00
|
|
|
void Histogram::sub(const float value)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if (_length > 0)
|
2017-07-16 02:04:20 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
uint16_t index = find(value);
|
|
|
|
_data[index]--;
|
|
|
|
_count++;
|
2017-07-16 02:04:20 -04:00
|
|
|
}
|
2021-11-04 07:32:04 -04:00
|
|
|
// could return index or count.
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:32:20 -04:00
|
|
|
// returns the count of a bucket
|
2021-11-04 07:32:04 -04:00
|
|
|
int32_t Histogram::bucket(const uint16_t index)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if (index > _length) return 0;
|
|
|
|
return _data[index];
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:28:34 -04:00
|
|
|
// returns the relative frequency of a bucket
|
2021-11-04 07:32:04 -04:00
|
|
|
float Histogram::frequency(const uint16_t index)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if ((_count == 0) || (_length == 0)) return NAN;
|
2017-07-27 07:13:41 -04:00
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
if (index > _length) return 0; // differs from PMF()
|
|
|
|
return (1.0 * _data[index]) / _count;
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 15:55:02 -04:00
|
|
|
// EXPERIMENTAL
|
2013-09-29 13:32:20 -04:00
|
|
|
// returns the probability of the bucket of a value
|
2021-11-04 07:32:04 -04:00
|
|
|
float Histogram::PMF(const float value)
|
2013-09-29 13:32:20 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if ((_count == 0) || (_length == 0)) return NAN;
|
2017-07-27 07:13:41 -04:00
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
uint16_t index = find(value);
|
|
|
|
return (1.0 * _data[index]) / _count;
|
2013-09-29 13:32:20 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 15:55:02 -04:00
|
|
|
// EXPERIMENTAL
|
2021-11-04 07:32:04 -04:00
|
|
|
// returns the cumulative probability of
|
2013-09-29 13:32:20 -04:00
|
|
|
// values <= value
|
2021-11-04 07:32:04 -04:00
|
|
|
float Histogram::CDF(const float value)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if ((_count == 0) || (_length == 0)) return NAN;
|
2017-07-27 07:13:41 -04:00
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
// TODO: could be done in one loop?
|
|
|
|
uint16_t index = find(value);
|
|
|
|
int32_t sum = 0;
|
|
|
|
for (uint16_t i = 0; i <= index; i++)
|
2017-07-16 02:04:20 -04:00
|
|
|
{
|
|
|
|
sum += _data[i];
|
|
|
|
}
|
2021-11-04 07:32:04 -04:00
|
|
|
return (1.0 * sum) / _count;
|
2013-09-29 13:32:20 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
2013-09-29 13:32:20 -04:00
|
|
|
// EXPERIMENTAL
|
2013-09-29 15:55:02 -04:00
|
|
|
// returns the value of the original array for
|
2021-11-04 07:32:04 -04:00
|
|
|
// which the CDF is at least prob(ability).
|
2017-07-27 07:13:41 -04:00
|
|
|
float Histogram::VAL(const float prob)
|
2013-09-29 13:32:20 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if ((_count == 0) || (_length == 0)) return NAN;
|
|
|
|
|
2017-07-27 07:13:41 -04:00
|
|
|
float p = prob;
|
2017-07-16 02:04:20 -04:00
|
|
|
if (p < 0.0) p = 0.0;
|
|
|
|
if (p > 1.0) p = 1.0;
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
float probability = p * _count;
|
2017-07-16 02:04:20 -04:00
|
|
|
int32_t sum = 0;
|
2021-11-04 07:32:04 -04:00
|
|
|
for (uint16_t i = 0; i < _length; i++)
|
2017-07-16 02:04:20 -04:00
|
|
|
{
|
|
|
|
sum += _data[i];
|
2021-11-04 07:32:04 -04:00
|
|
|
if (sum >= probability && (i < (_length - 1)) )
|
|
|
|
{
|
|
|
|
return _bounds[i];
|
|
|
|
}
|
2017-07-16 02:04:20 -04:00
|
|
|
}
|
|
|
|
return INFINITY;
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
|
|
|
// returns the bucket number for value
|
|
|
|
// - binary search, more memory ; faster for #buckets > 20 ?
|
|
|
|
// uint16_t Histogram::find(const float value)
|
|
|
|
// {
|
|
|
|
// if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
// uint16_t low = 0, high = _length;
|
|
|
|
// uint16_t mid;
|
|
|
|
// while (high - low > 1)
|
|
|
|
// {
|
|
|
|
// mid = (low + high)/2;
|
|
|
|
// if (_bounds[mid] > value)
|
|
|
|
// {
|
|
|
|
// high = mid;
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// low = mid;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// if (_bounds[mid] > value) return mid;
|
|
|
|
// return _length - 1;
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// returns the bucket number for value
|
|
|
|
uint16_t Histogram::find(const float value)
|
|
|
|
{
|
|
|
|
if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
for (uint16_t i = 0; i < (_length - 1); i++)
|
|
|
|
{
|
|
|
|
if (_bounds[i] >= value)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the (first) index of the bucket with minimum value.
|
|
|
|
uint16_t Histogram::findMin()
|
|
|
|
{
|
|
|
|
if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
uint16_t index = 0;
|
|
|
|
for (uint16_t i = 1; i < _length; i++)
|
|
|
|
{
|
|
|
|
if (_data[i] < _data[index]) index = i;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the (first) index of the bucket with maximum value.
|
|
|
|
uint16_t Histogram::findMax()
|
|
|
|
{
|
|
|
|
if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
uint16_t index = 0;
|
|
|
|
for (uint16_t i = 1; i < _length; i++)
|
|
|
|
{
|
|
|
|
if (_data[i] > _data[index]) index = i;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the number of buckets above a certain level.
|
|
|
|
uint16_t Histogram::countLevel(const int32_t level)
|
|
|
|
{
|
|
|
|
if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
uint16_t buckets = 0;
|
|
|
|
for (uint16_t i = 0; i < _length; i++)
|
|
|
|
{
|
|
|
|
if (_data[i] == level) buckets++;
|
|
|
|
}
|
|
|
|
return buckets;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the number of buckets above a certain level.
|
|
|
|
uint16_t Histogram::countAbove(const int32_t level)
|
2013-09-29 13:28:34 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if (_length <= 0) return -1;
|
2017-07-27 07:13:41 -04:00
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
uint16_t buckets = 0;
|
|
|
|
for (uint16_t i = 0; i < _length; i++)
|
2017-07-16 02:04:20 -04:00
|
|
|
{
|
2021-11-04 07:32:04 -04:00
|
|
|
if (_data[i] > level) buckets++;
|
2017-07-16 02:04:20 -04:00
|
|
|
}
|
2021-11-04 07:32:04 -04:00
|
|
|
return buckets;
|
2013-09-29 13:28:34 -04:00
|
|
|
}
|
|
|
|
|
2021-11-04 07:32:04 -04:00
|
|
|
|
|
|
|
// returns the number of buckets below a certain level.
|
|
|
|
uint16_t Histogram::countBelow(const int32_t level)
|
|
|
|
{
|
|
|
|
if (_length <= 0) return -1;
|
|
|
|
|
|
|
|
uint16_t buckets = 0;
|
|
|
|
for (uint16_t i = 0; i < _length; i++)
|
|
|
|
{
|
|
|
|
if (_data[i] < level) buckets++;
|
|
|
|
}
|
|
|
|
return buckets;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// DERIVED CLASS
|
|
|
|
//
|
|
|
|
|
|
|
|
Histogram16::Histogram16(const uint16_t length, float *bounds) : Histogram(length, bounds)
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Histogram8::Histogram8(const uint16_t length, float *bounds) : Histogram(length, bounds)
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
2020-11-27 05:16:22 -05:00
|
|
|
// -- END OF FILE --
|
2021-11-04 07:32:04 -04:00
|
|
|
|