Module: WeightedAverage::ArelSelectManagerInstanceMethods

Defined in:
lib/weighted_average/arel_select_manager_instance_methods.rb

Instance Method Summary collapse

Instance Method Details

#weighted_average(data_column_names, options = {}) ⇒ Float?

Calculate the weighted average of column(s).

Examples:

Weighted average of load factor in flight stage data

Arel::Table.new(:flight_segments).weighted_average(:load_factor, :weighted_by => :passengers)

Parameters:

  • data_column_names (Symbol, Array<Symbol>)

    One or more column names whose average should be calculated. Added together before being multiplied by the weighting if more than one.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :weighted_by (Symbol)

    The name of the weighting column if it’s not :weighting (the default)

  • :disaggregate_by (Symbol)

    The name of a column to disaggregate by. Usually not necessary.

Returns:

  • (Float, nil)

See Also:

[View source]

17
18
19
20
# File 'lib/weighted_average/arel_select_manager_instance_methods.rb', line 17

def weighted_average(data_column_names, options = {})
  weighted_average = @engine.connection.select_value(weighted_average_relation(data_column_names, options).to_sql)
  weighted_average.nil? ? nil : weighted_average.to_f
end

#weighted_average_relation(data_column_names, options = {}) ⇒ Arel::SelectManager

In case you want to get the relation and/or the SQL of the calculation query before actually runnnig it.

Examples:

Get the SQL

Arel::Table.new(:flight_segments).weighted_average_relation(:load_factor, :weighted_by => :passengers).to_sql

Returns:

  • (Arel::SelectManager)

    A relation you can play around with.

[View source]

28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
77
78
79
80
81
82
83
84
85
86
# File 'lib/weighted_average/arel_select_manager_instance_methods.rb', line 28

def weighted_average_relation(data_column_names, options = {})
  unless options[:safe] == true
    return clone.weighted_average_relation(data_column_names, options.merge(:safe => true))
  end

  data_column_names = Array.wrap data_column_names
  left = self.source.left

  weighted_by_column = case options[:weighted_by]
  when Arel::Attribute
    options[:weighted_by]
  when Symbol, String
    left[options[:weighted_by]]
  when NilClass
    left[DEFAULT_WEIGHTED_BY_COLUMN_NAME]
  else
    raise ArgumentError, ":weighted_by => #{options[:weighted_by].inspect} must be a column on #{left.inspect}"
  end
  
  disaggregate_by_column = if options[:disaggregate_by]
    left[options[:disaggregate_by]]
  end

  data_columns = data_column_names.map do |data_column_name|
    left[data_column_name]
  end

  data_columns_added_together = data_columns.inject(nil) do |memo, data_column|
    if memo
      Arel::Nodes::Addition.new(memo, data_column)
    else
      data_column
    end
  end

  if data_column_names.many?
    data_columns_added_together = Arel::Nodes::Grouping.new(data_columns_added_together)
  end

  if disaggregate_by_column
    self.projections = [Arel::Nodes::Division.new(Arel::Nodes::Sum.new(weighted_by_column * data_columns_added_together / disaggregate_by_column * 1.0), Arel::Nodes::Sum.new([weighted_by_column]))]
  else
    self.projections = [Arel::Nodes::Division.new(Arel::Nodes::Sum.new(weighted_by_column * data_columns_added_together * 1.0), Arel::Nodes::Sum.new([weighted_by_column]))]
  end

  data_columns_not_eq_nil = data_columns.inject(nil) do |memo, data_column|
    if memo
      memo.and(data_column.not_eq(nil))
    else
      data_column.not_eq(nil)
    end
  end

  if disaggregate_by_column
    where data_columns_not_eq_nil.and(weighted_by_column.gt(0)).and(disaggregate_by_column.gt(0))
  else
    where data_columns_not_eq_nil.and(weighted_by_column.gt(0))
  end
end