Chris@102: /////////////////////////////////////////////////////////////////////////////// Chris@102: // rolling_variance.hpp Chris@102: // Copyright (C) 2005 Eric Niebler Chris@102: // Copyright (C) 2014 Pieter Bastiaan Ober (Integricom). Chris@102: // Distributed under the Boost Software License, Version 1.0. Chris@102: // (See accompanying file LICENSE_1_0.txt or copy at Chris@102: // http://www.boost.org/LICENSE_1_0.txt) Chris@102: Chris@102: #ifndef BOOST_ACCUMULATORS_STATISTICS_ROLLING_VARIANCE_HPP_EAN_15_11_2011 Chris@102: #define BOOST_ACCUMULATORS_STATISTICS_ROLLING_VARIANCE_HPP_EAN_15_11_2011 Chris@102: Chris@102: #include Chris@102: #include Chris@102: Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: Chris@102: #include Chris@102: #include Chris@102: Chris@102: namespace boost { namespace accumulators Chris@102: { Chris@102: namespace impl Chris@102: { Chris@102: //! Immediate (lazy) calculation of the rolling variance. Chris@102: /*! Chris@102: Calculation of sample variance \f$\sigma_n^2\f$ is done as follows, see also Chris@102: http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance. Chris@102: For a rolling window of size \f$N\f$, when \f$n <= N\f$, the variance is computed according to the formula Chris@102: \f[ Chris@102: \sigma_n^2 = \frac{1}{n-1} \sum_{i = 1}^n (x_i - \mu_n)^2. Chris@102: \f] Chris@102: When \f$n > N\f$, the sample variance over the window becomes: Chris@102: \f[ Chris@102: \sigma_n^2 = \frac{1}{N-1} \sum_{i = n-N+1}^n (x_i - \mu_n)^2. Chris@102: \f] Chris@102: */ Chris@102: /////////////////////////////////////////////////////////////////////////////// Chris@102: // lazy_rolling_variance_impl Chris@102: // Chris@102: template Chris@102: struct lazy_rolling_variance_impl Chris@102: : accumulator_base Chris@102: { Chris@102: // for boost::result_of Chris@102: typedef typename numeric::functional::fdiv::result_type result_type; Chris@102: Chris@102: lazy_rolling_variance_impl(dont_care) {} Chris@102: Chris@102: template Chris@102: result_type result(Args const &args) const Chris@102: { Chris@102: result_type mean = rolling_mean(args); Chris@102: size_t nr_samples = rolling_count(args); Chris@102: if (nr_samples < 2) return result_type(); Chris@102: return nr_samples*(rolling_moment<2>(args) - mean*mean)/(nr_samples-1); Chris@102: } Chris@102: }; Chris@102: Chris@102: //! Iterative calculation of the rolling variance. Chris@102: /*! Chris@102: Iterative calculation of sample variance \f$\sigma_n^2\f$ is done as follows, see also Chris@102: http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance. Chris@102: For a rolling window of size \f$N\f$, for the first \f$N\f$ samples, the variance is computed according to the formula Chris@102: \f[ Chris@102: \sigma_n^2 = \frac{1}{n-1} \sum_{i = 1}^n (x_i - \mu_n)^2 = \frac{1}{n-1}M_{2,n}, Chris@102: \f] Chris@102: where the sum of squares \f$M_{2,n}\f$ can be recursively computed as: Chris@102: \f[ Chris@102: M_{2,n} = \sum_{i = 1}^n (x_i - \mu_n)^2 = M_{2,n-1} + (x_n - \mu_n)(x_n - \mu_{n-1}), Chris@102: \f] Chris@102: and the estimate of the sample mean as: Chris@102: \f[ Chris@102: \mu_n = \frac{1}{n} \sum_{i = 1}^n x_i = \mu_{n-1} + \frac{1}{n}(x_n - \mu_{n-1}). Chris@102: \f] Chris@102: For further samples, when the rolling window is fully filled with data, one has to take into account that the oldest Chris@102: sample \f$x_{n-N}\f$ is dropped from the window. The sample variance over the window now becomes: Chris@102: \f[ Chris@102: \sigma_n^2 = \frac{1}{N-1} \sum_{i = n-N+1}^n (x_i - \mu_n)^2 = \frac{1}{n-1}M_{2,n}, Chris@102: \f] Chris@102: where the sum of squares \f$M_{2,n}\f$ now equals: Chris@102: \f[ Chris@102: M_{2,n} = \sum_{i = n-N+1}^n (x_i - \mu_n)^2 = M_{2,n-1} + (x_n - \mu_n)(x_n - \mu_{n-1}) - (x_{n-N} - \mu_n)(x_{n-N} - \mu_{n-1}), Chris@102: \f] Chris@102: and the estimated mean is: Chris@102: \f[ Chris@102: \mu_n = \frac{1}{N} \sum_{i = n-N+1}^n x_i = \mu_{n-1} + \frac{1}{n}(x_n - x_{n-N}). Chris@102: \f] Chris@102: Chris@102: Note that the sample variance is not defined for \f$n <= 1\f$. Chris@102: Chris@102: */ Chris@102: /////////////////////////////////////////////////////////////////////////////// Chris@102: // immediate_rolling_variance_impl Chris@102: // Chris@102: template Chris@102: struct immediate_rolling_variance_impl Chris@102: : accumulator_base Chris@102: { Chris@102: // for boost::result_of Chris@102: typedef typename numeric::functional::fdiv::result_type result_type; Chris@102: Chris@102: template Chris@102: immediate_rolling_variance_impl(Args const &args) Chris@102: : previous_mean_(numeric::fdiv(args[sample | Sample()], numeric::one::value)) Chris@102: , sum_of_squares_(numeric::fdiv(args[sample | Sample()], numeric::one::value)) Chris@102: { Chris@102: } Chris@102: Chris@102: template Chris@102: void operator()(Args const &args) Chris@102: { Chris@102: Sample added_sample = args[sample]; Chris@102: Chris@102: result_type mean = immediate_rolling_mean(args); Chris@102: sum_of_squares_ += (added_sample-mean)*(added_sample-previous_mean_); Chris@102: Chris@102: if(is_rolling_window_plus1_full(args)) Chris@102: { Chris@102: Sample removed_sample = rolling_window_plus1(args).front(); Chris@102: sum_of_squares_ -= (removed_sample-mean)*(removed_sample-previous_mean_); Chris@102: prevent_underflow(sum_of_squares_); Chris@102: } Chris@102: previous_mean_ = mean; Chris@102: } Chris@102: Chris@102: template Chris@102: result_type result(Args const &args) const Chris@102: { Chris@102: size_t nr_samples = rolling_count(args); Chris@102: if (nr_samples < 2) return result_type(); Chris@102: return numeric::fdiv(sum_of_squares_,(nr_samples-1)); Chris@102: } Chris@102: Chris@102: private: Chris@102: Chris@102: result_type previous_mean_; Chris@102: result_type sum_of_squares_; Chris@102: Chris@102: template Chris@102: void prevent_underflow(T &non_negative_number,typename boost::enable_if,T>::type* = 0) Chris@102: { Chris@102: if (non_negative_number < T(0)) non_negative_number = T(0); Chris@102: } Chris@102: template Chris@102: void prevent_underflow(T &non_arithmetic_quantity,typename boost::disable_if,T>::type* = 0) Chris@102: { Chris@102: } Chris@102: }; Chris@102: } // namespace impl Chris@102: Chris@102: /////////////////////////////////////////////////////////////////////////////// Chris@102: // tag:: lazy_rolling_variance Chris@102: // tag:: immediate_rolling_variance Chris@102: // tag:: rolling_variance Chris@102: // Chris@102: namespace tag Chris@102: { Chris@102: struct lazy_rolling_variance Chris@102: : depends_on< rolling_count, rolling_mean, rolling_moment<2> > Chris@102: { Chris@102: /// INTERNAL ONLY Chris@102: /// Chris@102: typedef accumulators::impl::lazy_rolling_variance_impl< mpl::_1 > impl; Chris@102: Chris@102: #ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED Chris@102: /// tag::rolling_window::window_size named parameter Chris@102: static boost::parameter::keyword const window_size; Chris@102: #endif Chris@102: }; Chris@102: Chris@102: struct immediate_rolling_variance Chris@102: : depends_on< rolling_window_plus1, rolling_count, immediate_rolling_mean> Chris@102: { Chris@102: /// INTERNAL ONLY Chris@102: /// Chris@102: typedef accumulators::impl::immediate_rolling_variance_impl< mpl::_1> impl; Chris@102: Chris@102: #ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED Chris@102: /// tag::rolling_window::window_size named parameter Chris@102: static boost::parameter::keyword const window_size; Chris@102: #endif Chris@102: }; Chris@102: Chris@102: // make immediate_rolling_variance the default implementation Chris@102: struct rolling_variance : immediate_rolling_variance {}; Chris@102: } // namespace tag Chris@102: Chris@102: /////////////////////////////////////////////////////////////////////////////// Chris@102: // extract::lazy_rolling_variance Chris@102: // extract::immediate_rolling_variance Chris@102: // extract::rolling_variance Chris@102: // Chris@102: namespace extract Chris@102: { Chris@102: extractor const lazy_rolling_variance = {}; Chris@102: extractor const immediate_rolling_variance = {}; Chris@102: extractor const rolling_variance = {}; Chris@102: Chris@102: BOOST_ACCUMULATORS_IGNORE_GLOBAL(lazy_rolling_variance) Chris@102: BOOST_ACCUMULATORS_IGNORE_GLOBAL(immediate_rolling_variance) Chris@102: BOOST_ACCUMULATORS_IGNORE_GLOBAL(rolling_variance) Chris@102: } Chris@102: Chris@102: using extract::lazy_rolling_variance; Chris@102: using extract::immediate_rolling_variance; Chris@102: using extract::rolling_variance; Chris@102: Chris@102: // rolling_variance(lazy) -> lazy_rolling_variance Chris@102: template<> Chris@102: struct as_feature Chris@102: { Chris@102: typedef tag::lazy_rolling_variance type; Chris@102: }; Chris@102: Chris@102: // rolling_variance(immediate) -> immediate_rolling_variance Chris@102: template<> Chris@102: struct as_feature Chris@102: { Chris@102: typedef tag::immediate_rolling_variance type; Chris@102: }; Chris@102: Chris@102: // for the purposes of feature-based dependency resolution, Chris@102: // lazy_rolling_variance provides the same feature as rolling_variance Chris@102: template<> Chris@102: struct feature_of Chris@102: : feature_of Chris@102: { Chris@102: }; Chris@102: Chris@102: // for the purposes of feature-based dependency resolution, Chris@102: // immediate_rolling_variance provides the same feature as rolling_variance Chris@102: template<> Chris@102: struct feature_of Chris@102: : feature_of Chris@102: { Chris@102: }; Chris@102: }} // namespace boost::accumulators Chris@102: Chris@102: #endif