Module: Crunchr::ClassMethods

Defined in:
lib/crunchr.rb

Instance Method Summary collapse

Instance Method Details

#as_table(list, opts = {}) ⇒ Object

pass in a list off data-objects with and get a nice table

list = [ Object.data({ doors: 1, keys: 2}),
         Object.data({ doors: 1, keys: 3 },
         ...
       ]

table = Object.as_table(list, keys: %w[doors keys])
# => [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 3, 8 ] ]

Or use lists in lists

deep_list = [ list, list list ]
table = Object.as_table(
  deep_list, keys: %[doors keys], list_operator: delta
)
# => [ [ 2, 6 ] ]  (difference of max and min for both doors and keys)

Usage with dates/times

If you include Crunchr into something Active-Modely that has ‘created_at’ as a (sane) attribute, you can supply a :date key, it will add a column with the value of created_at into the table. If you do not supply :date_fmt, it will call #to_date on the column

Parameters:

  • list (Array)

    List (1d or 2d) of data objects

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

    Options

Options Hash (opts):

  • keys (Array)

    List of keys to fetch, may contain calculations, eg: [‘doors’, ‘keys’, ‘doors / keys’]

  • list_operator (Symbol)

    With a 2d list, what operator to apply to each given list to determine the 1d value see #delta for more info

  • date_fmt (String)

    Use as input to #strftime for the value in the date column

  • str_fmt (String)

    Use as input to #sprintf for the value in every column. (Cannot be used together with :delta)

  • delta (Boolean)

    After the first row, fill every other row with the difference to the previous row. (Cannot be used with :str_fmt)



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/crunchr.rb', line 163

def as_table(list, opts = {})
  keys = opts[:keys] || raise("Need keys")

  if opts[:delta] && opts[:str_fmt]
    raise ":delta and :str_fmt cannot be supplied together"
  end

  table = []

  list.each do |statistic|
    iteration_keys = keys.dup

    if statistic.is_a?(Array)
      (iteration_keys, statistic) = flatten(statistic, opts)
    end

    row = []

    iteration_keys.each do |key|
      value = zero()

      if key == :date
        value = opts[:date_fmt] ?
          statistic.created_at.strftime(opts[:date_fmt]) :
          statistic.created_at.to_date

      else
        value = statistic.fetch(key)

        if value.respond_to? :round
          value = case opts[:round]
                  when nil
                    value
                  when 0
                    value.round rescue value
                  else
                    value.round(opts[:round])
                  end
        end

        value = opts[:str_fmt] % value if opts[:str_fmt]

        value = value.to_f if value.is_a?(BigDecimal)
      end

      row << checked(value)
    end

    if opts[:delta] && table.any?
      new_row = []
      row.each_with_index do |value, idx|
        next unless value.kind_of?(Numeric)
        new_row[idx] = checked(row[idx] - @prev[idx])
      end

      @prev = row.dup
      row = new_row
    else
      @prev = row
    end

    table << row
  end

  return table
end

#checked(value) ⇒ Float, Integer

Make sure the value is zero if it is NaN, infinite, or nil Turn the value into a float if it is a BigDecimal

Parameters:

  • value

    The value to check

Returns:

  • (Float, Integer)

    the improved value



298
299
300
301
302
303
304
305
# File 'lib/crunchr.rb', line 298

def checked(value)
  value = zero() if value.respond_to?(:nan?) && value.nan?
  value = zero() if value.respond_to?(:infinity?) && value.infinity?
  value = zero() if value.nil?
  value = value.to_f if value.is_a? BigDecimal

  value
end

#flatten(array, opts) ⇒ Object

flatten an array of rows by applying an operator vertically on each column and accepting the result as a single row

Parameters:

  • array (Array)

    List of lists

  • opts (Hash)

    Options

Options Hash (opts):

  • list_operator (Symbol)

    What operator to apply to the array to get a single value, defaults to :mean, should be any of

    - :mean
    - :stddev
    - :median
    - :range
    - :mode
    - :sum
    - :min
    - :max
    - :delta (takes the difference of max and min)
    


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/crunchr.rb', line 247

def flatten(array, opts)
  keys = opts[:keys].dup

  # this must be an interval period : find the mean, sum, max, whatever
  opts[:list_operator] ||= :mean

  collection = self.new( :data => {} )

  keys.each_with_index do |key, idx|
    if key == :date
      collection.created_at = array.first.created_at
      next
    end

    collection_key = key.to_s.gsub(/[\s*\/:x+-]+/, '_')
    keys[idx] = collection_key if collection_key != key

    array.each do |item|
      collection.data[collection_key] ||= []
      collection.data[collection_key] << (item.fetch(key) || 0)
    end

    # turn the collection into a single value
    value = if opts[:list_operator] == :delta
      collection.data[collection_key].max -
      collection.data[collection_key].min

    else
       collection.data[collection_key].send(opts[:list_operator])

    end

    collection.data[collection_key] = value
  end

  collection
  collection.readonly! if collection.respond_to?(:readonly!)

  return [keys, collection]
end

#zeroObject

Return a BigDecimal zero value



289
290
291
# File 'lib/crunchr.rb', line 289

def zero
  BigDecimal.new("0.0")
end