2 * Copyright Nick Thompson, 2019
3 * Use, modification and distribution are subject to the
4 * Boost Software License, Version 1.0. (See accompanying file
5 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 #ifndef BOOST_MATH_STATISTICS_RUNS_TEST_HPP
9 #define BOOST_MATH_STATISTICS_RUNS_TEST_HPP
14 #include <boost/math/statistics/univariate_statistics.hpp>
15 #include <boost/math/distributions/normal.hpp>
17 namespace boost::math::statistics {
19 template<class RandomAccessContainer>
20 auto runs_above_and_below_threshold(RandomAccessContainer const & v,
21 typename RandomAccessContainer::value_type threshold)
23 using Real = typename RandomAccessContainer::value_type;
28 throw std::domain_error("At least 2 samples are required to get number of runs.");
30 typedef boost::math::policies::policy<
31 boost::math::policies::promote_float<false>,
32 boost::math::policies::promote_double<false> >
35 decltype(v.size()) nabove = 0;
36 decltype(v.size()) nbelow = 0;
38 decltype(v.size()) imin = 0;
40 // Take care of the case that v[0] == threshold:
41 while (imin < v.size() && v[imin] == threshold) {
45 // Take care of the constant vector case:
46 if (imin == v.size()) {
47 return std::make_pair(std::numeric_limits<Real>::quiet_NaN(), Real(0));
50 bool run_up = (v[imin] > threshold);
56 decltype(v.size()) runs = 1;
57 for (decltype(v.size()) i = imin + 1; i < v.size(); ++i) {
58 if (v[i] == threshold) {
59 // skip values precisely equal to threshold (following R's randtests package)
62 bool above = (v[i] > threshold);
68 if (run_up == above) {
77 // If you make n an int, the subtraction is gonna be bad in the variance:
78 Real n = nabove + nbelow;
80 Real expected_runs = Real(1) + Real(2*nabove*nbelow)/Real(n);
81 Real variance = 2*nabove*nbelow*(2*nabove*nbelow-n)/Real(n*n*(n-1));
83 // Bizarre, pathological limits:
86 if (runs == expected_runs)
90 return std::make_pair(statistic, pvalue);
94 return std::make_pair(std::numeric_limits<Real>::quiet_NaN(), Real(0));
98 Real sd = sqrt(variance);
99 Real statistic = (runs - expected_runs)/sd;
101 auto normal = boost::math::normal_distribution<Real, no_promote_policy>(0,1);
102 Real pvalue = 2*boost::math::cdf(normal, -abs(statistic));
103 return std::make_pair(statistic, pvalue);
106 template<class RandomAccessContainer>
107 auto runs_above_and_below_median(RandomAccessContainer const & v)
109 using Real = typename RandomAccessContainer::value_type;
113 // We have to memcpy v because the median does a partial sort,
114 // and that would be catastrophic for the runs test.
116 Real median = boost::math::statistics::median(w);
117 return runs_above_and_below_threshold(v, median);