- def get_retention(profiles, sessions, observation_date, horizon_days, dimensions = [], ignore_horizon = False):
- # добавляем столбец payer в передаваемый dimensions список
- dimensions = ['payer'] + dimensions
- # исключаем пользователей, не «доживших» до горизонта анализа
- # тех, которые присоединились позже, чем observation_date - horizon
- last_suitable_acquisition_date = observation_date
- if not ignore_horizon:
- last_suitable_acquisition_date = observation_date - timedelta(days = horizon_days - 1)
- result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
- # собираем «сырые» данные для расчёта удержания
- result_raw = result_raw.merge(sessions[['user_id', 'session_start']], on = 'user_id', how = 'left')
- result_raw['lifetime'] = (result_raw['session_start'] - result_raw['first_ts']).dt.days
- # функция для группировки таблицы по желаемым признакам
- def group_by_dimensions(df, dims, horizon_days):
- result = df.pivot_table(index = dims, columns = 'lifetime', values = 'user_id', aggfunc = 'nunique') # строим «треугольную таблицу»
- cohort_sizes = df.groupby(dims).agg({'user_id': 'nunique'}).rename(columns = {'user_id': 'cohort_size'}) # определяем размеры когорт
- result = cohort_sizes.merge(result, on = dims, how = 'left').fillna(0) # присоединяем размеры когорт к «треугольной» таблице
- result = result.div(result['cohort_size'], axis = 0) # делим каждую «ячейку» на соответствующий размер когорты и получаем retention rate
- result = result[['cohort_size'] + list(range(horizon_days))] # исключаем все лайфтаймы, превышающие горизонт анализа
- result['cohort_size'] = cohort_sizes # восстанавливаем размеры когорт
- return result
- # получаем таблицу удержания
- result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
- # получаем таблицу динамики удержания
- result_in_time = group_by_dimensions(result_raw, dimensions + ['dt'], horizon_days)
- # возвращаем обе таблицы и сырые данные
- # сырые данные пригодятся, если нужно будет отыскать ошибку в расчётах
- return result_raw, result_grouped, result_in_time
- def get_conversion(
- profiles,
- purchases,
- observation_date,
- horizon_days,
- dimensions=[],
- ignore_horizon=False,
- ):
- # исключаем пользователей, не «доживших» до горизонта анализа
- last_suitable_acquisition_date = observation_date
- if not ignore_horizon:
- last_suitable_acquisition_date = observation_date - timedelta(
- days=horizon_days - 1
- )
- result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
- # определяем дату и время первой покупки для каждого пользователя
- first_purchases = (
- purchases.sort_values(by=['user_id', 'event_dt'])
- .groupby('user_id')
- .agg({'event_dt': 'first'})
- .reset_index()
- )
- # добавляем данные о покупках в профили
- result_raw = result_raw.merge(
- first_purchases[['user_id', 'event_dt']], on='user_id', how='left'
- )
- # рассчитываем лайфтайм для каждой покупки
- result_raw['lifetime'] = (
- result_raw['event_dt'] - result_raw['first_ts']
- ).dt.days
- # группируем по cohort, если в dimensions ничего нет
- if len(dimensions) == 0:
- result_raw['cohort'] = 'All users'
- dimensions = dimensions + ['cohort']
- # функция для группировки таблицы по желаемым признакам
- def group_by_dimensions(df, dims, horizon_days):
- # строим «треугольную» таблицу конверсии
- result = df.pivot_table(
- index=dims, columns='lifetime', values='user_id', aggfunc='nunique'
- )
- # считаем сумму с накоплением для каждой строки
- result = result.fillna(0).cumsum(axis = 1)
- # вычисляем размеры когорт
- cohort_sizes = (
- df.groupby(dims)
- .agg({'user_id': 'nunique'})
- .rename(columns={'user_id': 'cohort_size'})
- )
- # добавляем размеры когорт в таблицу конверсии
- result = cohort_sizes.merge(result, on=dims, how='left').fillna(0)
- # делим каждую «ячейку» в строке на размер когорты
- # и получаем conversion rate
- result = result.div(result['cohort_size'], axis=0)
- # исключаем все лайфтаймы, превышающие горизонт анализа
- result = result[['cohort_size'] + list(range(horizon_days))]
- # восстанавливаем размеры когорт
- result['cohort_size'] = cohort_sizes
- return result
- # получаем таблицу конверсии
- result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
- # для таблицы динамики конверсии убираем 'cohort' из dimensions
- if 'cohort' in dimensions:
- dimensions = []
- # получаем таблицу динамики конверсии
- result_in_time = group_by_dimensions(
- result_raw, dimensions + ['dt'], horizon_days
- )
- # возвращаем обе таблицы и сырые данные
- return result_raw, result_grouped, result_in_time