module AMEE::Analytics::TermsListAnalyticsSupport
Mixin module for the AMEE::DataAbstraction::Term class, providing methods for handling collections of calculations.
Public Instance Methods
# File lib/amee/analytics/terms_list_analytics_support.rb, line 75 def +(other_list) self.class.new(self.to_a + other_list.to_a) end
# File lib/amee/analytics/terms_list_analytics_support.rb, line 79 def -(other_list) other_list = [other_list].flatten self.delete_if { |term| other_list.include?(term) } end
Returns true
if all terms in the list have numeric values. Otherwise, returns false
.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 125 def all_numeric? all? { |term| term.has_numeric_value? } end
Returns true
if all terms within the list have the same label. Otherwise, returns false
.
This enables a check as to whether all terms represent the same thing, i.e. same calculation component (i.e. the same drill choice, or profile item value, or return value, or metadata type).
# File lib/amee/analytics/terms_list_analytics_support.rb, line 26 def analogous? labels.uniq.size == (1 or nil) end
# File lib/amee/analytics/terms_list_analytics_support.rb, line 84 def first_of_each_type labels = self.labels.uniq terms = labels.map {|label| find { |term| term.label == label } } AMEE::DataAbstraction::TermsList.new(terms) end
Returns true
if TermsList is NOT homogeneous, i.e. it does NOT contain all analogous terms with corresponding units. Otherwise, returns false
.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 44 def heterogeneous? !homogeneous? end
Returns true
if all terms within the list have the same label AND contain consistent units. Otherwise, returns false
.
This enables a term list to be manipulated numerically, for example, by producing a sum or a mean across all terms.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 36 def homogeneous? analogous? and homogeneous_units? and homogeneous_per_units? end
Returns true
if all terms within the list are represented by the same PER unit or are all nil
. Otherwise, returns false
.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 61 def homogeneous_per_units? return true if all? { |term| term.per_unit.nil? } or ( all? { |term| term.per_unit.is_a? Quantity::Unit::Base } and map { |term| term.per_unit.label }.uniq.size == 1 ) return false end
Returns true
if all terms within the list are represented by the same unit or are all nil
. Otherwise, returns false
.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 51 def homogeneous_units? return true if all? { |term| term.unit.nil? } or ( all? { |term| term.unit.is_a? Quantity::Unit::Base } and map { |term| term.unit.label }.uniq.size == 1 ) return false end
Convenience method for initializing instances of the Result class. Intialize the new object with the attributes described by label
, value
, unit
and per_unit
. The unit and per_unit attributes default to nil
if left unspecified.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 249 def initialize_result(label,value,unit=nil,per_unit=nil) Result.new { label label; value value; unit unit; per_unit per_unit } end
Returns the label which defines all terms in contained within self
, if they are all the same. Otherwise, returns nil
.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 71 def label first.label if analogous? end
Returns a new instance of Result which represents the mean of all term values within the list.
Any terms within self which contain non-numeric values are ignored.
If the terms within self
do not contain consistent units, they are standardized by default to the unit (and per unit) which predominate in the list. Alternatively, the required unit and per units can be specified as arguments using the same conventions as the #standardize_units
method.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 197 def mean(unit=nil,per_unit=nil) list = numeric_terms sum = list.sum(unit,per_unit) Result.new { label sum.label; value (sum.value/list.size); unit sum.unit; per_unit sum.per_unit; name sum.name } end
Returns a representation of the term with median value in self
. This method considers both numerical and text values.
If self
has an even-numbered size, the median is caluclated as the mean of the values of the two centrally placed terms (having been sorted according to their value attributes).
# File lib/amee/analytics/terms_list_analytics_support.rb, line 231 def median new_list = standardize_units midpoint = new_list.size/2 if new_list.size % 2.0 == 1 median_term = new_list.sort_by_value[midpoint] elsif new_list.size % 2.0 == 0 median_term = new_list.sort_by_value[midpoint-1, 2].mean else raise end median_term.to_result end
Syntactic sugar for several instance methods.
Call a method on self
which named after a specific term label contained within self
and return a new instance of the TermsList
class containing each of those terms. E.g.,
my_terms = my_terms_list.type #=> <AMEE::DataAbstraction::TermsList> my_terms.label #=> :type my_terms = my_terms_list.mass #=> <AMEE::DataAbstraction::TermsList> my_terms.label #=> :mass my_terms = my_terms_list.co2 #=> <AMEE::DataAbstraction::TermsList> my_terms.label #=> :co2
Call either the #sort_by
or #sort_by!
methods including the argument term as part of the method name, e.g.,
my_calculation_collection.sort_by_value #=> <AMEE::DataAbstraction::TermsList ... > my_calculation_collection.sort_by_name! #=> <AMEE::DataAbstraction::TermsList ... >
# File lib/amee/analytics/terms_list_analytics_support.rb, line 376 def method_missing(method, *args, &block) if labels.include? method AMEE::DataAbstraction::TermsList.new select{ |x| x.label == method } elsif method.to_s =~ /sort_by_(.*)!/ and self.class::TermProperties.include? $1.to_sym sort_by! $1.to_sym elsif method.to_s =~ /sort_by_(.*)/ and self.class::TermProperties.include? $1.to_sym sort_by $1.to_sym else super end end
Returns a representation of the term with most prevalent value in self
, i.e. the modal value. This method considers both numerical and text values.
If only a single modal value is discovered an instance of the class Result is returning representing the modal value. Where multiple modal values occur a new instance of TermsList is returned containing Result representations of each modal value.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 212 def mode groups = standardize_units.reject { |term| term.value.nil? }. group_by { |term| term.value }.map(&:last) max_group_size = groups.max {|a,b| a.size <=> b.size }.size max_groups = groups.select {|a| a.size == max_group_size} if max_groups.size == 1 max_groups.first.first.to_result else AMEE::DataAbstraction::TermsList.new max_groups.map { |group| group.first.to_result } end end
Move an individual term to a specified location (index) within the list. The specific term is selected on the basis of one of it’s attributes values, with the attribute to use (e.g. :value, :unit) given by attr</attr> and value by <tt>value
. The location within the list to move the term is given as an index integer value as the final argument.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 259 def move_by(attr,value,index) if attr == :unit || attr == :per_unit value = Unit.for value end term = find {|t| t.send(attr) == value } return if term.nil? delete(term) insert(index, term) end
# File lib/amee/analytics/terms_list_analytics_support.rb, line 15 def name first.name if analogous? end
Returns a new instance of TermsList comprising only those terms belongong to self
which have numeric values.
This is useful for establishing which terms in a list to perform numerical operations on
# File lib/amee/analytics/terms_list_analytics_support.rb, line 135 def numeric_terms AMEE::DataAbstraction::TermsList.new select { |term| term.has_numeric_value? } end
Returns the label of the per unit which is predominantly used across all terms in the list, e.g.
list.predominant_per_unit #=> h list.predominant_per_unit #=> kWh
Returns nil if all per units are blank
# File lib/amee/analytics/terms_list_analytics_support.rb, line 115 def predominant_per_unit terms = reject { |term| term.per_unit.nil? } unit = terms.group_by { |term| term.per_unit.label }. max {|a,b| a.last.size <=> b.last.size }.first unless terms.blank? return unit end
Returns the label of the unit which is predominantly used across all terms in the list, e.g.
list.predominant_unit #=> kg list.predominant_unit #=> kWh
Returns nil if all units are blank
# File lib/amee/analytics/terms_list_analytics_support.rb, line 99 def predominant_unit terms = reject { |term| term.unit.nil? } unit = terms.group_by { |term| term.unit.label }. max {|a,b| a.last.size <=> b.last.size }.first unless terms.blank? return unit end
# File lib/amee/analytics/terms_list_analytics_support.rb, line 334 def respond_to?(method) if labels.include? method.to_sym return true elsif method.to_s =~ /sort_by_(.*)!/ and self.class::TermProperties.include? $1.to_sym return true elsif method.to_s =~ /sort_by_(.*)/ and self.class::TermProperties.include? $1.to_sym return true else super end end
Rotate the list terms by one element - shifts the first-placed term to the end of the list, advancing all terms forward by one place.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 271 def rotate push(self.shift) end
Similar to #sort_by!
but returns a new instance of TermsList arranged according to the values on the attribute attr
.
If differences in units exist between terms, sorting occur based on the absolute quantities implied.
E.g.
my_terms_list.sort_by :value #=> <AMEE::DataAbstraction::TermsList ... >
# File lib/amee/analytics/terms_list_analytics_support.rb, line 303 def sort_by(attr) # 1. Remove unset terms before sort and append at end # # 2. Establish set terms # # 3. Zip together with corresponding standardized units list creating a # list of Term pairs # # 4. Sort list according to standardized Terms # # 5. Return map of original (now sorted) Terms unset_terms, set_terms = self.partition { |term| term.unset? || term.value.nil? } standardized_set_terms = AMEE::DataAbstraction::TermsList.new(set_terms).standardize_units ordered_set_terms = set_terms.zip(standardized_set_terms).sort! do |term,other_term| term[1].send(attr) <=> other_term[1].send(attr) end.map {|term_array| term_array[0]} AMEE::DataAbstraction::TermsList.new(ordered_set_terms + unset_terms) end
Sorts the terms list in place according to the term attribute indicated by attr
, returning self
.
If differences in units exist between terms, sorting occur based on the absolute quantities implied.
my_terms_list.sort_by! :value #=> <AMEE::DataAbstraction::TermsList ... >
# File lib/amee/analytics/terms_list_analytics_support.rb, line 285 def sort_by!(attr) replace(sort_by(attr)) end
Returns a new instance of TermsList with all units standardized and the respective term values adjusted accordingly.
The unit and per units to be standardized to can be specified as the first and second arguments respectively. Either the unit name, symbol or label (as defined in the Quantify gem) can be used. If no arguments are specified, the standardized units represent those which are predominant in the list, e.g.
list.standardize_units #=> <TermsList> list.standardize_units(:t,:kWh) #=> <TermsList> list.standardize_units('pound') #=> <TermsList> list.standardize_units(nil, 'BTU') #=> <TermsList>
# File lib/amee/analytics/terms_list_analytics_support.rb, line 156 def standardize_units(unit=nil,per_unit=nil) return self if homogeneous? && ((unit.nil? or (first.unit && first.unit.label == unit)) && (per_unit.nil? || (first.per_unit && first.per_unit.label == per_unit))) unit = predominant_unit if unit.nil? per_unit = predominant_per_unit if per_unit.nil? new_terms = map { |term| term.convert_unit(:unit => unit, :per_unit => per_unit) } AMEE::DataAbstraction::TermsList.new new_terms end
Returns a new instance of Result which represents the sum of all term values within the list.
Any terms within self which contain non-numeric values are ignored.
If the terms within self
do not contain consistent units, they are standardized by default to the unit (and per unit) which predominate in the list. Alternatively, the required unit and per units can be specified as arguments using the same conventions as the #standardize_units
method.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 176 def sum(unit=nil,per_unit=nil) unit = predominant_unit if unit.nil? per_unit = predominant_per_unit if per_unit.nil? value = numeric_terms.standardize_units(unit,per_unit).inject(0.0) do |sum,term| sum + term.value end template = self Result.new { label template.label; value value; unit unit; per_unit per_unit; name template.name } end
Return an instance of TermsList containing only terms labelled :type.
This method overrides the standard type
method (which is deprecated) and mimics the functionality provied by the first method_missing
method in dynamically retrieving a subset of terms according their labels.
# File lib/amee/analytics/terms_list_analytics_support.rb, line 330 def type AMEE::DataAbstraction::TermsList.new select{ |x| x.label == :type } end