Skip to content

Instantly share code, notes, and snippets.

@nandilugio
Created December 12, 2013 10:44
Show Gist options
  • Save nandilugio/c54c2313d43d6a6cace5 to your computer and use it in GitHub Desktop.
Save nandilugio/c54c2313d43d6a6cace5 to your computer and use it in GitHub Desktop.
Retention metric
ActiveRecord::Base.logger = Logger.new "/dev/null"
########################################################################################################################
class RetentionCalculation
def users_who_bought_in_range(from, to)
User.joins(:orders).where(
orders: { confirmed_at: from..to }
).merge(Order.not_zero).merge(Order.excluding_special_users).merge(Order.not_by_salesman).pluck(:id).uniq
end
def users_who_bought_until(til)
User.joins(:orders).where(
Order.arel_table[:confirmed_at].lt(til)
).merge(Order.not_zero).merge(Order.excluding_special_users).merge(Order.not_by_salesman).pluck(:id).uniq
end
def retention_for_range(from, to)
users_who_bought_in_range = users_who_bought_in_range(from, to)
users_at_risk = users_who_bought_until(from)
((users_who_bought_in_range & users_at_risk).size.to_f / users_at_risk.size.to_f) * 100.0
end
def monthly_retention(month_n)
from = Time.new(2013, month_n, 01)
to = from + 1.month
retention_for_range(from, to)
end
def average_daily_retention_for_month(month_n, year_n = Time.now.year)
range = 30.day
daily_data = []
(1..Time.days_in_month(month_n, year_n)).each do |day_n|
day = Time.new(year_n, month_n, day_n)
daily_data << retention_for_range(day - range, day)
end
daily_data.sum / daily_data.size.to_f
end
end
########################################################################################################################
class RetentionCalculationWithFakeEvents < RetentionCalculation
def initialize(events)
super()
@events = events
end
def users_who_bought_in_range(from, to)
users = super
@events.each do |event_time, add_count|
if (from..to).cover?(event_time)
users = add_fake_users(users, add_count)
end
end
users
end
def users_who_bought_until(til)
users = super
@events.each do |event_time, add_count|
if event_time < til
users = add_fake_users(users, add_count)
end
end
users
end
def add_fake_users(user_id_list, add_count)
@first_fake_id ||= User.maximum(:id) * 10
user_id_list + (@first_fake_id..(@first_fake_id + add_count - 1)).to_a
end
end
########################################################################################################################
puts "Month, Monthly retention, Daily retention average, (sim) Monthly retention, (sim) Daily retention average"
rc = RetentionCalculation.new
rcwfe = RetentionCalculationWithFakeEvents.new([
[Time.new(2013,2,15), 10000],
[Time.new(2013,7,15), 1000],
])
1.upto(11).each do |month|
puts "#{month},#{rc.monthly_retention(month).round(2)},#{rc.average_daily_retention_for_month(month).round(2)},#{rcwfe.monthly_retention(month).round(2)},#{rcwfe.average_daily_retention_for_month(month).round(2)}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment