CorrelationFunctions.hh 13.2 KB
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   CorrelationFunctions.hh
 * Author: hacene
 *
 * Created on September 27, 2021, 3:35 PM
 */

#ifndef CORRELATIONFUNCTIONS_HH
#define CORRELATIONFUNCTIONS_HH

#include "DicError.hh"
#include "AMDA_exception.hh"
#include "Parameter.hh"
#include "ParamData.hh"
#include "DataTypeMath.hh"
#include "Operation.hh"
#include <vector>
#include <iostream>
#include <iterator>
#include <c++/4.8.2/bits/stl_vector.h>
#include <c++/4.8.2/bits/stl_pair.h>
#include "Toolbox.hh"
#include "AbstractFunc.hh"

namespace AMDA {
    namespace Parameters {
        namespace StatisticFunctions {

#define AVERAGE_TIME 1200 // (seconds)
#define MAX_GAP_SIZE 3600 // (seconds)

            enum COEFS {
                COVARIANCE = 1,
                PAERSON = 2,
                SPEARMAN = 3,
                KENDALL = 4,
            };
            static std::map<std::string, COEFS> coefsToStr = {
                {"covariance", COEFS::COVARIANCE},
                {"pearson", COEFS::PAERSON},
                {"spearman", COEFS::SPEARMAN},
                {"kendall", COEFS::KENDALL},
                {"1", COEFS::COVARIANCE},
                {"2", COEFS::PAERSON},
                {"3", COEFS::SPEARMAN},
                {"4", COEFS::KENDALL},
            };

            template <typename InputElemType, typename OutputElemType>
            class AbstractCorrelationFunc : public AbstractFuncBase {
            public:

                /**
                 * @brief Constructor.
                 * @details Create the ParamData type of the input ParamData.
                 */
                AbstractCorrelationFunc(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList, ParamDataSpec<InputElemType>& firstParamInput, ParamDataSpec<InputElemType>& secondParamInput, double windowtime)
                : AbstractFuncBase(pProcess, pTimeIntervalList, windowtime),
                _firstParamInput(firstParamInput),
                _secondParamInput(secondParamInput),
                _paramOutput(new ParamDataSpec<OutputElemType>) {
                    _paramDataOutput = _paramOutput;
                }

                virtual ~AbstractCorrelationFunc() {
                }

                virtual void pushData(double time, std::pair<InputElemType, InputElemType>& elem) = 0;

                virtual OutputElemType compute() = 0;

                void pushSecondParamData(ParamDataIndexInfo &pParamDataIndexInfo) {
                    for (unsigned int _index = pParamDataIndexInfo._startIndex;
                            _index < pParamDataIndexInfo._startIndex + pParamDataIndexInfo._nbDataToProcess;
                            ++_index) {
                        double time = _secondParamInput.getTime(_index);
                        InputElemType val_ = _secondParamInput.get(_index);
                        _secondParamInputData.push_back(std::pair<double, InputElemType> (time, val_));
                    }
                }

                virtual InputElemType getValue(std::vector<std::pair<double, InputElemType> >& input, double time) = 0;

                /**
                 * @overload Operation::write(ParamDataIndexInfo &pParamDataIndexInfo)
                 */

                void write(ParamDataIndexInfo &pParamDataIndexInfo) {

                    if ((pParamDataIndexInfo._nbDataToProcess > 0)) {
                        if (pParamDataIndexInfo._startIndex == 0) {
                            _nanVal = _firstParamInput.get(0);
                            _nanVal << NotANumber();
                        }
                        for (unsigned int _index = pParamDataIndexInfo._startIndex;
                                _index < pParamDataIndexInfo._startIndex + pParamDataIndexInfo._nbDataToProcess;
                                ++_index) {
                            double crtTime = _firstParamInput.getTime(_index);
                            InputElemType firstVal = _firstParamInput.get(_index);
                            // get the second element 
                            InputElemType secondVal = getValue(_secondParamInputData, crtTime);
                            std::pair<InputElemType, InputElemType> crtVal(firstVal, secondVal);

                            if (needToChangeTarget(crtTime)) {
                                _paramOutput->pushTime(getTarget());
                                _paramOutput->push(compute());
                                pushData(crtTime, crtVal);
                                nextTarget();
                                bool skip = false;
                                while (!skip && needToChangeTarget(crtTime)) {
                                    _paramOutput->pushTime(getTarget());
                                    _paramOutput->push(compute());
                                    skip = nextTarget();
                                }
                            } else {
                                pushData(crtTime, crtVal);
                                if (needInit()) {
                                    init();
                                }
                            }
                        }
                    }
                    if (pParamDataIndexInfo._timeIntToProcessChanged || pParamDataIndexInfo._noMoreTimeInt) {
                        if (!needInit()) {
                            do {
                                if (inInt(getTarget())) {
                                    _paramOutput->pushTime(getTarget());
                                    _paramOutput->push(compute());
                                }
                            } while (nextTarget());
                        }
                    }

                }

                double getInputParamSampling() {
                    return _firstParamInput.getMinSampling();
                }

            private:
                ParamDataSpec<InputElemType>& _firstParamInput;

                ParamDataSpec<InputElemType>& _secondParamInput;

                ParamDataSpec<OutputElemType>* _paramOutput;

                std::vector<std::pair<double, InputElemType> > _secondParamInputData;

            protected:
                OutputElemType _nanVal;
            };

            /**
             * 
             * @param pProcess
             * @param pTimeIntervalList
             * @param firstParamInput
             * @param secondParamInput
             * @param windowtime
             * @param type
             */
            template <typename InputElemType, typename OutputElemType>
            class Correlation : public AbstractCorrelationFunc<InputElemType, OutputElemType> {
            public:

                Correlation(Process & pProcess, TimeIntervalListSPtr pTimeIntervalList, ParamDataSpec<InputElemType>& firstParamInput,
                        ParamDataSpec<InputElemType>& secondParamInput, double windowtime, std::string correlationType) :
                AbstractCorrelationFunc<InputElemType, OutputElemType> (pProcess, pTimeIntervalList, firstParamInput, secondParamInput, windowtime), _correlationType(correlationType) {

                }

                virtual ~Correlation() {
                }

                virtual void init() {
                    AbstractCorrelationFunc<InputElemType, OutputElemType>::setTarget(AbstractCorrelationFunc<InputElemType, OutputElemType>::getIntStartTime());
                    AbstractCorrelationFunc<InputElemType, OutputElemType>::setNeedInit(false);
                }

                virtual bool nextTarget() {
                    double target = AbstractCorrelationFunc<InputElemType, OutputElemType>::getTarget() + AbstractCorrelationFunc<InputElemType, OutputElemType>::getWindowTime();
                    bool res = AbstractCorrelationFunc<InputElemType, OutputElemType>::setTarget(target);
                    while (!_mem.empty() && !AbstractCorrelationFunc<InputElemType, OutputElemType>::inWindow(_mem.front().first)) {
                        _mem.pop_front();
                    }
                    return res;
                }

                virtual bool needToChangeTarget(double crtTime) {
                    return !AbstractCorrelationFunc<InputElemType, OutputElemType>::needInit() && !AbstractCorrelationFunc<InputElemType, OutputElemType>::inWindow(crtTime);
                }

                virtual double getSampling() {
                    return AbstractCorrelationFunc<InputElemType, OutputElemType>::getWindowTime();
                }

                virtual void pushData(double time, std::pair<InputElemType, InputElemType>& elem) {
                    _mem.push_back(std::make_pair(time, elem));
                }

                virtual void resetFunc() {
                    _mem.clear();
                }

                InputElemType getValue(std::vector<std::pair<double, InputElemType> >& input, double time) {
                    double min_t = time - AVERAGE_TIME / 2.;
                    double max_t = time + AVERAGE_TIME / 2.;
                    std::vector<std::pair<double, InputElemType> > values_for_mean;
                    InputElemType nanVal;
                    nanVal << NotANumber();
                    std::pair<double, InputElemType> prev_value(NAN, nanVal);
                    std::pair<double, InputElemType> next_value(NAN, nanVal);
                    InputElemType value = nanVal;
                    for (auto it = input.begin(); it != input.end(); ++it) {
                        if (it->first == time) {
                            value = it->second;
                            return value;
                            break;
                        } else if (isNAN(it->second))
                            continue;
                        else if (it->first > max_t) {
                            next_value = *it;
                            break;
                        } else if (it->first < min_t) {
                            prev_value = *it;
                        } else {
                            values_for_mean.push_back(*it);
                        }
                    }
                    if (!values_for_mean.empty()) {
                        //Compute mean
                        InputElemType sum = 0;
                        for (auto it = values_for_mean.begin(); it != values_for_mean.end(); ++it) {
                            sum += it->second;
                        }
                        value = sum / (InputElemType) values_for_mean.size();
                    } else {
                        if (!isNAN(prev_value.first) && !isNAN(next_value.first) && (next_value.first - prev_value.first <= MAX_GAP_SIZE)) {
                            //Compute interpolated value
                            value = prev_value.second + (time - prev_value.first) / (next_value.first - prev_value.first) * (next_value.second - prev_value.second);
                        }
                    }

                    return value;
                }
                
                                OutputElemType compute() {
                    return computeCorrelation(_mem, AbstractCorrelationFunc<InputElemType, OutputElemType>::_nanVal, _correlationType);
                }

                OutputElemType computeCorrelation(std::list<std::pair<double, std::pair<InputElemType, InputElemType>>>&mem, OutputElemType& nanVal, std::string type) {
                    OutputElemType result = nanVal;
                    if (mem.empty()) {
                        return result;
                    }
                    std::list<std::pair<InputElemType, InputElemType>> list;
                    for (typename std::list<std::pair<double, std::pair < InputElemType, InputElemType>>>::iterator it = mem.begin(); it != mem.end(); ++it) {
                        list.push_back(it->second);
                    }
                    if (coefsToStr.find(type) == coefsToStr.end()) {
                        BOOST_THROW_EXCEPTION(AMDA::AMDA_exception() << AMDA::errno_code(AMDA_ERROR_UNKNOWN) << AMDA::ex_msg("StatisticFunctions::CorrelationFunction  unknown correlation type " + type));
                    }

                    switch (coefsToStr[type]) {
                        case COEFS::COVARIANCE:
                            getCovariance(list, result);
                            break;
                        case COEFS::PAERSON:
                            getPearson(list, result);
                            break;
                        case COEFS::SPEARMAN:
                            getSpearman(list, result);
                            break;
                        case COEFS::KENDALL:
                            getKendall(list, result);
                            break;
                        default:
                            BOOST_THROW_EXCEPTION(AMDA::AMDA_exception() << AMDA::errno_code(AMDA_ERROR_UNKNOWN) << AMDA::ex_msg("StatisticFunctions::CorrelationFunction  unknown correlation type :" + type));
                    }

                    return result;
                }
                
            protected:
                std::string _correlationType;
                std::list<std::pair<double, std::pair<InputElemType, InputElemType>> > _mem;
            };
        }
    }
}


#endif /* CORRELATIONFUNCTIONS_HH */