class ActiveCohort

Public: Provides a cohort analysis of a set of ActiveRecord objects. Intended to be consumed by domain-specific classes, such AigAnalyst, OrderAnalyst, etc. See the constructor’s documentation for information on the options hash.

Examples

cohort = ActiveCohort.new(some_options_hash)
cohort.generate_report
# => [["", "Week 0", "Week 1", "Week 2", "Week 3", "Week 4", "Week 5"],
     ["1/9", "27.0%", "8.1%", "2.7%", "0.0%", "0.0%", "0.0%"],
     ["1/16", "37.9%", "7.6%", "0.0%", "0.0%", "0.0%"],
     ["1/23", "42.2%", "3.1%", "0.0%", "0.0%"],
     ["1/30", "31.8%", "0.0%", "0.0%"],
     ["2/6", "-", "-"]]

puts cohort.to_csv
# => ,Week 0,Week 1,Week 2,Week 3,Week 4,Week 5
     1/9,27.0%,8.1%,2.7%,0.0%,0.0%,0.0%
     1/16,37.9%,7.6%,0.0%,0.0%,0.0%
     1/23,42.2%,3.1%,0.0%,0.0%
     1/30,31.8%,0.0%,0.0%
     2/6,-,-

Attributes

activation_lambda[RW]
interval_timestamp_field[W]
start_at[W]
subject_collection[RW]

Public Class Methods

new(subject_collection, activation_lambda, opts={}) click to toggle source

Public: Initialize a ActiveCohort.

Required params

subject_collection          - An ActiveRecord collection of records to perform a
                              cohort analysis on.
activation_lambda           - A lambda that returns a boolean indicating whether
                              a given record has activated (e.g., converted,
                              signed up, purchased, etc.)
opts                        - A String naming the widget.
  start_at                  - The date at which to begin the analysis.
                              Default: 30 days ago.
  interval                  - A string representation of the interval to run the analysis
                              over (e.g, day, week, etc.) For instance, 'week' would
                              result in a week-over-week analysis.
                              Default: 'day'.
  interval_timestamp_field  - A String representation of the timestamp
                              field on the cohort records to be used to
                              offset between intervals.
                              Default: 'created_at'.
# File lib/active_cohort.rb, line 46
def initialize(subject_collection, activation_lambda, opts={})
  @subject_collection = subject_collection
  @activation_lambda = activation_lambda
  opts.each { |k,v| instance_variable_set("@#{k}", v) }
end

Public Instance Methods

generate_report() click to toggle source

Public: Generates a cohort report using params supplied to the instance in the constructor.

Example

cohort.generate_report
# => [["", "Week 0", "Week 1", "Week 2", "Week 3", "Week 4", "Week 5"],
     ["1/9", "27.0%", "8.1%", "2.7%", "0.0%", "0.0%", "0.0%"],
     ["1/16", "37.9%", "7.6%", "0.0%", "0.0%", "0.0%"],
     ["1/23", "42.2%", "3.1%", "0.0%", "0.0%"],
     ["1/30", "31.8%", "0.0%", "0.0%"],
     ["2/6", "-", "-"]]

Returns an Array of values representing the report.

# File lib/active_cohort.rb, line 85
def generate_report
  validate_required_fields
  @report = []
  @report << header

  (number_of_intervals - 1).times do |row|
    @report << build_row(row)
  end
  @report
end
interval() click to toggle source
# File lib/active_cohort.rb, line 52
def interval
  @interval || 'day'
end
interval=(interval) click to toggle source
# File lib/active_cohort.rb, line 56
def interval=(interval)
  unless interval.downcase.in? valid_intervals
    raise "The interval \"#{interval}\" isn't valid.\n" +
          "Use #{valid_intervals.join ', '}"
  end
  @interval = interval.downcase
end
interval_timestamp_field() click to toggle source
# File lib/active_cohort.rb, line 68
def interval_timestamp_field
  @interval_timestamp_field || 'created_at'
end
start_at() click to toggle source
# File lib/active_cohort.rb, line 64
def start_at
  @start_at || 30.days.ago
end
to_csv(seperator=',') click to toggle source

Public: Outputs the cohort report in CSV format. Does not regenerate the report if the instance has already generated it.

Example

puts cohort.to_csv
# => ,Week 0,Week 1,Week 2,Week 3,Week 4,Week 5
     1/9,27.0%,8.1%,2.7%,0.0%,0.0%,0.0%
     1/16,37.9%,7.6%,0.0%,0.0%,0.0%
     1/23,42.2%,3.1%,0.0%,0.0%
     1/30,31.8%,0.0%,0.0%
     2/6,-,-

Returns a String representation of the report with CSV formatting.

# File lib/active_cohort.rb, line 109
def to_csv(seperator=',')
  report = @report || generate_report
  report.map{ |row| row.join(seperator) }.join("\n")
end

Private Instance Methods

assemble_cohort(start_date, end_date) click to toggle source
# File lib/active_cohort.rb, line 131
def assemble_cohort(start_date, end_date)
  @subject_collection.where(
    @interval_timestamp_field.to_sym => start_date..end_date
  )
end
build_row(row) click to toggle source
# File lib/active_cohort.rb, line 148
def build_row(row)
  row_values = []
  row_offset = row.send(:"#{interval}")
  cohort_start_date = (start_at + row_offset).send(:"beginning_of_#{interval}")
  cohort_end_date = cohort_start_date.send(:"end_of_#{interval}")
  cohort = assemble_cohort cohort_start_date, cohort_end_date
  row_values << cohort_start_date.strftime("%-m/%-d")
  (number_of_intervals - row).times do |col|
    activation_start_date = start_date_for_cell(row, col)
    activation_end_date = activation_start_date.send(:"end_of_#{interval}")
    activated = cohort.select { |c| @activation_lambda.call(c, activation_start_date, activation_end_date) }
    row_values << percentage_as_string(activated.length, cohort.length)
  end
  row_values
end
header() click to toggle source
# File lib/active_cohort.rb, line 115
def header
  header = ['']
  number_of_intervals.times do |i|
    header << "#{interval.capitalize} #{i}"
  end
  header
end
number_of_intervals() click to toggle source
# File lib/active_cohort.rb, line 123
def number_of_intervals
  @interval == 'day' ? 30 : 6
end
percentage_as_string(numerator, denominator) click to toggle source
# File lib/active_cohort.rb, line 137
def percentage_as_string(numerator, denominator)
  return "-" if denominator.zero?
  "#{((numerator / denominator.to_f) * 100).round(1)}%"
end
start_date_for_cell(row, col) click to toggle source
# File lib/active_cohort.rb, line 142
def start_date_for_cell(row, col)
  row_offset = row.send(:"#{interval}")
  col_offset = col.send(:"#{interval}")
  (start_at + row_offset + col_offset).send(:"beginning_of_#{interval}")
end
valid_intervals() click to toggle source
# File lib/active_cohort.rb, line 127
def valid_intervals
  %w(day week month)
end
validate_required_fields() click to toggle source
# File lib/active_cohort.rb, line 164
def validate_required_fields
  raise "Missing subject_collection" unless subject_collection.present?
  raise "Missing activation_lambda" unless activation_lambda.present?
end