Templated class for calculating averages and statistics of data sets. Original code is here: https://github.com/MajenkoLibraries/Average

Average.h

Committer:
smartsystemdesign
Date:
2016-10-18
Revision:
1:eb5616b13885
Parent:
0:f44fc7f90e31

File content as of revision 1:eb5616b13885:

/*
 * Copyright (c) , Majenko Technologies
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 *  1. Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 * 
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 * 
 *  3. Neither the name of Majenko Technologies nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without 
 *     specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#ifndef _AVERAGE_H
#define _AVERAGE_H

inline static float sqr(float x) {
    return x*x;
}

template <class T> class Average {
    private:
        // Private functions and variables here.  They can only be accessed
        // by functions within the class.
        T *_store;
        T _sum;                                               // _sum variable for faster mean calculation
        uint32_t _position;                                   // _position variable for circular buffer
        uint32_t _count;
        uint32_t _size;

    public:
        // Public functions and variables.  These can be accessed from
        // outside the class.
        Average(uint32_t size);
        ~Average();
        float rolling(T entry);
        void push(T entry);
        float mean();
        T mode();
        T minimum();
        T minimum(int *);
        T maximum();
        T maximum(int *);
        float stddev();
        T get(uint32_t);
        void leastSquares(float &m, float &b, float &r);
        int getCount();
        T predict(int x);
        T sum();
        void clear();
        Average<T> &operator=(Average<T> &a);

};

template <class T> int Average<T>::getCount() {
    return _count;
}

template <class T> Average<T>::Average(uint32_t size) {
    _size = size;
    _count = 0;
    _store = (T *)malloc(sizeof(T) * size);
    _position = 0;                                            // track position for circular storage
    _sum = 0;                                                 // track sum for fast mean calculation
    for (uint32_t i = 0; i < size; i++) {
        _store[i] = 0;
    }
}

template <class T> Average<T>::~Average() {
    free(_store);
}

template <class T> void Average<T>::push(T entry) {
    if (_count < _size) {                                     // adding new values to array
        _count++;                                             // count number of values in array
    } else {                                                    // overwriting old values
        _sum = _sum -_store[_position];                       // remove old value from _sum
    }
    _store[_position] = entry;                                // store new value in array
    _sum += entry;                                            // add the new value to _sum
    _position += 1;                                           // increment the position counter
    if (_position >= _size) _position = 0;                    // loop the position counter
}


template <class T> float Average<T>::rolling(T entry) {
    push(entry);
    return mean();
}

template <class T> float Average<T>::mean() {
    if (_count == 0) {
        return 0;
    }
    return ((float)_sum / (float)_count);                     // mean calculation based on _sum
}

template <class T> T Average<T>::mode() {
    uint32_t pos;
    uint32_t inner;
    T most;
    uint32_t mostcount;
    T current;
    uint32_t currentcount;

    if (_count == 0) {
        return 0;
    }

    most = get(0);
    mostcount = 1;
    for(pos = 0; pos < _count; pos++) {
        current = get(pos);
        currentcount = 1;
        for(inner = pos + 1; inner < _count; inner++) {
            if(get(inner) == current) {
                currentcount++;
            }
        }
        if(currentcount > mostcount) {
            most = current;
            mostcount = currentcount;
        }
        // If we have less array slices left than the current
        // maximum count, then there is no room left to find
        // a bigger count.  We have finished early and we can
        // go home.
        if(_count - pos < mostcount) {
            break;
        }
    }
    return most;
}

template <class T> T Average<T>::minimum() {
    return minimum(NULL);
}

template <class T> T Average<T>::minimum(int *index) {
    T minval;

    if (index != NULL) {
        *index = 0;
    }

    if (_count == 0) {
        return 0;
    }

    minval = get(0);

    for(uint32_t i = 0; i < _count; i++) {
        if(get(i) < minval) {
            minval = get(i);
            if (index != NULL) { 
                *index = i;
            }
        }
    }
    return minval;
}

template <class T> T Average<T>::maximum() {
    return maximum(NULL);
}

template <class T> T Average<T>::maximum(int *index) {
    T maxval;

    if (index != NULL) {
        *index = 0;
    }

    if (_count == 0) {
        return 0;
    }

    maxval = get(0);

    for(uint32_t i = 0; i < _count; i++) {
        if(get(i) > maxval) {
            maxval = get(i);
            if (index != NULL) { 
                *index = i;
            }
        }
    }
    return maxval;
}

template <class T> float Average<T>::stddev() {
    float square;
    float sum;
    float mu;
    float theta;
    int i;

    if (_count == 0) {
        return 0;
    }

    mu = mean();

    sum = 0;
    for(uint32_t i = 0; i < _count; i++) {
        theta = mu - (float)get(i);
        square = theta * theta;
        sum += square;
    }
    return sqrt(sum/(float)_count);
}

template <class T> T Average<T>::get(uint32_t index) {
    if (index >= _count) {
        return -1;
    }

    int32_t start = _position - _count;
    if (start < 0) start += _size;
    int32_t cindex = start + index;
    if (cindex >= _size) cindex -= _size;
    return _store[cindex];
}

template <class T> void Average<T>::leastSquares(float &m, float &c, float &r) {
    float   sumx = 0.0;                        /* sum of x                      */
    float   sumx2 = 0.0;                       /* sum of x**2                   */
    float   sumxy = 0.0;                       /* sum of x * y                  */
    float   sumy = 0.0;                        /* sum of y                      */
    float   sumy2 = 0.0;                       /* sum of y**2                   */

    for (uint32_t i=0;i<_count;i++)   { 
        sumx  += i;
        sumx2 += sqr(i);  
        sumxy += i * get(i);
        sumy  += get(i);      
        sumy2 += sqr(get(i)); 
    } 

    float denom = (_count * sumx2 - sqr(sumx));
    if (denom == 0) {
        // singular matrix. can't solve the problem.
        m = 0;
        c = 0;
        r = 0;
        return;
    }

    m = 0 - (_count * sumxy  -  sumx * sumy) / denom;
    c = (sumy * sumx2  -  sumx * sumxy) / denom;
    r = (sumxy - sumx * sumy / _count) / sqrt((sumx2 - sqr(sumx)/_count) * (sumy2 - sqr(sumy)/_count));
}

template <class T> T Average<T>::predict(int x) {
    float m, c, r;
    leastSquares(m, c, r); // y = mx + c;

    T y = m * x + c;
    return y;
}

// Return the sum of all the array items
template <class T> T Average<T>::sum() {
    return _sum;
}

template <class T> void Average<T>::clear() {
    _count = 0;
    _sum = 0;
    _position = 0;
}

template <class T> Average<T> &Average<T>::operator=(Average<T> &a) {
    clear();
    for (int i = 0; i < _size; i++) {
        push(a.get(i));
    }
    return *this;
}

#endif