lib/sparklines.rb in sparklines-0.4.1 vs lib/sparklines.rb in sparklines-0.4.2

- old
+ new

@@ -72,11 +72,11 @@ Licensed under the MIT license. =end class Sparklines - VERSION = '0.4.1' + VERSION = '0.4.2' @@label_margin = 5.0 @@pointsize = 10.0 class << self @@ -99,14 +99,16 @@ :share_color => 'red', :remain_color => 'lightgrey', :min_color => 'blue', :max_color => 'green', :last_color => 'red', + :std_dev_color => '#efefef', :has_min => false, :has_max => false, :has_last => false, + :has_std_dev => false, :label => nil } # HACK for HashWithIndifferentAccess @@ -142,41 +144,41 @@ end ## # Creates a continuous area sparkline. Relevant options. # - # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2. - # - # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 - # - # :upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50. - # - # :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false. - # - # :has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false. - # - # :has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false. - # - # :min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue. - # - # :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green. - # - # :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red. - # - # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red. - # - # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray. + # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2. + # + # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 + # + # :upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50. + # + # :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false. + # + # :has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false. + # + # :has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false. + # + # :min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue. + # + # :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green. + # + # :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red. + # + # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red. + # + # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray. def area - step = @options[:step].to_i - height = @options[:height].to_i + step = @options[:step].to_f + height = @options[:height].to_f background_color = @options[:background_color] create_canvas((@norm_data.size - 1) * step + 4, height, background_color) - upper = @options[:upper].to_i + upper = @options[:upper].to_f has_min = @options[:has_min] has_max = @options[:has_max] has_last = @options[:has_last] @@ -201,19 +203,19 @@ @draw.fill(above_color) @draw.define_clip_path('top') do @draw.rectangle(0,0,(@norm_data.size - 1) * step + 4,(height - 3 - upper/(101.0/(height-4)))) end @draw.clip_path('top') - @draw.polygon *coords.flatten + @draw.polygon(*coords.flatten) # Block off the top half of the image and draw the sparkline @draw.fill(below_color) @draw.define_clip_path('bottom') do @draw.rectangle(0,(height - 3 - upper/(101.0/(height-4))),(@norm_data.size - 1) * step + 4,height) end @draw.clip_path('bottom') - @draw.polygon *coords.flatten + @draw.polygon(*coords.flatten) # The sparkline looks kinda nasty if either the above_color or below_color gets the center line @draw.fill('black') @draw.line(0,(height - 3 - upper/(101.0/(height-4))),(@norm_data.size - 1) * step + 4,(height - 3 - upper/(101.0/(height-4)))) @@ -235,17 +237,17 @@ ## # A bar graph. def bar - step = @options[:step].to_i + step = @options[:step].to_f height = @options[:height].to_f background_color = @options[:background_color] create_canvas(@norm_data.length * step + 2, height, background_color) - upper = @options[:upper].to_i + upper = @options[:upper].to_f below_color = @options[:below_color] above_color = @options[:above_color] i = 1 @norm_data.each_with_index do |r, index| @@ -263,36 +265,41 @@ ## # Creates a discretized sparkline # - # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 + # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 # - # :upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50. + # :upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50. # - # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red. + # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red. # - # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray. + # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray. def discrete - height = @options[:height].to_i - upper = @options[:upper].to_i + height = @options[:height].to_f + upper = @options[:upper].to_f background_color = @options[:background_color] - step = @options[:step].to_i + step = @options[:step].to_f + + width = @norm_data.size * step - 1 - create_canvas(@norm_data.size * step - 1, height, background_color) + create_canvas(width, height, background_color) below_color = @options[:below_color] above_color = @options[:above_color] + std_dev_color = @options[:std_dev_color] + drawstddevbox(width,height,std_dev_color) if @options[:has_std_dev] == true + i = 0 @norm_data.each do |r| color = (r >= upper) ? above_color : below_color @draw.stroke(color) - @draw.line(i, (@canvas.rows - r/(101.0/(height-4))-4).to_i, - i, (@canvas.rows - r/(101.0/(height-4))).to_i) + @draw.line(i, (@canvas.rows - r/(101.0/(height-4))-4).to_f, + i, (@canvas.rows - r/(101.0/(height-4))).to_f) i += step end @draw.draw(@canvas) @canvas.to_blob @@ -300,18 +307,18 @@ ## # Creates a pie-chart sparkline # - # :diameter - An integer that determines what the size of the sparkline will be. Defaults to 20 + # :diameter - An integer that determines what the size of the sparkline will be. Defaults to 20 # - # :share_color - A string or color code representing the color to draw the share of the pie represented by percent. Defaults to red. + # :share_color - A string or color code representing the color to draw the share of the pie represented by percent. Defaults to red. # - # :remain_color - A string or color code representing the color to draw the pie not taken by the share color. Defaults to lightgrey. + # :remain_color - A string or color code representing the color to draw the pie not taken by the share color. Defaults to lightgrey. def pie - diameter = @options[:diameter].to_i + diameter = @options[:diameter].to_f background_color = @options[:background_color] create_canvas(diameter, diameter, background_color) share_color = @options[:share_color] @@ -368,41 +375,51 @@ ## # Creates a smooth sparkline. # - # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2. + # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2. # - # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 + # :height - An integer that determines what the height of the sparkline will be. Defaults to 14 # - # :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false. + # :has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false. # - # :has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false. + # :has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false. # - # :has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false. + # :has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false. # - # :min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue. + # :has_std_dev - Determines whether there will be a standard deviation bar behind the smooth graph or not. Defaults to false. # - # :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green. + # :min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue. # - # :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red. + # :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green. + # + # :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red. + # + # :std_dev_color - A string or color code representing the color that the standard deviation bar behind the smooth graph will be displayed as. Defaults to #efefef def smooth - step = @options[:step].to_i - height = @options[:height].to_i + step = @options[:step].to_f + height = @options[:height].to_f background_color = @options[:background_color] - create_canvas((@norm_data.size - 1) * step + 4, height, background_color) + width = (@norm_data.size - 1) * step + 4 + + create_canvas(width, height, background_color) min_color = @options[:min_color] max_color = @options[:max_color] last_color = @options[:last_color] has_min = @options[:has_min] has_max = @options[:has_max] has_last = @options[:has_last] line_color = @options[:line_color] + has_std_dev = @options[:has_std_dev] + std_dev_color = @options[:std_dev_color] + + drawstddevbox(width,height,std_dev_color) if has_std_dev == true @draw.stroke(line_color) coords = [] i=0 @norm_data.each do |r| @@ -432,20 +449,20 @@ # # * results - an array of integer values between -2 and 2. -2 is exceptional # down, 1 is regular down, 0 is no value, 1 is up, and 2 is exceptional up. # * options - a hash that takes parameters # - # :height - height of the sparkline + # :height - height of the sparkline # - # :whisker_color - the color of regular whiskers; defaults to black + # :whisker_color - the color of regular whiskers; defaults to black # - # :exception_color - the color of exceptional whiskers; defaults to red + # :exception_color - the color of exceptional whiskers; defaults to red def whisker - # step = @options[:step].to_i - height = @options[:height].to_i + # step = @options[:step].to_f + height = @options[:height].to_f background_color = @options[:background_color] create_canvas((@data.size - 1) * 2, height, background_color) whisker_color = @options[:whisker_color] || 'black' @@ -503,11 +520,11 @@ @norm_data = @data.map { |value| value = (value.to_f / @maximum_value) * 100.0 } end end ## - # * :arr - an array of points (represented as two element arrays) + # :arr - an array of points (represented as two element arrays) def open_ended_polyline(arr) 0.upto(arr.length - 2) { |i| @draw.line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1]) } @@ -519,10 +536,11 @@ # TODO Refactor into smaller methods def create_canvas(w, h, bkg_col) @draw = Magick::Draw.new @draw.pointsize = @@pointsize # TODO Use height + @draw.pointsize = @options[:font_size] if @options.has_key?(:font_size) @canvas = Magick::Image.new(w , h) { self.background_color = bkg_col } # Make room for label and last value unless @options[:label].nil? @options[:has_last] = true @@ -542,10 +560,11 @@ vera_font_path = File.expand_path('Vera.ttf', ENV['MAGICK_FONT_PATH']) @font = File.exists?(vera_font_path) ? vera_font_path : nil else @font = nil end + @font = @options[:font] if @options.has_key?(:font) @draw.fill = 'black' @draw.font = @font if @font @draw.gravity = Magick::WestGravity @draw.annotate( @canvas, @@ -569,14 +588,56 @@ @draw.stroke 'transparent' @draw.fill(color) @draw.rectangle(pt[0]-offset, pt[1]-offset, pt[0]+offset, pt[1]+offset) end + ## + # Utility to draw the standard deviation box + # + def drawstddevbox(width,height,color) + mid=@norm_data.inject(0) {|sum,v| sum+=v}/@norm_data.size + std_dev = standard_deviation(@norm_data)/100*height + @draw.stroke 'transparent' + @draw.fill(color) + @draw.rectangle(0, mid-std_dev, width, mid+std_dev) + end + def calculate_width(text) @draw.get_type_metrics(@canvas, text.to_s).width end def calculate_caps_height @draw.get_type_metrics(@canvas, 'X').height end + ## + # Calculation helper for standard deviation. + # + # Thanks to Warren Seen + # http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ + def variance(population) + n = 0 + mean = 0.0 + s = 0.0 + population.each { |x| + n = n + 1 + delta = x - mean + mean = mean + (delta / n) + s = s + delta * (x - mean) + } + # if you want to calculate std deviation + # of a sample change this to "s / (n-1)" + return s / n + end + + ## + # Calculate the standard deviation of a population + # + # accepts: an array, the population + # returns: the standard deviation + def standard_deviation(population) + Math.sqrt(variance(population)) + end + + end +