Подсчет LT и график Rolling Retention

Всем привет.

Мне понадобилось на работе подсчитать LT и построить график Rolling Retention. После небольшого исследования, я поняла, что тема является насущной и неплохо было бы написать обо всех шагах, дабы кому-то это обязательно пригодится.

В основном, я опиралась на пример от Марии Мансуровой и библиотеку, написанную Darshil Desai и добавила кое-что свое.
https://nbviewer.jupyter.org/github/miptgirl/misc_code/blob/master/webinar_case.ipynb
medium.com/analytics-vidhya/user-retention-in-python-8c33fa5766b6

Теория по подсчету LT хорошо написана здесь и здесь.

Итак, я выбрала следующие пути:

  1. Я взяла когорты пользователей — зарегистрировавшихся с 1 Января по 31 Января 2020, c 1 Февраля по 29 Февраля и т.д. вплоть до Апреля. Посчитала LT в месяцах, предварительно выкинув пользователей 'проживших' один день. То есть внесших депозит и больше не появляющихся в какой-либо день за полгода.
  2. Я брала всех пользователей, даже тех кто прожил 1 день и рассчитывала когорты по неделям среди всех, зарегистрировавшихся с 1 января по 29 февраля 2020 года.

Я строила Rolling Retention. Его основное отличие от классического Retention в том, что в данном случае, смотрится первая дата активности и последняя, и считается, что пользователь заходил на сайт каждый месяц/неделю/день.

Итак, 1-ый способ


Для начала введем код в ячейку в Jupyter Notebook и установим следующую библиотеку:

!pip install calculate-retention

Данная библиотека была написана инженером Darshil Desai.
Подключим необходимые библиотеки:

from calculate_retention import CalculateRetention
import requests
import pandas as pd
import numpy as np
from yaml import load
from functools import reduce
import xlsxwriter
from numpy import array
import matplotlib.pyplot as plt

import seaborn as sns

from datetime import date, timedelta
import datetime as dt
import warnings

Считаем файл:

data5 = pd.read_excel('firstdep2.xlsx')
data5  = pd.DataFrame(data5)

data5 = data5[(data5['date_firs_dep'] >= datetime.date(2020, 1, 1)) & (data5['date_firs_dep'] < datetime.date(2020, 3, 1)) ]
data5['Последняя активность'] = pd.to_datetime(data5['Последняя активность'])

data5['Последняя активность'] = data5['Последняя активность'].map(lambda t: t.strftime("%m/%d/%Y"))
data5['Последняя активность'] = pd.to_datetime(data5['Последняя активность'])
data5.dropna().head() 


Первые 5 строк выглядят так:

IdUser	       date_firs_dep	Последняя активность
1              	2020-02-27 	2020-06-27
2              	2020-01-24 	2020-07-04
3              	2020-02-10 	2020-03-03
4              	2020-01-02       2020-01-24
5	                2020-02-26 	2020-07-02

Далее, я выделяю пользователей, которые зарегестрировались с Января по Апрель, переименовываю колонки и убираю всех, «проживших» один день.

data5 = data5[(data5['date_firs_dep'] >= datetime.date(2020, 1, 1)) & (data5['date_firs_dep'] < datetime.date(2020, 4, 1)) ]

data5.rename(columns={'IdUser': 'user_id', 'date_firs_dep': 'created_at','Последняя активность': 'end_time' }, inplace=True)
data5 = data5[['user_id','created_at', 'end_time']]

data5['created_at'] = pd.to_datetime(data5['created_at'])
data5['end_time'] = pd.to_datetime(data5['end_time'])

data5['created_at'] = data5['created_at'].map(lambda t: t.strftime("%m/%d/%Y"))
data5['end_time'] = data5['end_time'].map(lambda t: t.strftime("%m/%d/%Y"))

data5['created_at'] = pd.to_datetime(data5['created_at'])
data5['end_time'] = pd.to_datetime(data5['end_time'])

data5 = data5[data5['created_at'] < data5['end_time']]

data5.head()

Здесь, в функции выбираю режим 'monthly' и строю график:

retention_data = CalculateRetention(data5, 'monthly')
retention_data.plot_retention('raw')

Вывожу таблицу:

 z = retention_data.get_raw_retention()
z.fillna('')

Строю график Rolling Retention:

p = retention_data.compile_percentages()
p = p.fillna('')
plotly_df2(p.T, title = 'Rolling retention')

Считаю LT в днях:

import numpy as np
from scipy.integrate import simps
from numpy import trapz
LT = []
x = np.arange(len(p))

for column in p.columns:
    
    y = p['{}'.format(column)].fillna(0)
    
    lt = trapz(y,x, dx=1/4)/100*30
    LT.append(lt)
p = p.T.apply(lambda x: pd.to_numeric(x), axis=0)

2-ой способ


Второй способ я подсмотрела у Марии Мансуровой, немного его преобразовав:


data  = pd.DataFrame(data)

data5 = data[(data['date_firs_dep'] >= datetime.date(2020, 1, 1)) & (data['date_firs_dep'] < datetime.date(2020, 3, 1))]
data5['date_firs_dep'] = pd.to_datetime(data5['date_firs_dep'])
data5['min_date'] = data5['date_firs_dep'].dt.to_period('W').dt.to_timestamp()
data5['Последняя активность'] = pd.to_datetime(data5['Последняя активность'])
data5['week_num_left'] = data5['Последняя активность'].dt.to_period('W').dt.to_timestamp().dt.week
data5['week_num_start'] = data5['date_firs_dep'].dt.to_period('W').dt.to_timestamp().dt.week
data5['weeks_visited']  = data5[['week_num_left','week_num_start']].apply(lambda x: x['week_num_left']-x['week_num_start'], axis=1)
data5['weeks_visited_range'] = data5['weeks_visited'].apply(lambda x:range(0,x))
data5['date_firs_dep'] = data['date_firs_dep'].dt.date
data5.head()

data5 = data5.sort_values(by='weeks_visited')
data5 = data5.explode('weeks_visited_range')
data5['weeks_visited_range'] = data5['weeks_visited_range'].fillna(0)
data5['clients'] = 1
roll_ret_df = data5.pivot_table(index = 'min_date', 
                                          values = 'clients' , 
                                          columns = 'weeks_visited_range', aggfunc=np.sum).fillna(0).T
roll_ret_df_norm = roll_ret_df.apply(lambda x: 100*x/roll_ret_df.loc[0], axis = 1).applymap(lambda x: x if x!=0 else None)
roll_ret_df_norm = roll_ret_df_norm.fillna(0)
roll_ret_df_norm.plot()
plt.ylabel('% of users')
plt.title('Rolling Retention')

Теперь найдем площадь под кривой для каждой отдельной когорты. Как известно, из курса школьной математики, площадь под кривой — это интеграл. Посчитала LT для каждой когорты:

LT = []
x = np.arange(len(roll_ret_df_norm))

for column in roll_ret_df_norm.columns:
    
    y = roll_ret_df_norm['{}'.format(column)].fillna(0)
    
    
    lt = trapz(y,x, dx=1/4)/100*7
    LT.append(lt)  

LT

Здесь, я усреднила по всем когортам и посчитала среднее LT:

roll_ret_df_norm['mean'] = roll_ret_df_norm.mean(axis=1)
roll_ret_df_norm
roll_ret_df_norm['mean'].plot()
plt.ylabel('% of users')
plt.title('Rolling Retention')
x = np.arange(len(roll_ret_df_norm))
y = roll_ret_df_norm['mean']
    
lt = trapz(y,x, dx=1/4)/100*7
lt
Tags:
LT, retention

You can't comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author's username will be hidden by an alias.