Signature Description

enum class exponential_decay_spec : unsigned char  {
    center_of_gravity = 1, // Decay = 1 / (1 + value)   For value >= 0
    span = 2,              // Decay = 2 / (1 + value)   For value >= 1
    halflife = 3,          // Decay = 1 - elog(0.5)/value   For value > 0
    fixed = 4,             // Decay = value   For 0 < value <= 1
};
This spec determines how an exponentially moving stat decays. Based on this spec, the value parameter is converted to decay.

Signature Description Parameters
#include <DataFrame/DataFrameStatsVisitors.h>

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
struct ExponentiallyWeightedMeanVisitor;

// -------------------------------------

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
using ewm_v = ExponentiallyWeightedMeanVisitor<T, I, A>;
        
This is a “single action visitor”, meaning it is passed the whole data vector in one call and you must use the single_act_visit() interface.

This visitor calculates exponentially weighted mean (EWM). EWM gives more weight to recent data by using a decay factor.
Formula if finite_adjust is false (default):
    Yt = decay * Xt + (1 − decay) * Yt-1
		
Formula if finite_adjust is true:
          Xt + (1 - decay) * Xt-1 + (1 - decay)2 * Xt-2 + ... + (1 - decay)t * X0
    Yt = ------------------------------------------------------------------------
                  1 + (1 - decay) + (1 - decay)2 + ... + (1 - decay)t
		
Constructor:
    ExponentiallyWeightedMeanVisitor(
        exponential_decay_spec eds,  // See exponential_decay_spec type
        double value,                // Value to be decayed
        bool finite_adjust = false); // Adjust for the fact that this is not an infinite data set
        
T: Column data type
I: Index type
A: Memory alignment boundary for vectors. Default is system default alignment

Signature Description Parameters
#include <DataFrame/DataFrameStatsVisitors.h>

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
struct ExponentiallyWeightedVarVisitor;

// -------------------------------------

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
using ewm_var_v = ExponentiallyWeightedVarVisitor<T, I, A>;
        
This is a “single action visitor”, meaning it is passed the whole data vector in one call and you must use the single_act_visit() interface.

This visitor calculates exponentially weighted variance and standard deviation. It gives more weight to recent data by using a decay factor. get_result() returns the vector of EWM variances. There is also a get_std() that returns the vector of EWM standard deviations.

Constructor:
    ExponentiallyWeightedVarVisitor(
        exponential_decay_spec eds,  // See exponential_decay_spec type
        double value);               // Value to be decayed
        
T: Column data type
I: Index type
A: Memory alignment boundary for vectors. Default is system default alignment

Signature Description Parameters
#include <DataFrame/DataFrameStatsVisitors.h>

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
struct ExponentiallyWeightedCovVisitor;

// -------------------------------------

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
using ewm_cov_v = ExponentiallyWeightedCovVisitor<T, I, A>;
        
This is a “single action visitor”, meaning it is passed the whole data vector in one call and you must use the single_act_visit() interface.

This visitor calculates exponentially weighted covariance. It gives more weight to recent data by using a decay factor. get_result() returns the vector of EWM covariances. Constructor:
    ExponentiallyWeightedCovVisitor(
        exponential_decay_spec eds,  // See exponential_decay_spec type
        double value);               // Value to be decayed
        
T: Column data type
I: Index type
A: Memory alignment boundary for vectors. Default is system default alignment

Signature Description Parameters
#include <DataFrame/DataFrameStatsVisitors.h>

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
struct ExponentiallyWeightedCorrVisitor;

// -------------------------------------

template<typename T, typename I = unsigned long,
         std::size_t A = 0>
using ewm_corr_v = ExponentiallyWeightedCorrVisitor<T, I, A>;
        
This is a “single action visitor”, meaning it is passed the whole data vector in one call and you must use the single_act_visit() interface.

This visitor calculates exponentially weighted correlation. It gives more weight to recent data by using a decay factor. get_result() returns the vector of EWM correlations. Constructor:
    ExponentiallyWeightedCorrVisitor(
        exponential_decay_spec eds,  // See exponential_decay_spec type
        double value);               // Value to be decayed
        
T: Column data type
I: Index type
A: Memory alignment boundary for vectors. Default is system default alignment

static void test_ExponentiallyWeightedMeanVisitor()  {

    std::cout << "\nTesting ExponentiallyWeightedMeanVisitor{ } ..."
              << std::endl;

    std::vector<unsigned long>  idx =
        { 123450, 123451, 123452, 123453, 123454, 123455, 123456, 123457, 123458, 123459, 123460 };
    std::vector<double> d1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    std::vector<double> d2 = { 8, 9, 10, 11,
                               std::numeric_limits<double>::quiet_NaN(),
                               13, 14,
                               std::numeric_limits<double>::quiet_NaN(),
                               16, 17, 18 };
    std::vector<double> d3 = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
    std::vector<double> d4 = { 22, 23, 24, 25, 26, 27 };
    std::vector<std::string> s1 = { "11", "22", "33", "aa", "bb", "cc", "dd" "tt", "uu", "ii", "88" };
    MyDataFrame         df;

    df.load_data(std::move(idx),
                 std::make_pair("col_1", d1),
                 std::make_pair("col_2", d2),
                 std::make_pair("col_3", d3),
                 std::make_pair("col_str", s1));
    df.load_column("col_4", std::move(d4), nan_policy::dont_pad_with_nans);

    ewm_v<double>   hl_expo_mean_roller(exponential_decay_spec::halflife, 0.5);
    const auto      &hl_expo_result = df.single_act_visit<double>("col_3", hl_expo_mean_roller).get_result();

    assert(hl_expo_result.size() == 11);
    assert(hl_expo_result[0] == 15.0);
    assert(fabs(hl_expo_result[1] - 15.75) < 0.01);
    assert(fabs(hl_expo_result[2] - 16.6875) < 0.0001);
    assert(fabs(hl_expo_result[5] - 19.667) < 0.001);
    assert(fabs(hl_expo_result[8] - 22.6667) < 0.0001);

    ewm_v<double>   cg_expo_mean_roller(exponential_decay_spec::center_of_gravity, 0.5);
    const auto      &cg_expo_result = df.single_act_visit<double>("col_3", cg_expo_mean_roller).get_result();

    assert(cg_expo_result.size() == 11);
    assert(cg_expo_result[0] == 15.0);
    assert(fabs(cg_expo_result[1] - 15.6667) < 0.0001);
    assert(fabs(cg_expo_result[2] - 16.5556) < 0.0001);
    assert(fabs(cg_expo_result[5] - 19.5021) < 0.0001);
    assert(fabs(cg_expo_result[8] - 22.5001) < 0.0001);

    ewm_v<double>   s_expo_mean_roller(exponential_decay_spec::span, 1.5);
    const auto      &s_expo_result = df.single_act_visit<double>("col_3", s_expo_mean_roller).get_result();

    assert(s_expo_result.size() == 11);
    assert(s_expo_result[0] == 15.0);
    assert(fabs(s_expo_result[1] - 15.8) < 0.01);
    assert(fabs(s_expo_result[2] - 16.76) < 0.001);
    assert(fabs(s_expo_result[5] - 19.7501) < 0.0001);
    assert(fabs(s_expo_result[8] - 22.75) < 0.001);

    ewm_v<double>   f_expo_mean_roller(exponential_decay_spec::fixed, 0.5);
    const auto      &f_expo_result = df.single_act_visit<double>("col_3", f_expo_mean_roller).get_result();

    assert(f_expo_result.size() == 11);
    assert(f_expo_result[0] == 15.0);
    assert(fabs(f_expo_result[1] - 15.5) < 0.01);
    assert(fabs(f_expo_result[2] - 16.25) < 0.001);
    assert(fabs(f_expo_result[5] - 19.0312) < 0.0001);
    assert(fabs(f_expo_result[8] - 22.0039) < 0.0001);

    ewm_v<double>   expo_mean_roller_3(exponential_decay_spec::span, 3);
    const auto      &expo_result_3 = df.single_act_visit<double>("col_3", expo_mean_roller_3).get_result();

    assert(expo_result_3.size() == 11);
    assert(expo_result_3[0] == 15.0);
    assert(std::fabs(expo_result_3[1] - 15.5) < 0.01);
    assert(std::fabs(expo_result_3[2] - 16.25) < 0.001);
    assert(std::fabs(expo_result_3[5] - 19.0312) < 0.0001);
    assert(std::fabs(expo_result_3[8] - 22.0039) < 0.0001);

    ewm_v<double>   expo_mean_roller_3_t(exponential_decay_spec::span, 3, true);
    const auto      &expo_result_3_t = df.single_act_visit<double>("col_3", expo_mean_roller_3_t).get_result();

    assert(expo_result_3_t.size() == 11);
    assert(expo_result_3_t[0] == 15.0);
    assert(std::fabs(expo_result_3_t[1] - 15.6667) < 0.0001);
    assert(std::fabs(expo_result_3_t[2] - 16.4286) < 0.0001);
    assert(std::fabs(expo_result_3_t[5] - 19.0952) < 0.0001);
    assert(std::fabs(expo_result_3_t[8] - 22.0176) < 0.0001);
}
// -----------------------------------------------------------------------------

static void test_ExponentiallyWeightedVarVisitor()  {

    std::cout << "\nTesting ExponentiallyWeightedVarVisitor{  } ..."
              << std::endl;

    StlVecType<unsigned long>  idx =
        { 123450, 123451, 123452, 123453, 123454, 123455, 123456,
          123457, 123458, 123459, 123460, 123461, 123462, 123466, 123467, 123468 };
    StlVecType<double>         d1 =
        { 1.0, 1.5, 1.0, 1.2, 1.7, 1.5, 1.2, 1.7, 1.7, 1.3, 1.4, 1.5, 1.2, 1.1, 1.15, 1.0 };
    StlVecType<double>         d2 =
        { 0.2, 0.58, -0.60, -0.08, 0.05, 0.87, 0.2, 0.4, 0.5, 0.06, 0.3, -0.34,
          -0.9, 0.8, -0.4, 0.86 };
    StlVecType<int>            i1 = { 22, 23, 24, 25, 99 };
    MyDataFrame                df;

    df.load_data(std::move(idx),
                 std::make_pair("values", d1),
                 std::make_pair("benchmark", d2),
                 std::make_pair("col_3", i1));

    ewm_var_v<double>   ewmvar(exponential_decay_spec::span, 3);
    const auto          result = df.single_act_visit<double>("values", ewmvar).get_result();

    assert(result.size() == 16);
    assert(std::isnan(result[0]));
    assert(fabs(result[1] - 0.125) < 0.001);
    assert(fabs(result[2] - 0.0893) < 0.0001);
    assert(fabs(result[3] - 0.0396) < 0.0001);
    assert(fabs(result[4] - 0.1258) < 0.0001);
    assert(fabs(result[15] - 0.0228) < 0.0001);
    assert(fabs(result[14] - 0.0205) < 0.0001);
    assert(fabs(result[13] - 0.0377) < 0.0001);
}

// -----------------------------------------------------------------------------

static void test_ExponentiallyWeightedCovVisitor()  {

    std::cout << "\nTesting ExponentiallyWeightedCovVisitor{  } ..."
              << std::endl;

    StlVecType<unsigned long>  idx =
        { 123450, 123451, 123452, 123453, 123454, 123455, 123456,
          123457, 123458, 123459, 123460, 123461, 123462, 123466, 123467, 123468 };
    StlVecType<double>         d1 =
        { 1.0, 1.5, 1.0, 1.2, 1.7, 1.5, 1.2, 1.7, 1.7, 1.3, 1.4, 1.5, 1.2, 1.1, 1.15, 1.0 };
    StlVecType<double>         d2 =
        { 1.5, 1.0, 1.1, 1.3, 1.35, 1.2, 1.2, 1.6, 1.6, 1.8, 1.4, 1.5, 1.25, 1.3, 1.25, 1.0 };
    StlVecType<int>            i1 = { 22, 23, 24, 25, 99 };
    MyDataFrame                df;

    df.load_data(std::move(idx),
                 std::make_pair("X Column", d1),
                 std::make_pair("Y Column", d2),
                 std::make_pair("col_3", i1));

    ewm_cov_v<double>   ewmcov(exponential_decay_spec::span, 3);
    const auto          &result = df.single_act_visit<double, double>("X Column", "Y Column", ewmcov).get_result();

    assert(result.size() == 16);
    assert(std::isnan(result[0]));
    assert(fabs(result[1] - -0.125) < 0.001);
    assert(fabs(result[2] - -0.0321) < 0.0001);
    assert(fabs(result[3] - -0.0099) < 0.0001);
    assert(fabs(result[4] - 0.0219) < 0.0001);
    assert(fabs(result[15] - 0.0263) < 0.0001);
    assert(fabs(result[14] - 0.0121) < 0.0001);
    assert(fabs(result[13] - 0.0197) < 0.0001);
}

// -----------------------------------------------------------------------------

static void test_ExponentiallyWeightedCorrVisitor()  {

    std::cout << "\nTesting ExponentiallyWeightedCorrVisitor{  } ..."
              << std::endl;

    StlVecType<unsigned long>  idx =
        { 123450, 123451, 123452, 123453, 123454, 123455, 123456,
          123457, 123458, 123459, 123460, 123461, 123462, 123466, 123467, 123468 };
    StlVecType<double>         d1 =
        { 1.0, 1.5, 1.0, 1.2, 1.7, 1.5, 1.2, 1.7, 1.7, 1.3, 1.4, 1.5, 1.2, 1.1, 1.15, 1.0 };
    StlVecType<double>         d2 =
        { 1.5, 1.0, 1.1, 1.3, 1.35, 1.2, 1.2, 1.6, 1.6, 1.8, 1.4, 1.5, 1.25, 1.3, 1.25, 1.0 };
    StlVecType<int>            i1 = { 22, 23, 24, 25, 99 };
    MyDataFrame                df;

    df.load_data(std::move(idx),
                 std::make_pair("X Column", d1),
                 std::make_pair("Y Column", d2),
                 std::make_pair("col_3", i1));

    ewm_corr_v<double>  ewmcorr(exponential_decay_spec::span, 3);
    const auto          &result = df.single_act_visit<double, double>("X Column", "Y Column", ewmcorr).get_result();

    assert(result.size() == 16);
    assert(std::isnan(result[0]));
    assert(fabs(result[1] - -1.0) < 0.001);
    assert(fabs(result[2] - -0.5153) < 0.0001);
    assert(fabs(result[3] - -0.2841) < 0.0001);
    assert(fabs(result[4] - 0.4287) < 0.0001);
    assert(fabs(result[15] - 0.8746) < 0.0001);
    assert(fabs(result[14] - 0.714) < 0.0001);
    assert(fabs(result[13] - 0.6865) < 0.0001);
}
C++ DataFrame