Untitled

From Big Pig, 4 Days ago, written in Plain Text, viewed 14 times.
URL http://codebin.org/view/3da70178 Embed
Download Paste or View Raw
  1. def get_ltv(
  2.     profiles,  # Шаг 1. Получить профили и данные о покупках
  3.     purchases,
  4.     observation_date,
  5.     horizon_days,
  6.     dimensions=[],
  7.     ignore_horizon=False,
  8. ):
  9.  
  10.     # исключаем пользователей, не «доживших» до горизонта анализа
  11.     last_suitable_acquisition_date = observation_date
  12.     if not ignore_horizon:
  13.         last_suitable_acquisition_date = observation_date - timedelta(
  14.             days=horizon_days - 1
  15.         )
  16.     result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
  17.  
  18.     # Шаг 2. Добавить данные о покупках в профили
  19.  
  20.     result_raw = result_raw.merge(
  21.         # добавляем в профили время совершения покупок и выручку
  22.         purchases[['user_id', 'event_dt', 'revenue']],
  23.         on='user_id',
  24.         how='left',
  25.     )
  26.  
  27.     # Шаг 3. Рассчитать лайфтайм пользователя для каждой покупки
  28.     result_raw['lifetime'] = (
  29.         result_raw['event_dt'] - result_raw['first_ts']
  30.     ).dt.days
  31.  
  32.     # группируем по cohort, если в dimensions ничего нет
  33.     if len(dimensions) == 0:
  34.         result_raw['cohort'] = 'All users'
  35.         dimensions = dimensions + ['cohort']
  36.  
  37.     # функция для группировки таблицы по желаемым признакам
  38.     def group_by_dimensions(df, dims, horizon_days):
  39.  
  40.         # Шаг 4. Построить таблицу выручки
  41.         # строим «треугольную» таблицу
  42.         result = df.pivot_table(
  43.             index=dims,
  44.             columns='lifetime',
  45.             values='revenue',  # в ячейках — выручка за каждый лайфтайм
  46.             aggfunc='sum',
  47.         )
  48.  
  49.         # Шаг 5. Посчитать сумму выручки с накоплением
  50.         result = result.fillna(0).cumsum(axis=1)
  51.  
  52.         # Шаг 6. Вычислить размеры когорт
  53.         cohort_sizes = (
  54.             df.groupby(dims)
  55.             .agg({'user_id': 'nunique'})
  56.             .rename(columns={'user_id': 'cohort_size'})
  57.         )
  58.  
  59.         # Шаг 7. Объединить размеры когорт и таблицу выручки
  60.         result = cohort_sizes.merge(result, on=dims, how='left').fillna(0)
  61.  
  62.         # Шаг 8. Посчитать LTV
  63.         # делим каждую «ячейку» в строке на размер когорты
  64.         result = result.div(result['cohort_size'], axis=0)
  65.         # исключаем все лайфтаймы, превышающие горизонт анализа
  66.         result = result[['cohort_size'] + list(range(horizon_days))]
  67.         # восстанавливаем размеры когорт
  68.         result['cohort_size'] = cohort_sizes
  69.         return result
  70.  
  71.     # получаем таблицу LTV
  72.     result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
  73.  
  74.     # для таблицы динамики LTV убираем 'cohort' из dimensions
  75.     if 'cohort' in dimensions:
  76.         dimensions = []
  77.     # получаем таблицу динамики LTV
  78.     result_in_time = group_by_dimensions(
  79.         result_raw, dimensions + ['dt'], horizon_days
  80.     )
  81.  
  82.     # возвращаем обе таблицы LTV и сырые данные
  83.     return result_raw, result_grouped, result_in_time
  84.  
  85.  
  86.  
  87.  
  88. # функция для визуализации LTV и ROI
  89.  
  90. def plot_ltv_roi(ltv, ltv_history, roi, roi_history, horizon, window=14):
  91.  
  92.     # задаём сетку отрисовки графиков
  93.     plt.figure(figsize=(20, 10))
  94.  
  95.     # из таблицы ltv исключаем размеры когорт
  96.     ltv = ltv.drop(columns=['cohort_size'])
  97.     # в таблице динамики ltv оставляем только нужный лайфтайм
  98.     ltv_history = ltv_history.drop(columns=['cohort_size'])[[horizon - 1]]
  99.  
  100.     # стоимость привлечения запишем в отдельный фрейм
  101.     cac_history = roi_history[['cac']]
  102.  
  103.     # из таблицы roi исключаем размеры когорт и cac
  104.     roi = roi.drop(columns=['cohort_size', 'cac'])
  105.     # в таблице динамики roi оставляем только нужный лайфтайм
  106.     roi_history = roi_history.drop(columns=['cohort_size', 'cac'])[
  107.         [horizon - 1]
  108.     ]
  109.  
  110.     # первый график — кривые ltv
  111.     ax1 = plt.subplot(2, 3, 1)
  112.     ltv.T.plot(grid=True, ax=ax1)
  113.     plt.legend()
  114.     plt.xlabel('Лайфтайм')
  115.     plt.title('LTV')
  116.  
  117.     # второй график — динамика ltv
  118.     ax2 = plt.subplot(2, 3, 2, sharey=ax1)
  119.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  120.     columns = [name for name in ltv_history.index.names if name not in ['dt']]
  121.     filtered_data = ltv_history.pivot_table(
  122.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  123.     )
  124.     filter_data(filtered_data, window).plot(grid=True, ax=ax2)
  125.     plt.xlabel('Дата привлечения')
  126.     plt.title('Динамика LTV пользователей на {}-й день'.format(horizon))
  127.  
  128.     # третий график — динамика cac
  129.     ax3 = plt.subplot(2, 3, 3, sharey=ax1)
  130.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  131.     columns = [name for name in cac_history.index.names if name not in ['dt']]
  132.     filtered_data = cac_history.pivot_table(
  133.         index='dt', columns=columns, values='cac', aggfunc='mean'
  134.     )
  135.     filter_data(filtered_data, window).plot(grid=True, ax=ax3)
  136.     plt.xlabel('Дата привлечения')
  137.     plt.title('Динамика стоимости привлечения пользователей')
  138.  
  139.     # четвёртый график — кривые roi
  140.     ax4 = plt.subplot(2, 3, 4)
  141.     roi.T.plot(grid=True, ax=ax4)
  142.     plt.axhline(y=1, color='red', linestyle='--', label='Уровень окупаемости')
  143.     plt.legend()
  144.     plt.xlabel('Лайфтайм')
  145.     plt.title('ROI')
  146.  
  147.     # пятый график — динамика roi
  148.     ax5 = plt.subplot(2, 3, 5, sharey=ax4)
  149.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  150.     columns = [name for name in roi_history.index.names if name not in ['dt']]
  151.     filtered_data = roi_history.pivot_table(
  152.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  153.     )
  154.     filter_data(filtered_data, window).plot(grid=True, ax=ax5)
  155.     plt.axhline(y=1, color='red', linestyle='--', label='Уровень окупаемости')
  156.     plt.xlabel('Дата привлечения')
  157.     plt.title('Динамика ROI пользователей на {}-й день'.format(horizon))
  158.  
  159.     plt.tight_layout()
  160.     plt.show()
  161.  
  162.  
  163.  
  164.  
  165. observation_date = datetime(2019, 11, 1).date()  # момент анализа
  166. horizon_days = 14  # горизонт анализа
  167.  
  168.  
  169.  
  170.  
  171. # считаем LTV и ROI
  172. ltv_raw, ltv_grouped, ltv_history, roi_grouped, roi_history = get_ltv(
  173.     profiles, orders, observation_date, horizon_days
  174. )
  175.  
  176. # строим графики
  177. plot_ltv_roi(ltv_grouped, ltv_history, roi_grouped, roi_history, horizon_days)
  178.  
  179. ---------------------------------------------------------------------------
  180. ValueError                                Traceback (most recent call last)
  181. /tmp/ipykernel_48/39697251.py in <module>
  182.       1 # считаем LTV и ROI
  183. ----> 2 ltv_raw, ltv_grouped, ltv_history, roi_grouped, roi_history = get_ltv(
  184.       3     profiles, orders, observation_date, horizon_days
  185.       4 )
  186.       5
  187.  
  188. ValueError: not enough values to unpack (expected 5, got 3)
  189.  

Reply to "Untitled"

Here you can reply to the paste above