Class: TrendLineCalculator

Inherits:
Object
  • Object
show all
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

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

Returns:

  • (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

Returns:

  • (Boolean)


26
# File 'lib/jirametrics/trend_line_calculator.rb', line 26

def valid? = @valid

#vertical?Boolean

Returns:

  • (Boolean)


28
# File 'lib/jirametrics/trend_line_calculator.rb', line 28

def vertical? = @slope.nan? || @slope.infinite?