summary.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Unless explicitly stated otherwise all files in this repository are licensed
  2. // under the Apache License 2.0.
  3. // This product includes software developed at Datadog (https://www.datadoghq.com/).
  4. // Copyright 2021 Datadog, Inc.
  5. package stat
  6. import "math"
  7. // SummaryStatistics keeps track of the count, the sum, the min and the max of
  8. // recorded values. We use a compensated sum to avoid accumulating rounding
  9. // errors (see https://en.wikipedia.org/wiki/Kahan_summation_algorithm).
  10. type SummaryStatistics struct {
  11. count float64
  12. sum float64
  13. sumCompensation float64
  14. simpleSum float64
  15. min float64
  16. max float64
  17. }
  18. func NewSummaryStatistics() *SummaryStatistics {
  19. return &SummaryStatistics{
  20. count: 0,
  21. sum: 0,
  22. sumCompensation: 0,
  23. simpleSum: 0,
  24. min: math.Inf(1),
  25. max: math.Inf(-1),
  26. }
  27. }
  28. func (s *SummaryStatistics) Count() float64 {
  29. return s.count
  30. }
  31. func (s *SummaryStatistics) Sum() float64 {
  32. // Better error bounds to add both terms as the final sum
  33. tmp := s.sum + s.sumCompensation
  34. if math.IsNaN(tmp) && math.IsInf(s.simpleSum, 0) {
  35. // If the compensated sum is spuriously NaN from accumulating one or more same-signed infinite
  36. // values, return the correctly-signed infinity stored in simpleSum.
  37. return s.simpleSum
  38. } else {
  39. return tmp
  40. }
  41. }
  42. func (s *SummaryStatistics) Min() float64 {
  43. return s.min
  44. }
  45. func (s *SummaryStatistics) Max() float64 {
  46. return s.max
  47. }
  48. func (s *SummaryStatistics) Add(value, count float64) {
  49. s.AddToCount(count)
  50. s.AddToSum(value * count)
  51. if value < s.min {
  52. s.min = value
  53. }
  54. if value > s.max {
  55. s.max = value
  56. }
  57. }
  58. func (s *SummaryStatistics) AddToCount(addend float64) {
  59. s.count += addend
  60. }
  61. func (s *SummaryStatistics) AddToSum(addend float64) {
  62. s.sumWithCompensation(addend)
  63. s.simpleSum += addend
  64. }
  65. func (s *SummaryStatistics) MergeWith(o *SummaryStatistics) {
  66. s.count += o.count
  67. s.sumWithCompensation(o.sum)
  68. s.sumWithCompensation(o.sumCompensation)
  69. s.simpleSum += o.simpleSum
  70. if o.min < s.min {
  71. s.min = o.min
  72. }
  73. if o.max > s.max {
  74. s.max = o.max
  75. }
  76. }
  77. func (s *SummaryStatistics) sumWithCompensation(value float64) {
  78. tmp := value - s.sumCompensation
  79. velvel := s.sum + tmp // little wolf of rounding error
  80. s.sumCompensation = velvel - s.sum - tmp
  81. s.sum = velvel
  82. }
  83. // Reweight adjusts the statistics so that they are equal to what they would
  84. // have been if AddWithCount had been called with counts multiplied by factor.
  85. func (s *SummaryStatistics) Reweight(factor float64) {
  86. s.count *= factor
  87. s.sum *= factor
  88. s.sumCompensation *= factor
  89. s.simpleSum *= factor
  90. if factor == 0 {
  91. s.min = math.Inf(1)
  92. s.max = math.Inf(-1)
  93. }
  94. }
  95. // Rescale adjusts the statistics so that they are equal to what they would have
  96. // been if AddWithCount had been called with values multiplied by factor.
  97. func (s *SummaryStatistics) Rescale(factor float64) {
  98. s.sum *= factor
  99. s.sumCompensation *= factor
  100. s.simpleSum *= factor
  101. if factor > 0 {
  102. s.min *= factor
  103. s.max *= factor
  104. } else if factor < 0 {
  105. tmp := s.max * factor
  106. s.max = s.min * factor
  107. s.min = tmp
  108. } else if s.count != 0 {
  109. s.min = 0
  110. s.max = 0
  111. }
  112. }
  113. func (s *SummaryStatistics) Clear() {
  114. s.count = 0
  115. s.sum = 0
  116. s.sumCompensation = 0
  117. s.simpleSum = 0
  118. s.min = math.Inf(1)
  119. s.max = math.Inf(-1)
  120. }
  121. func (s *SummaryStatistics) Copy() *SummaryStatistics {
  122. return &SummaryStatistics{
  123. count: s.count,
  124. sum: s.sum,
  125. sumCompensation: s.sumCompensation,
  126. simpleSum: s.simpleSum,
  127. min: s.min,
  128. max: s.max,
  129. }
  130. }