binomial_proportion_interval.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. // Copyright 2022 Jay Gohil, Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_UTILITY_BINOMIAL_PROPORTION_INTERVAL_HPP
  7. #define BOOST_HISTOGRAM_UTILITY_BINOMIAL_PROPORTION_INTERVAL_HPP
  8. #include <boost/histogram/detail/normal.hpp>
  9. #include <boost/histogram/fwd.hpp>
  10. #include <boost/throw_exception.hpp>
  11. #include <cmath>
  12. #include <stdexcept>
  13. #include <type_traits>
  14. namespace boost {
  15. namespace histogram {
  16. namespace utility {
  17. /**
  18. Common base class for interval calculators.
  19. */
  20. template <class ValueType>
  21. class binomial_proportion_interval {
  22. static_assert(std::is_floating_point<ValueType>::value,
  23. "Value must be a floating point!");
  24. public:
  25. using value_type = ValueType;
  26. using interval_type = std::pair<value_type, value_type>;
  27. /** Compute interval for given number of successes and failures.
  28. @param successes Number of successful trials.
  29. @param failures Number of failed trials.
  30. */
  31. virtual interval_type operator()(value_type successes,
  32. value_type failures) const noexcept = 0;
  33. /** Compute interval for a fraction accumulator.
  34. @param fraction Fraction accumulator.
  35. */
  36. template <class T>
  37. interval_type operator()(const accumulators::fraction<T>& fraction) const noexcept {
  38. return operator()(fraction.successes(), fraction.failures());
  39. }
  40. };
  41. class deviation;
  42. class confidence_level;
  43. /** Confidence level in units of deviations for intervals.
  44. Intervals become wider as the deviation value increases. The standard deviation
  45. corresponds to a value of 1 and corresponds to 68.3 % confidence level. The conversion
  46. between confidence level and deviations is based on a two-sided interval on the normal
  47. distribution.
  48. */
  49. class deviation {
  50. public:
  51. /// constructor from units of standard deviations
  52. explicit deviation(double d) : d_{d} {
  53. if (d <= 0)
  54. BOOST_THROW_EXCEPTION(std::invalid_argument("scaling factor must be positive"));
  55. }
  56. /// explicit conversion to units of standard deviations
  57. template <class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
  58. explicit operator T() const noexcept {
  59. return static_cast<T>(d_);
  60. }
  61. /// implicit conversion to confidence level
  62. operator confidence_level() const noexcept; // need to implement confidence_level first
  63. friend deviation operator*(deviation d, double z) noexcept {
  64. return deviation(d.d_ * z);
  65. }
  66. friend deviation operator*(double z, deviation d) noexcept { return d * z; }
  67. friend bool operator==(deviation a, deviation b) noexcept { return a.d_ == b.d_; }
  68. friend bool operator!=(deviation a, deviation b) noexcept { return !(a == b); }
  69. private:
  70. double d_;
  71. };
  72. /** Confidence level for intervals.
  73. Intervals become wider as the deviation value increases.
  74. */
  75. class confidence_level {
  76. public:
  77. /// constructor from confidence level (a probability)
  78. explicit confidence_level(double cl) : cl_{cl} {
  79. if (cl <= 0 || cl >= 1)
  80. BOOST_THROW_EXCEPTION(std::invalid_argument("0 < cl < 1 is required"));
  81. }
  82. /// explicit conversion to numerical confidence level
  83. template <class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
  84. explicit operator T() const noexcept {
  85. return static_cast<T>(cl_);
  86. }
  87. /// implicit conversion to units of standard deviation
  88. operator deviation() const noexcept {
  89. return deviation{detail::normal_ppf(std::fma(0.5, cl_, 0.5))};
  90. }
  91. friend bool operator==(confidence_level a, confidence_level b) noexcept {
  92. return a.cl_ == b.cl_;
  93. }
  94. friend bool operator!=(confidence_level a, confidence_level b) noexcept {
  95. return !(a == b);
  96. }
  97. private:
  98. double cl_;
  99. };
  100. inline deviation::operator confidence_level() const noexcept {
  101. // solve normal cdf(z) - cdf(-z) = 2 (cdf(z) - 0.5)
  102. return confidence_level{std::fma(2.0, detail::normal_cdf(d_), -1.0)};
  103. }
  104. } // namespace utility
  105. } // namespace histogram
  106. } // namespace boost
  107. #endif