stddev.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "stddev.h"
  3. // ----------------------------------------------------------------------------
  4. // stddev
  5. // this implementation comes from:
  6. // https://www.johndcook.com/blog/standard_deviation/
  7. struct grouping_stddev {
  8. long count;
  9. calculated_number m_oldM, m_newM, m_oldS, m_newS;
  10. };
  11. void *grouping_create_stddev(RRDR *r) {
  12. long entries = r->group;
  13. if(entries < 0) entries = 0;
  14. return callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
  15. }
  16. // resets when switches dimensions
  17. // so, clear everything to restart
  18. void grouping_reset_stddev(RRDR *r) {
  19. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  20. g->count = 0;
  21. }
  22. void grouping_free_stddev(RRDR *r) {
  23. freez(r->internal.grouping_data);
  24. r->internal.grouping_data = NULL;
  25. }
  26. void grouping_add_stddev(RRDR *r, calculated_number value) {
  27. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  28. if(calculated_number_isnumber(value)) {
  29. g->count++;
  30. // See Knuth TAOCP vol 2, 3rd edition, page 232
  31. if (g->count == 1) {
  32. g->m_oldM = g->m_newM = value;
  33. g->m_oldS = 0.0;
  34. }
  35. else {
  36. g->m_newM = g->m_oldM + (value - g->m_oldM) / g->count;
  37. g->m_newS = g->m_oldS + (value - g->m_oldM) * (value - g->m_newM);
  38. // set up for next iteration
  39. g->m_oldM = g->m_newM;
  40. g->m_oldS = g->m_newS;
  41. }
  42. }
  43. }
  44. static inline calculated_number mean(struct grouping_stddev *g) {
  45. return (g->count > 0) ? g->m_newM : 0.0;
  46. }
  47. static inline calculated_number variance(struct grouping_stddev *g) {
  48. return ( (g->count > 1) ? g->m_newS/(g->count - 1) : 0.0 );
  49. }
  50. static inline calculated_number stddev(struct grouping_stddev *g) {
  51. return sqrtl(variance(g));
  52. }
  53. calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  54. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  55. calculated_number value;
  56. if(likely(g->count > 1)) {
  57. value = stddev(g);
  58. if(!calculated_number_isnumber(value)) {
  59. value = 0.0;
  60. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  61. }
  62. }
  63. else if(g->count == 1) {
  64. value = 0.0;
  65. }
  66. else {
  67. value = 0.0;
  68. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  69. }
  70. grouping_reset_stddev(r);
  71. return value;
  72. }
  73. // https://en.wikipedia.org/wiki/Coefficient_of_variation
  74. calculated_number grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  75. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  76. calculated_number value;
  77. if(likely(g->count > 1)) {
  78. calculated_number m = mean(g);
  79. value = 100.0 * stddev(g) / ((m < 0)? -m : m);
  80. if(unlikely(!calculated_number_isnumber(value))) {
  81. value = 0.0;
  82. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  83. }
  84. }
  85. else if(g->count == 1) {
  86. // one value collected
  87. value = 0.0;
  88. }
  89. else {
  90. // no values collected
  91. value = 0.0;
  92. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  93. }
  94. grouping_reset_stddev(r);
  95. return value;
  96. }
  97. /*
  98. * Mean = average
  99. *
  100. calculated_number grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  101. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  102. calculated_number value;
  103. if(unlikely(!g->count)) {
  104. value = 0.0;
  105. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  106. }
  107. else {
  108. value = mean(g);
  109. if(!isnormal(value)) {
  110. value = 0.0;
  111. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  112. }
  113. }
  114. grouping_reset_stddev(r);
  115. return value;
  116. }
  117. */
  118. /*
  119. * It is not advised to use this version of variance directly
  120. *
  121. calculated_number grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  122. struct grouping_stddev *g = (struct grouping_stddev *)r->internal.grouping_data;
  123. calculated_number value;
  124. if(unlikely(!g->count)) {
  125. value = 0.0;
  126. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  127. }
  128. else {
  129. value = variance(g);
  130. if(!isnormal(value)) {
  131. value = 0.0;
  132. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  133. }
  134. }
  135. grouping_reset_stddev(r);
  136. return value;
  137. }
  138. */