Class: TrendLineCalculator
- Inherits:
-
Object
- Object
- TrendLineCalculator
- Defined in:
- lib/jirametrics/trend_line_calculator.rb
Constant Summary collapse
- MINIMUM_POINTS =
Three points minimum. Two will always fit a straight line perfectly, so the result looks authoritative while carrying no evidence of a trend at all; drawing it was misleading.
3
Instance Method Summary collapse
- #calc_y(x:) ⇒ Object
-
#chart_datapoints(range:, max_y:, min_y: 0) ⇒ Object
If the trend line can't be calculated then return an empty array.
- #horizontal? ⇒ Boolean
-
#initialize(points) ⇒ TrendLineCalculator
constructor
A new instance of TrendLineCalculator.
- #line_crosses_at(y:) ⇒ Object
- #valid? ⇒ Boolean
- #vertical? ⇒ Boolean
Constructor Details
#initialize(points) ⇒ TrendLineCalculator
Returns a new instance of TrendLineCalculator.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 10 def initialize points @valid = points.size >= MINIMUM_POINTS return unless valid? sum_of_x = points.sum { |x, _y| x } sum_of_y = points.sum { |_x, y| y } sum_of_xy = points.sum { |x, y| x * y } sum_of_x2 = points.sum { |x, _y| x * x } n = points.size.to_f @slope = ((n * sum_of_xy) - (sum_of_x * sum_of_y)) / ((n * sum_of_x2) - (sum_of_x * sum_of_x)) @offset = (sum_of_y - (@slope * sum_of_x)) / n @valid = false if vertical? end |
Instance Method Details
#calc_y(x:) ⇒ Object
30 31 32 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 30 def calc_y x: ((x * @slope) + @offset).to_i end |
#chart_datapoints(range:, max_y:, min_y: 0) ⇒ Object
If the trend line can't be calculated then return an empty array. Otherwise, return an array with two (x,y) points, with which you can draw the trend line.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 42 def chart_datapoints range:, max_y:, min_y: 0 raise 'max_y is nil' if max_y.nil? return [] unless valid? data_points = [] x_start = range.begin y_start = calc_y(x: range.begin).to_i x_end = range.end y_end = calc_y(x: range.end).to_i if y_start < min_y x_start = line_crosses_at y: 0 y_start = 0 end if y_start > max_y x_start = line_crosses_at y: max_y y_start = max_y end if y_end < min_y x_end = line_crosses_at y: 0 y_end = 0 end if y_end > max_y x_end = line_crosses_at y: max_y y_end = max_y end data_points << { x: x_start, y: y_start } data_points << { x: x_end, y: y_end } data_points end |
#horizontal? ⇒ Boolean
27 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 27 def horizontal? = @slope.zero? |
#line_crosses_at(y:) ⇒ Object
34 35 36 37 38 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 34 def line_crosses_at y: raise "line will never cross #{y}. Trend is perfectly horizontal" if horizontal? ((y.to_f - @offset) / @slope).to_i end |
#valid? ⇒ Boolean
26 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 26 def valid? = @valid |
#vertical? ⇒ Boolean
28 |
# File 'lib/jirametrics/trend_line_calculator.rb', line 28 def vertical? = @slope.nan? || @slope.infinite? |