univariate_statistics.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. // (C) Copyright Nick Thompson 2018.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_TOOLS_UNIVARIATE_STATISTICS_HPP
  6. #define BOOST_MATH_TOOLS_UNIVARIATE_STATISTICS_HPP
  7. #include <algorithm>
  8. #include <iterator>
  9. #include <boost/type_traits/is_complex.hpp>
  10. #include <boost/assert.hpp>
  11. #include <boost/multiprecision/detail/number_base.hpp>
  12. namespace boost::math::tools {
  13. template<class ForwardIterator>
  14. auto mean(ForwardIterator first, ForwardIterator last)
  15. {
  16. using Real = typename std::iterator_traits<ForwardIterator>::value_type;
  17. BOOST_ASSERT_MSG(first != last, "At least one sample is required to compute the mean.");
  18. if constexpr (std::is_integral<Real>::value)
  19. {
  20. double mu = 0;
  21. double i = 1;
  22. for(auto it = first; it != last; ++it) {
  23. mu = mu + (*it - mu)/i;
  24. i += 1;
  25. }
  26. return mu;
  27. }
  28. else
  29. {
  30. Real mu = 0;
  31. Real i = 1;
  32. for(auto it = first; it != last; ++it) {
  33. mu = mu + (*it - mu)/i;
  34. i += 1;
  35. }
  36. return mu;
  37. }
  38. }
  39. template<class Container>
  40. inline auto mean(Container const & v)
  41. {
  42. return mean(v.cbegin(), v.cend());
  43. }
  44. template<class ForwardIterator>
  45. auto variance(ForwardIterator first, ForwardIterator last)
  46. {
  47. using Real = typename std::iterator_traits<ForwardIterator>::value_type;
  48. BOOST_ASSERT_MSG(first != last, "At least one sample is required to compute mean and variance.");
  49. // Higham, Accuracy and Stability, equation 1.6a and 1.6b:
  50. if constexpr (std::is_integral<Real>::value)
  51. {
  52. double M = *first;
  53. double Q = 0;
  54. double k = 2;
  55. for (auto it = std::next(first); it != last; ++it)
  56. {
  57. double tmp = *it - M;
  58. Q = Q + ((k-1)*tmp*tmp)/k;
  59. M = M + tmp/k;
  60. k += 1;
  61. }
  62. return Q/(k-1);
  63. }
  64. else
  65. {
  66. Real M = *first;
  67. Real Q = 0;
  68. Real k = 2;
  69. for (auto it = std::next(first); it != last; ++it)
  70. {
  71. Real tmp = (*it - M)/k;
  72. Q += k*(k-1)*tmp*tmp;
  73. M += tmp;
  74. k += 1;
  75. }
  76. return Q/(k-1);
  77. }
  78. }
  79. template<class Container>
  80. inline auto variance(Container const & v)
  81. {
  82. return variance(v.cbegin(), v.cend());
  83. }
  84. template<class ForwardIterator>
  85. auto sample_variance(ForwardIterator first, ForwardIterator last)
  86. {
  87. size_t n = std::distance(first, last);
  88. BOOST_ASSERT_MSG(n > 1, "At least two samples are required to compute the sample variance.");
  89. return n*variance(first, last)/(n-1);
  90. }
  91. template<class Container>
  92. inline auto sample_variance(Container const & v)
  93. {
  94. return sample_variance(v.cbegin(), v.cend());
  95. }
  96. // Follows equation 1.5 of:
  97. // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
  98. template<class ForwardIterator>
  99. auto skewness(ForwardIterator first, ForwardIterator last)
  100. {
  101. using Real = typename std::iterator_traits<ForwardIterator>::value_type;
  102. BOOST_ASSERT_MSG(first != last, "At least one sample is required to compute skewness.");
  103. if constexpr (std::is_integral<Real>::value)
  104. {
  105. double M1 = *first;
  106. double M2 = 0;
  107. double M3 = 0;
  108. double n = 2;
  109. for (auto it = std::next(first); it != last; ++it)
  110. {
  111. double delta21 = *it - M1;
  112. double tmp = delta21/n;
  113. M3 = M3 + tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  114. M2 = M2 + tmp*(n-1)*delta21;
  115. M1 = M1 + tmp;
  116. n += 1;
  117. }
  118. double var = M2/(n-1);
  119. if (var == 0)
  120. {
  121. // The limit is technically undefined, but the interpretation here is clear:
  122. // A constant dataset has no skewness.
  123. return double(0);
  124. }
  125. double skew = M3/(M2*sqrt(var));
  126. return skew;
  127. }
  128. else
  129. {
  130. Real M1 = *first;
  131. Real M2 = 0;
  132. Real M3 = 0;
  133. Real n = 2;
  134. for (auto it = std::next(first); it != last; ++it)
  135. {
  136. Real delta21 = *it - M1;
  137. Real tmp = delta21/n;
  138. M3 += tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  139. M2 += tmp*(n-1)*delta21;
  140. M1 += tmp;
  141. n += 1;
  142. }
  143. Real var = M2/(n-1);
  144. if (var == 0)
  145. {
  146. // The limit is technically undefined, but the interpretation here is clear:
  147. // A constant dataset has no skewness.
  148. return Real(0);
  149. }
  150. Real skew = M3/(M2*sqrt(var));
  151. return skew;
  152. }
  153. }
  154. template<class Container>
  155. inline auto skewness(Container const & v)
  156. {
  157. return skewness(v.cbegin(), v.cend());
  158. }
  159. // Follows equation 1.5/1.6 of:
  160. // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
  161. template<class ForwardIterator>
  162. auto first_four_moments(ForwardIterator first, ForwardIterator last)
  163. {
  164. using Real = typename std::iterator_traits<ForwardIterator>::value_type;
  165. BOOST_ASSERT_MSG(first != last, "At least one sample is required to compute the first four moments.");
  166. if constexpr (std::is_integral<Real>::value)
  167. {
  168. double M1 = *first;
  169. double M2 = 0;
  170. double M3 = 0;
  171. double M4 = 0;
  172. double n = 2;
  173. for (auto it = std::next(first); it != last; ++it)
  174. {
  175. double delta21 = *it - M1;
  176. double tmp = delta21/n;
  177. M4 = M4 + tmp*(tmp*tmp*delta21*((n-1)*(n*n-3*n+3)) + 6*tmp*M2 - 4*M3);
  178. M3 = M3 + tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  179. M2 = M2 + tmp*(n-1)*delta21;
  180. M1 = M1 + tmp;
  181. n += 1;
  182. }
  183. return std::make_tuple(M1, M2/(n-1), M3/(n-1), M4/(n-1));
  184. }
  185. else
  186. {
  187. Real M1 = *first;
  188. Real M2 = 0;
  189. Real M3 = 0;
  190. Real M4 = 0;
  191. Real n = 2;
  192. for (auto it = std::next(first); it != last; ++it)
  193. {
  194. Real delta21 = *it - M1;
  195. Real tmp = delta21/n;
  196. M4 = M4 + tmp*(tmp*tmp*delta21*((n-1)*(n*n-3*n+3)) + 6*tmp*M2 - 4*M3);
  197. M3 = M3 + tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  198. M2 = M2 + tmp*(n-1)*delta21;
  199. M1 = M1 + tmp;
  200. n += 1;
  201. }
  202. return std::make_tuple(M1, M2/(n-1), M3/(n-1), M4/(n-1));
  203. }
  204. }
  205. template<class Container>
  206. inline auto first_four_moments(Container const & v)
  207. {
  208. return first_four_moments(v.cbegin(), v.cend());
  209. }
  210. // Follows equation 1.6 of:
  211. // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
  212. template<class ForwardIterator>
  213. auto kurtosis(ForwardIterator first, ForwardIterator last)
  214. {
  215. auto [M1, M2, M3, M4] = first_four_moments(first, last);
  216. if (M2 == 0)
  217. {
  218. return M2;
  219. }
  220. return M4/(M2*M2);
  221. }
  222. template<class Container>
  223. inline auto kurtosis(Container const & v)
  224. {
  225. return kurtosis(v.cbegin(), v.cend());
  226. }
  227. template<class ForwardIterator>
  228. auto excess_kurtosis(ForwardIterator first, ForwardIterator last)
  229. {
  230. return kurtosis(first, last) - 3;
  231. }
  232. template<class Container>
  233. inline auto excess_kurtosis(Container const & v)
  234. {
  235. return excess_kurtosis(v.cbegin(), v.cend());
  236. }
  237. template<class RandomAccessIterator>
  238. auto median(RandomAccessIterator first, RandomAccessIterator last)
  239. {
  240. size_t num_elems = std::distance(first, last);
  241. BOOST_ASSERT_MSG(num_elems > 0, "The median of a zero length vector is undefined.");
  242. if (num_elems & 1)
  243. {
  244. auto middle = first + (num_elems - 1)/2;
  245. std::nth_element(first, middle, last);
  246. return *middle;
  247. }
  248. else
  249. {
  250. auto middle = first + num_elems/2 - 1;
  251. std::nth_element(first, middle, last);
  252. std::nth_element(middle, middle+1, last);
  253. return (*middle + *(middle+1))/2;
  254. }
  255. }
  256. template<class RandomAccessContainer>
  257. inline auto median(RandomAccessContainer & v)
  258. {
  259. return median(v.begin(), v.end());
  260. }
  261. template<class RandomAccessIterator>
  262. auto gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
  263. {
  264. using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
  265. BOOST_ASSERT_MSG(first != last && std::next(first) != last, "Computation of the Gini coefficient requires at least two samples.");
  266. std::sort(first, last);
  267. if constexpr (std::is_integral<Real>::value)
  268. {
  269. double i = 1;
  270. double num = 0;
  271. double denom = 0;
  272. for (auto it = first; it != last; ++it)
  273. {
  274. num += *it*i;
  275. denom += *it;
  276. ++i;
  277. }
  278. // If the l1 norm is zero, all elements are zero, so every element is the same.
  279. if (denom == 0)
  280. {
  281. return double(0);
  282. }
  283. return ((2*num)/denom - i)/(i-1);
  284. }
  285. else
  286. {
  287. Real i = 1;
  288. Real num = 0;
  289. Real denom = 0;
  290. for (auto it = first; it != last; ++it)
  291. {
  292. num += *it*i;
  293. denom += *it;
  294. ++i;
  295. }
  296. // If the l1 norm is zero, all elements are zero, so every element is the same.
  297. if (denom == 0)
  298. {
  299. return Real(0);
  300. }
  301. return ((2*num)/denom - i)/(i-1);
  302. }
  303. }
  304. template<class RandomAccessContainer>
  305. inline auto gini_coefficient(RandomAccessContainer & v)
  306. {
  307. return gini_coefficient(v.begin(), v.end());
  308. }
  309. template<class RandomAccessIterator>
  310. inline auto sample_gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
  311. {
  312. size_t n = std::distance(first, last);
  313. return n*gini_coefficient(first, last)/(n-1);
  314. }
  315. template<class RandomAccessContainer>
  316. inline auto sample_gini_coefficient(RandomAccessContainer & v)
  317. {
  318. return sample_gini_coefficient(v.begin(), v.end());
  319. }
  320. template<class RandomAccessIterator>
  321. auto median_absolute_deviation(RandomAccessIterator first, RandomAccessIterator last, typename std::iterator_traits<RandomAccessIterator>::value_type center=std::numeric_limits<typename std::iterator_traits<RandomAccessIterator>::value_type>::quiet_NaN())
  322. {
  323. using std::abs;
  324. using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
  325. using std::isnan;
  326. if (isnan(center))
  327. {
  328. center = boost::math::tools::median(first, last);
  329. }
  330. size_t num_elems = std::distance(first, last);
  331. BOOST_ASSERT_MSG(num_elems > 0, "The median of a zero-length vector is undefined.");
  332. auto comparator = [&center](Real a, Real b) { return abs(a-center) < abs(b-center);};
  333. if (num_elems & 1)
  334. {
  335. auto middle = first + (num_elems - 1)/2;
  336. std::nth_element(first, middle, last, comparator);
  337. return abs(*middle);
  338. }
  339. else
  340. {
  341. auto middle = first + num_elems/2 - 1;
  342. std::nth_element(first, middle, last, comparator);
  343. std::nth_element(middle, middle+1, last, comparator);
  344. return (abs(*middle) + abs(*(middle+1)))/abs(static_cast<Real>(2));
  345. }
  346. }
  347. template<class RandomAccessContainer>
  348. inline auto median_absolute_deviation(RandomAccessContainer & v, typename RandomAccessContainer::value_type center=std::numeric_limits<typename RandomAccessContainer::value_type>::quiet_NaN())
  349. {
  350. return median_absolute_deviation(v.begin(), v.end(), center);
  351. }
  352. }
  353. #endif