require_relative 'instrument' require 'ruby-metrics/statistics/uniform_sample' require 'ruby-metrics/statistics/exponential_sample' module Metrics module Instruments class Histogram < Instrument attr_reader :count def initialize(type = :uniform) @sample = case type when :uniform Metrics::Statistics::UniformSample.new when :exponential Metrics::Statistics::ExponentialSample.new else raise ArgumentError, "Unknown type #{type.inspect}" end clear end def update(value) @count += 1 @sum += value @sample.update(value) update_max(value) update_min(value) update_variance(value) end def clear @sample.clear @min = nil @max = nil @sum = 0 @count = 0 @variance_m = -1 @variance_s = 0 end def quantiles(percentiles) # Calculated using the same logic as R and Excel use # as outlined by the NIST here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm sorted_values = @sample.values[0...@count].sort scores = { } percentiles.each do |pct| scores[pct] = if @count == 0 0.0 else index = pct * (sorted_values.length - 1) + 1.0 if index <= 1 sorted_values.first elsif index >= sorted_values.length sorted_values.last else lower = sorted_values[index.to_i - 1] upper = sorted_values[index.to_i] lower + (index - index.floor) * (upper - lower) end end end scores end def update_min(value) if @min.nil? || value < @min @min = value end end def update_max(value) if @max.nil? || value > @max @max = value end end def update_variance(value) new_m = @variance_m + ((value - @variance_m) / @count) new_s = @variance_s + ((value - @variance_m) * (value - new_m)) @variance_m = new_m @variance_s = new_s end def variance if @count <= 1 0.0 else @variance_s.to_f / (@count - 1) end end def max @max || 0.0 end def min @min || 0.0 end def mean if @count > 0 @sum / @count else 0.0 end end def std_dev if @count > 0 Math.sqrt(variance) else 0.0 end end def values @sample.values end def as_json(*_) { :min => min, :max => max, :mean => mean, :variance => variance, :percentiles => quantiles(Timer::DEFAULT_PERCENTILES) } end def to_json(*_) as_json.to_json end end class ExponentialHistogram < Histogram def initialize super(:exponential) end end class UniformHistogram < Histogram def initialize super(:uniform) end end end end