Signature | Description |
---|---|
enum class decompose_type : unsigned char { additive = 1, // Yt = Trend + Seasonal + Residual multiplicative = 2, // Yt = Trend * Seasonal * Residual }; |
The additive decomposition is the most appropriate if the magnitude of the seasonal fluctuations, or the variation around the trend-cycle, does not vary with the level of the time series. When the variation in the seasonal pattern, or the variation around the trend-cycle, appears to be proportional to the level of the time series, then a multiplicative decomposition is more appropriate. Multiplicative decompositions are common with economic time series. An alternative to using a multiplicative decomposition is to first transform the data until the variation in the series appears to be stable over time, then use an additive decomposition. When a log transformation has been used, this is equivalent to using a multiplicative decomposition because: Yt = T * S * R is equivalent to log(Yt) = log(T) + logt(S) + log(R) |
Signature | Description | Parameters |
---|---|---|
#include <DataFrame/DataFrameStatsVisitors.h> template<typename T, typename I = unsigned long> struct DecomposeVisitor; // ------------------------------------- template<typename T, typename I = unsigned long> using decom_v = DecomposeVisitor<T, I>; |
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 creates a seasonal-trend (with Lowess, aka "STL") decomposition of observed time series data. This implementation is modeled after the statsmodels.tsa.seasonal_decompose method but substitutes a Lowess regression for a convolution in its trend estimation. get_result() returns the Trend vector. There are 3 get result methods: get_trend(), get_seasonal(), get_residual(). They return the corresponding vectors explicit DecomposeVisitor(std::size_t s_period, T frac, T delta, decompose_type type = decompose_type::additive);s_period: Length of a season in untis of one observation. There must be at least two seasons in the data frac: Between 0 and 1. The fraction of the data used when estimating each y-value. delta: Distance within which to use linear-interpolation instead of weighted regression. type: Model type. See above. |
T: Column data type. I: Index type. |
static void test_DecomposeVisitor() { std::cout << "\nTesting DecomposeVisitor{ } ..." << std::endl; std::vector<double> y_vec = { 131.157, 131.367, 132.215, 132.725, 132.648, 130.585, 130.701, 129.631, 129.168, 129.554, 129.467, 129.670, 128.397, 129.014, 129.496, 131.067, 130.219, 128.947, 129.602, 128.118, 127.356, 127.231, 127.154, 128.417, 129.091, 129.082, 128.937, 130.441, 129.371, 129.294, 129.381, 129.564, 129.708, 130.701, 130.663, 130.113, 130.046, 130.393, 128.026, 129.204, 130.530, 129.499, 129.266, 129.357, 130.431, 131.810, 131.761, 131.675, 130.923, 131.694, 133.005, 133.323, 134.152, 138.702, 137.719, 135.492, 133.622, 134.518, 132.725, 131.839, 138.548, 140.996, 143.734, 150.693, 151.108, 149.423, 150.416, 149.491, 151.273, 150.299, 146.783, 147.173, 146.939, 147.290, 145.946, 142.624, 138.027, 136.118, 129.650, 126.767, 130.809, 125.550, 130.732, 126.183, 124.410, 114.748, 121.527, 114.904, 100.138, 105.144, }; MyDataFrame df; df.load_data(std::move(MyDataFrame::gen_sequence_index(0, y_vec.size(), 1)), std::make_pair("IBM_closes", y_vec)); DecomposeVisitor<double> d_v (7); df.single_act_visit<double>("IBM_closes", d_v); auto actual_trends = std::vector<double> { 130.613, 130.55, 130.489, 130.43, 130.372, 130.317, 130.263, 130.211, 130.161, 130.111, 130.064, 130.017, 129.972, 129.928, 129.885, 129.842, 129.801, 129.76, 129.72, 129.681, 129.642, 129.603, 129.564, 129.526, 129.49, 129.458, 129.435, 129.459, 129.496, 129.546, 129.61, 129.688, 129.78, 129.885, 130.002, 130.129, 130.267, 130.414, 130.568, 130.73, 130.898, 131.071, 131.248, 131.429, 131.613, 131.801, 131.994, 132.193, 132.4, 132.618, 132.847, 133.089, 133.343, 133.594, 133.814, 133.958, 133.982, 133.867, 133.633, 133.329, 133.013, 132.715, 132.449, 132.214, 131.88, 131.594, 131.336, 131.094, 130.862, 130.636, 130.412, 130.191, 129.97, 129.749, 129.527, 129.305, 129.082, 128.858, 128.633, 128.406, 128.178, 127.949, 127.717, 127.484, 127.249, 127.012, 126.773, 126.531, 126.288, 126.043 }; for (size_t idx = 0; idx < actual_trends.size(); ++idx) assert(fabs(d_v.get_trend()[idx] - actual_trends[idx]) < 0.001); auto actual_seasonals = std::vector<double> { 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695, 0.951993, 0.499135, -0.362488, -0.0226401, -0.138991, -0.774313, -0.152695 }; for (size_t idx = 0; idx < actual_seasonals.size(); ++idx) assert(fabs(d_v.get_seasonal()[idx] - actual_seasonals[idx]) < 0.00001); auto actual_residuals = std::vector<double> { 0.0450645, 1.17948, 1.74866, 2.43421, 3.04989, 0.420796, -0.514129, -1.07918, -0.630027, -0.534809, -0.457752, 0.427013, -1.42233, -1.86582, -0.887751, 1.58716, 0.440736, -0.674248, 0.656095, -1.41002, -3.2376, -2.87093, -2.04782, -1.08684, -0.260342, 0.397977, -0.345524, 0.0298508, -0.623855, 0.110697, -0.206247, 0.014974, 0.702412, 0.968872, -0.290624, -0.515527, 0.141332, 0.0017837, -2.40348, -0.751754, -0.215182, -2.52399, -2.48155, -1.7098, -1.15976, 0.147774, 0.541487, -0.36517, -2.4292, -1.42281, 0.520609, 0.256637, 0.94848, 5.88205, 4.05781, 0.58219, -0.858726, 1.01365, -0.88524, -1.35148, 6.30971, 8.4335, 10.3326, 17.98, 19.5905, 17.8516, 19.2189, 19.171, 20.5634, 18.7112, 15.8714, 17.3448, 16.992, 17.6804, 17.1932, 13.4718, 7.99313, 6.76106, 1.3799, -1.61643, 2.7699, -1.62422, 3.16745, -2.25312, -3.33817, -11.9014, -5.22295, -11.4883, -25.3757, -20.7463 }; for (size_t idx = 0; idx < actual_residuals.size(); ++idx) assert(fabs(d_v.get_residual()[idx] - actual_residuals[idx]) < 0.0001); }
// ----------------------------------------------------------------------------- static void test_IBM_data() { std::cout << "\nTesting IBM_data( ) ..." << std::endl; typedef StdDataFrame<std::string> StrDataFrame; StrDataFrame df; df.read("IBM.csv", io_format::csv2); DecomposeVisitor<double, std::string> d_v(178, 2.0 / 3.0, 0, decompose_type::multiplicative); // decompose_type::additive); df.single_act_visit<double>("IBM_Adj_Close", d_v); const auto &ibm_closes = df.get_column<double>("IBM_Adj_Close"); for (std::size_t i = 0; i < ibm_closes.size(); ++i) std::cout << ibm_closes[i] << "," << d_v.get_trend()[i] << "," << d_v.get_seasonal()[i] << "," << d_v.get_residual()[i] << std::endl; }