require 'pry' # gem install pry # Pass in an enumeration of data and # (optionally) a block to extract the grouping aspect of the data. # # Optional: sort_by lambda (operates on group key and count) def puts_hist(data, sort_by:nil, &blk) data = data.map(&blk) if blk counts = data.each_with_object(Hash.new(0)) {|k,h| h[k]+=1} max = counts.values.max width = Pry::Terminal.size!.last width0 = counts.keys.map{|k|k.to_s.length}.max width1 = width - width0 - 30 div = [1, max / width1.to_f].max puts "max: %d; widths: %d, %d; divisor: %0.3f" % [max, width0, width1, div] puts "bucket count sum bar" counts = counts.sort_by(&sort_by) if sort_by acc = 0 counts.each do |k,c| acc += c puts "%#{width0}s (%05s %06s) %-#{width1}s" % [("%.2f" % k), c, acc, "#"*((c/div).round)] end counts end # sample class RandomGaussian def initialize(mean, stddev, rand_helper = lambda { Kernel.rand }) @rand_helper = rand_helper @mean = mean @stddev = stddev @valid = false @next = 0 end def rand if @valid then @valid = false return @next else @valid = true x, y = self.class.gaussian(@mean, @stddev, @rand_helper) @next = y return x end end private def self.gaussian(mean, stddev, rand) theta = 2 * Math::PI * rand.call rho = Math.sqrt(-2 * Math.log(1 - rand.call)) scale = stddev * rho x = mean + scale * Math.cos(theta) y = mean + scale * Math.sin(theta) return x, y end end r=RandomGaussian.new(3, 2) pp=Array.new(1e4){r.rand}.map{|d|d if d >= 0}.compact tt = pp.map{|i| (i*4).to_i/4.0 }.sort puts_hist(tt)