practicum

From mariam, 1 Month ago, written in Python, viewed 106 times.
URL http://codebin.org/view/c8799276 Embed
Download Paste or View Raw
  1. # функция для создания пользовательских профилей
  2.  
  3. def get_profiles(sessions, orders, ad_costs):
  4.  
  5.     # находим параметры первых посещений
  6.     profiles = (
  7.         sessions.sort_values(by=['user_id', 'session_start'])
  8.         .groupby('user_id')
  9.         .agg(
  10.             {
  11.                 'session_start': 'first',
  12.                 'channel': 'first',
  13.                 'device': 'first',
  14.                 'region': 'first',
  15.             }
  16.         )
  17.         .rename(columns={'session_start': 'first_ts'})
  18.         .reset_index()
  19.     )
  20.  
  21.     # для когортного анализа определяем дату первого посещения
  22.     # и первый день месяца, в который это посещение произошло
  23.     profiles['dt'] = profiles['first_ts'].dt.date
  24.     profiles['dt'] = pd.to_datetime(profiles['dt'], format="%Y-%m-%d")
  25.     profiles['month'] = profiles['first_ts'].astype('datetime64[M]')
  26.  
  27.     # добавляем признак платящих пользователей
  28.     profiles['payer'] = profiles['user_id'].isin(orders['user_id'].unique())
  29.  
  30.  
  31.     # считаем количество уникальных пользователей
  32.     # с одинаковыми источником и датой привлечения
  33.     new_users = (
  34.         profiles.groupby(['dt', 'channel'])
  35.         .agg({'user_id': 'nunique'})
  36.         .rename(columns={'user_id': 'unique_users'})
  37.         .reset_index()
  38.     )
  39.  
  40.     # объединяем траты на рекламу и число привлечённых пользователей
  41.     ad_costs = ad_costs.merge(new_users, on=['dt', 'channel'], how='left')
  42.  
  43.     # делим рекламные расходы на число привлечённых пользователей
  44.     # результаты сохраним в столбец acquisition_cost (CAC)
  45.     ad_costs['acquisition_cost'] = ad_costs['costs'] / ad_costs['unique_users']
  46.  
  47.     # добавляем стоимость привлечения в профили
  48.     profiles = profiles.merge(
  49.         ad_costs[['dt', 'channel', 'acquisition_cost']],
  50.         on=['dt', 'channel'],
  51.         how='left',
  52.     )
  53.  
  54.     # стоимость привлечения органических пользователей равна нулю
  55.     profiles['acquisition_cost'] = profiles['acquisition_cost'].fillna(0)
  56.  
  57.     return profiles
  58.  
  59.  
  60. # функция для расчёта конверсии
  61.  
  62. def get_conversion(
  63.     profiles,
  64.     purchases,
  65.     observation_date,
  66.     horizon_days,
  67.     dimensions=[],
  68.     ignore_horizon=False,
  69. ):
  70.  
  71.     # исключаем пользователей, не «доживших» до горизонта анализа
  72.     last_suitable_acquisition_date = observation_date
  73.     if not ignore_horizon:
  74.         last_suitable_acquisition_date = observation_date - timedelta(
  75.             days=horizon_days - 1
  76.         )
  77.     result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
  78.  
  79.     # определяем дату и время первой покупки для каждого пользователя
  80.     first_purchases = (
  81.         purchases.sort_values(by=['user_id', 'event_dt'])
  82.         .groupby('user_id')
  83.         .agg({'event_dt': 'first'})
  84.         .reset_index()
  85.     )
  86.  
  87.     # собираем «сырые» данные для расчёта удержания
  88.     result_raw = result_raw.merge(
  89.         first_purchases[['user_id', 'event_dt']], on='user_id', how='left'
  90.     )
  91.  
  92.    
  93.     result_raw['lifetime'] = (
  94.         result_raw['event_dt'] - result_raw['first_ts']
  95.     ).dt.days
  96.  
  97.    
  98.     if len(dimensions) == 0:
  99.         result_raw['cohort'] = 'All users'
  100.         dimensions = dimensions + ['cohort']
  101.  
  102.     # функция для группировки таблицы по желаемым признакам
  103.     def group_by_dimensions(df, dims, horizon_days):
  104.         result = df.pivot_table(
  105.             index=dims, columns='lifetime', values='user_id', aggfunc='nunique'
  106.         )
  107.         result = result.fillna(0).cumsum(axis = 1)
  108.         cohort_sizes = (
  109.             df.groupby(dims)
  110.             .agg({'user_id': 'nunique'})
  111.             .rename(columns={'user_id': 'cohort_size'})
  112.         )
  113.         result = cohort_sizes.merge(result, on=dims, how='left').fillna(0)
  114.         # делим каждую «ячейку» в строке на размер когорты
  115.         # и получаем conversion rate
  116.         result = result.div(result['cohort_size'], axis=0)
  117.         result = result[['cohort_size'] + list(range(horizon_days))]
  118.         result['cohort_size'] = cohort_sizes
  119.         return result
  120.  
  121.     # получаем таблицу конверсии
  122.     result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
  123.  
  124.    
  125.     if 'cohort' in dimensions:
  126.         dimensions = []
  127.  
  128.     # получаем таблицу динамики конверсии
  129.     result_in_time = group_by_dimensions(
  130.         result_raw, dimensions + ['dt'], horizon_days
  131.     )
  132.  
  133.     # возвращаем обе таблицы и сырые данные
  134.     return result_raw, result_grouped, result_in_time
  135.  
  136. def get_ltv(
  137.     profiles,  # Шаг 1. Получить профили и данные о покупках
  138.     purchases,
  139.     observation_date,
  140.     horizon_days,
  141.     dimensions=[],
  142.     ignore_horizon=False,
  143. ):
  144.  
  145.     # исключаем пользователей, не «доживших» до горизонта анализа
  146.     last_suitable_acquisition_date = observation_date
  147.     if not ignore_horizon:
  148.         last_suitable_acquisition_date = observation_date - timedelta(
  149.             days=horizon_days - 1
  150.         )
  151.     result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
  152.  
  153.     # Шаг 2. Добавить данные о покупках в профили
  154.  
  155.     result_raw = result_raw.merge(
  156.         # добавляем в профили время совершения покупок и выручку
  157.         purchases[['user_id', 'event_dt', 'revenue']],
  158.         on='user_id',
  159.         how='left',
  160.     )
  161.  
  162.     # Шаг 3. Рассчитать лайфтайм пользователя для каждой покупки
  163.     result_raw['lifetime'] = (
  164.         result_raw['event_dt'] - result_raw['first_ts']
  165.     ).dt.days
  166.  
  167.     # группируем по cohort, если в dimensions ничего нет
  168.     if len(dimensions) == 0:
  169.         result_raw['cohort'] = 'All users'
  170.         dimensions = dimensions + ['cohort']
  171.  
  172.     # функция для группировки таблицы по желаемым признакам
  173.     def group_by_dimensions(df, dims, horizon_days):
  174.  
  175.         # Шаг 3. Построить таблицу выручки
  176.         # строим «треугольную» таблицу
  177.         result = df.pivot_table(
  178.             index=dims,
  179.             columns='lifetime',
  180.             values='revenue',  # в ячейках — выручка за каждый лайфтайм
  181.             aggfunc='sum',
  182.         )
  183.  
  184.         # Шаг 4. Посчитать сумму выручки с накоплением
  185.         result = result.fillna(0).cumsum(axis=1)
  186.  
  187.         # Шаг 5. Вычислить размеры когорт
  188.         cohort_sizes = (
  189.             df.groupby(dims)
  190.             .agg({'user_id': 'nunique'})
  191.             .rename(columns={'user_id': 'cohort_size'})
  192.         )
  193.  
  194.         # Шаг 6. Объединить размеры когорт и таблицу выручки
  195.         result = cohort_sizes.merge(result, on=dims, how='left').fillna(0)
  196.  
  197.         # Шаг 7. Посчитать LTV
  198.         # делим каждую «ячейку» в строке на размер когорты
  199.         result = result.div(result['cohort_size'], axis=0)
  200.         # исключаем все лайфтаймы, превышающие горизонт анализа
  201.         result = result[['cohort_size'] + list(range(horizon_days))]
  202.         # восстанавливаем размеры когорт
  203.         result['cohort_size'] = cohort_sizes
  204.         # сохраняем в датафрейм данные пользователей и значения CAC,
  205.         # добавив параметры из dimensions
  206.         cac = df[['user_id', 'acquisition_cost'] + dims].drop_duplicates()
  207.  
  208.         # считаем средний CAC по параметрам из dimensions
  209.         cac = (
  210.             cac.groupby(dims)
  211.             .agg({'acquisition_cost': 'mean'})
  212.             .rename(columns={'acquisition_cost': 'cac'})
  213.         )
  214.  
  215.         # считаем ROI: делим LTV на CAC
  216.         roi = result.div(cac['cac'], axis=0)
  217.  
  218.         # удаляем строки с бесконечным ROI
  219.         roi = roi[~roi['cohort_size'].isin([np.inf])]
  220.  
  221.         # восстанавливаем размеры когорт в таблице ROI
  222.         roi['cohort_size'] = cohort_sizes
  223.  
  224.         # добавляем CAC в таблицу ROI
  225.         roi['cac'] = cac['cac']
  226.  
  227.         # в финальной таблице оставляем размеры когорт, CAC
  228.         # и ROI в лайфтаймы, не превышающие горизонт анализа
  229.         roi = roi[['cohort_size', 'cac'] + list(range(horizon_days))]
  230.  
  231.         # возвращаем таблицы LTV и ROI
  232.         return result, roi
  233.  
  234.     # получаем таблицы LTV и ROI
  235.     result_grouped, roi_grouped = group_by_dimensions(
  236.         result_raw, dimensions, horizon_days
  237.     )
  238.  
  239.     # для таблиц динамики убираем 'cohort' из dimensions
  240.     if 'cohort' in dimensions:
  241.         dimensions = []
  242.  
  243.     # получаем таблицы динамики LTV и ROI
  244.     result_in_time, roi_in_time = group_by_dimensions(
  245.         result_raw, dimensions + ['dt'], horizon_days
  246.     )
  247.  
  248.     return (
  249.         result_raw,  # сырые данные
  250.         result_grouped,  # таблица LTV
  251.         result_in_time,  # таблица динамики LTV
  252.         roi_grouped,  # таблица ROI
  253.         roi_in_time,  # таблица динамики ROI
  254.     )
  255.  
  256. def get_retention(
  257.     profiles,
  258.     sessions,
  259.     observation_date,
  260.     horizon_days,
  261.     dimensions=[],
  262.     ignore_horizon=False,
  263. ):
  264.  
  265.     # добавляем столбец payer в передаваемый dimensions список
  266.     dimensions = ['payer'] + dimensions
  267.  
  268.     # исключаем пользователей, не «доживших» до горизонта анализа
  269.     last_suitable_acquisition_date = observation_date
  270.     if not ignore_horizon:
  271.         last_suitable_acquisition_date = observation_date - timedelta(
  272.             days=horizon_days - 1
  273.         )
  274.     result_raw = profiles.query('dt <= @last_suitable_acquisition_date')
  275.  
  276.     # собираем «сырые» данные для расчёта удержания
  277.     result_raw = result_raw.merge(
  278.         sessions[['user_id', 'session_start']], on='user_id', how='left'
  279.     )
  280.     result_raw['lifetime'] = (
  281.         result_raw['session_start'] - result_raw['first_ts']
  282.     ).dt.days
  283.  
  284.     # функция для группировки таблицы по желаемым признакам
  285.     def group_by_dimensions(df, dims, horizon_days):
  286.         result = df.pivot_table(
  287.             index=dims, columns='lifetime', values='user_id', aggfunc='nunique'
  288.         )
  289.         cohort_sizes = (
  290.             df.groupby(dims)
  291.             .agg({'user_id': 'nunique'})
  292.             .rename(columns={'user_id': 'cohort_size'})
  293.         )
  294.         result = cohort_sizes.merge(result, on=dims, how='left').fillna(0)
  295.         result = result.div(result['cohort_size'], axis=0)
  296.         result = result[['cohort_size'] + list(range(horizon_days))]
  297.         result['cohort_size'] = cohort_sizes
  298.         return result
  299.  
  300.     # получаем таблицу удержания
  301.     result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
  302.  
  303.     # получаем таблицу динамики удержания
  304.     result_in_time = group_by_dimensions(
  305.         result_raw, dimensions + ['dt'], horizon_days
  306.     )
  307.  
  308.     # возвращаем обе таблицы и сырые данные
  309.     return result_raw, result_grouped, result_in_time
  310.  
  311.  
  312. # функция для сглаживания фрейма
  313.  
  314. def filter_data(df, window):
  315.     # для каждого столбца применяем скользящее среднее
  316.     for column in df.columns.values:
  317.         df[column] = df[column].rolling(window).mean()
  318.     return df
  319.  
  320. # функция для визуализации удержания
  321.  
  322. def plot_retention(retention, retention_history, horizon, window=7):
  323.  
  324.     # задаём размер сетки для графиков
  325.     plt.figure(figsize=(15, 10))
  326.  
  327.     # исключаем размеры когорт и удержание первого дня
  328.     retention = retention.drop(columns=['cohort_size', 0])
  329.     # в таблице динамики оставляем только нужный лайфтайм
  330.     retention_history = retention_history.drop(columns=['cohort_size'])[
  331.         [horizon - 1]
  332.     ]
  333.  
  334.     # если в индексах таблицы удержания только payer,
  335.     # добавляем второй признак — cohort
  336.     if retention.index.nlevels == 1:
  337.         retention['cohort'] = 'All users'
  338.         retention = retention.reset_index().set_index(['cohort', 'payer'])
  339.  
  340.     # в таблице графиков — два столбца и две строки, четыре ячейки
  341.     # в первой строим кривые удержания платящих пользователей
  342.     ax1 = plt.subplot(2, 2, 1)
  343.     retention.query('payer == True').droplevel('payer').T.plot(
  344.         grid=True, ax=ax1
  345.     )
  346.     plt.legend()
  347.     plt.xlabel('Лайфтайм')
  348.     plt.title('Удержание платящих пользователей')
  349.  
  350.     # во второй ячейке строим кривые удержания неплатящих
  351.     # вертикальная ось — от графика из первой ячейки
  352.     ax2 = plt.subplot(2, 2, 2, sharey=ax1)
  353.     retention.query('payer == False').droplevel('payer').T.plot(
  354.         grid=True, ax=ax2
  355.     )
  356.     plt.legend()
  357.     plt.xlabel('Лайфтайм')
  358.     plt.title('Удержание неплатящих пользователей')
  359.  
  360.     # в третьей ячейке — динамика удержания платящих
  361.     ax3 = plt.subplot(2, 2, 3)
  362.     # получаем названия столбцов для сводной таблицы
  363.     columns = [
  364.         name
  365.         for name in retention_history.index.names
  366.         if name not in ['dt', 'payer']
  367.     ]
  368.     # фильтруем данные и строим график
  369.     filtered_data = retention_history.query('payer == True').pivot_table(
  370.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  371.     )
  372.     filter_data(filtered_data, window).plot(grid=True, ax=ax3)
  373.     plt.xlabel('Дата привлечения')
  374.     plt.title(
  375.         'Динамика удержания платящих пользователей на {}-й день'.format(
  376.             horizon
  377.         )
  378.     )
  379.  
  380.     # в чётвертой ячейке — динамика удержания неплатящих
  381.     ax4 = plt.subplot(2, 2, 4, sharey=ax3)
  382.     # фильтруем данные и строим график
  383.     filtered_data = retention_history.query('payer == False').pivot_table(
  384.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  385.     )
  386.     filter_data(filtered_data, window).plot(grid=True, ax=ax4)
  387.     plt.xlabel('Дата привлечения')
  388.     plt.title(
  389.         'Динамика удержания неплатящих пользователей на {}-й день'.format(
  390.             horizon
  391.         )
  392.     )
  393.    
  394.     plt.tight_layout()
  395.     plt.show()
  396.  
  397.  
  398. # функция для визуализации конверсии
  399.  
  400. def plot_conversion(conversion, conversion_history, horizon, window=7):
  401.  
  402.     # задаём размер сетки для графиков
  403.     plt.figure(figsize=(15, 5))
  404.  
  405.     # исключаем размеры когорт
  406.     conversion = conversion.drop(columns=['cohort_size'])
  407.     # в таблице динамики оставляем только нужный лайфтайм
  408.     conversion_history = conversion_history.drop(columns=['cohort_size'])[
  409.         [horizon - 1]
  410.     ]
  411.  
  412.     # первый график — кривые конверсии
  413.     ax1 = plt.subplot(1, 2, 1)
  414.     conversion.T.plot(grid=True, ax=ax1)
  415.     plt.legend()
  416.     plt.xlabel('Лайфтайм')
  417.     plt.title('Конверсия пользователей')
  418.  
  419.     # второй график — динамика конверсии
  420.     ax2 = plt.subplot(1, 2, 2, sharey=ax1)
  421.     columns = [
  422.         # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  423.         name for name in conversion_history.index.names if name not in ['dt']
  424.     ]
  425.     filtered_data = conversion_history.pivot_table(
  426.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  427.     )
  428.     filter_data(filtered_data, window).plot(grid=True, ax=ax2)
  429.     plt.xlabel('Дата привлечения')
  430.     plt.title('Динамика конверсии пользователей на {}-й день'.format(horizon))
  431.  
  432.     plt.tight_layout()
  433.     plt.show()
  434.  
  435. # функция для визуализации LTV и ROI
  436. def plot_ltv_roi(ltv, ltv_history, roi, roi_history, horizon, window=7):
  437.  
  438.     # задаём сетку отрисовки графиков
  439.     plt.figure(figsize=(20, 10))
  440.  
  441.     # из таблицы ltv исключаем размеры когорт
  442.     ltv = ltv.drop(columns=['cohort_size'])
  443.     # в таблице динамики ltv оставляем только нужный лайфтайм
  444.     ltv_history = ltv_history.drop(columns=['cohort_size'])[[horizon - 1]]
  445.  
  446.     # стоимость привлечения запишем в отдельный фрейм
  447.     cac_history = roi_history[['cac']]
  448.  
  449.     # из таблицы roi исключаем размеры когорт и cac
  450.     roi = roi.drop(columns=['cohort_size', 'cac'])
  451.     # в таблице динамики roi оставляем только нужный лайфтайм
  452.     roi_history = roi_history.drop(columns=['cohort_size', 'cac'])[
  453.         [horizon - 1]
  454.     ]
  455.  
  456.     # кривые ltv
  457.     ax1 = plt.subplot(2, 3, 1)
  458.     ltv.T.plot(grid=True, ax=ax1)
  459.     plt.legend()
  460.     plt.xlabel('Лайфтайм')
  461.     plt.title('LTV')
  462.  
  463.     # динамика ltv
  464.     ax2 = plt.subplot(2, 3, 2, sharey=ax1)
  465.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  466.     columns = [name for name in ltv_history.index.names if name not in ['dt']]
  467.     filtered_data = ltv_history.pivot_table(
  468.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  469.     )
  470.     filter_data(filtered_data, window).plot(grid=True, ax=ax2)
  471.     plt.xlabel('Дата привлечения')
  472.     plt.title('Динамика LTV пользователей на {}-й день'.format(horizon))
  473.  
  474.     # динамика cac
  475.     ax3 = plt.subplot(2, 3, 3, sharey=ax1)
  476.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  477.     columns = [name for name in cac_history.index.names if name not in ['dt']]
  478.     filtered_data = cac_history.pivot_table(
  479.         index='dt', columns=columns, values='cac', aggfunc='mean'
  480.     )
  481.     filter_data(filtered_data, window).plot(grid=True, ax=ax3)
  482.     plt.xlabel('Дата привлечения')
  483.     plt.title('Динамика стоимости привлечения пользователей')
  484.  
  485.     # кривые roi
  486.     ax4 = plt.subplot(2, 3, 4)
  487.     roi.T.plot(grid=True, ax=ax4)
  488.     plt.axhline(y=1, color='red', linestyle='--', label='Уровень окупаемости')
  489.     plt.legend()
  490.     plt.xlabel('Лайфтайм')
  491.     plt.title('ROI')
  492.  
  493.     # динамика roi
  494.     ax5 = plt.subplot(2, 3, 5, sharey=ax4)
  495.     # столбцами сводной таблицы станут все столбцы индекса, кроме даты
  496.     columns = [name for name in roi_history.index.names if name not in ['dt']]
  497.     filtered_data = roi_history.pivot_table(
  498.         index='dt', columns=columns, values=horizon - 1, aggfunc='mean'
  499.     )
  500.     filter_data(filtered_data, window).plot(grid=True, ax=ax5)
  501.     plt.axhline(y=1, color='red', linestyle='--', label='Уровень окупаемости')
  502.     plt.xlabel('Дата привлечения')
  503.     plt.title('Динамика ROI пользователей на {}-й день'.format(horizon))
  504.  
  505.     plt.tight_layout()
  506.     plt.show()
  507.  
  508.  
  509. # считаем LTV и ROI
  510. ltv_raw, ltv_grouped, ltv_history, roi_grouped, roi_history = get_ltv(
  511.     profiles, orders, observation_date, horizon_days
  512. )
  513.  
  514. # строим графики
  515. plot_ltv_roi(ltv_grouped, ltv_history, roi_grouped, roi_history, horizon_days)
  516.  
  517. ---------------------------------------------------------------------------
  518. KeyError                                  Traceback (most recent call last)
  519. Input In [80], in <cell line: 2>()
  520.       1 # считаем LTV и ROI
  521. ----> 2 ltv_raw, ltv_grouped, ltv_history, roi_grouped, roi_history = get_ltv(
  522.       3     profiles, orders, observation_date, horizon_days
  523.       4 )
  524.       6 # строим графики
  525.       7 plot_ltv_roi(ltv_grouped, ltv_history, roi_grouped, roi_history, horizon_days)
  526.  
  527. Input In [63], in get_ltv(profiles, purchases, observation_date, horizon_days, dimensions, ignore_horizon)
  528.      97     return result, roi
  529.      99 # получаем таблицы LTV и ROI
  530. --> 100 result_grouped, roi_grouped = group_by_dimensions(
  531.     101     result_raw, dimensions, horizon_days
  532.     102 )
  533.     104 # для таблиц динамики убираем 'cohort' из dimensions
  534.     105 if 'cohort' in dimensions:
  535.  
  536. Input In [63], in get_ltv.<locals>.group_by_dimensions(df, dims, horizon_days)
  537.      64 result = result.div(result['cohort_size'], axis=0)
  538.      65 # исключаем все лайфтаймы, превышающие горизонт анализа
  539. ---> 66 result = result[['cohort_size'] + list(range(horizon_days))]
  540.      67 # восстанавливаем размеры когорт
  541.      68 result['cohort_size'] = cohort_sizes
  542.  
  543. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py:3511, in DataFrame.__getitem__(self, key)
  544.    3509     if is_iterator(key):
  545.    3510         key = list(key)
  546. -> 3511     indexer = self.columns._get_indexer_strict(key, "columns")[1]
  547.    3513 # take() does not accept boolean indexers
  548.    3514 if getattr(indexer, "dtype", None) == bool:
  549.  
  550. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:5796, in Index._get_indexer_strict(self, key, axis_name)
  551.    5793 else:
  552.    5794     keyarr, indexer, new_indexer = self._reindex_non_unique(keyarr)
  553. -> 5796 self._raise_if_missing(keyarr, indexer, axis_name)
  554.    5798 keyarr = self.take(indexer)
  555.    5799 if isinstance(key, Index):
  556.    5800     # GH 42790 - Preserve name from an Index
  557.  
  558. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:5859, in Index._raise_if_missing(self, key, indexer, axis_name)
  559.    5856     raise KeyError(f"None of [{key}] are in the [{axis_name}]")
  560.    5858 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
  561. -> 5859 raise KeyError(f"{not_found} not in index")
  562.  
  563. KeyError: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] not in index'
  564.  
  565.  
  566. conversion_raw, conversion_grouped, conversion_history = get_conversion(
  567.     profiles, orders, observation_date, horizon_days
  568. )
  569.  
  570. plot_conversion(conversion_grouped, conversion_history, horizon_days)
  571.  
  572. ---------------------------------------------------------------------------
  573. KeyError                                  Traceback (most recent call last)
  574. Input In [79], in <cell line: 1>()
  575. ----> 1 conversion_raw, conversion_grouped, conversion_history = get_conversion(
  576.       2     profiles, orders, observation_date, horizon_days
  577.       3 )
  578.       5 plot_conversion(conversion_grouped, conversion_history, horizon_days)
  579.  
  580. Input In [62], in get_conversion(profiles, purchases, observation_date, horizon_days, dimensions, ignore_horizon)
  581.      60     return result
  582.      62 # получаем таблицу конверсии
  583. ---> 63 result_grouped = group_by_dimensions(result_raw, dimensions, horizon_days)
  584.      66 if 'cohort' in dimensions:
  585.      67     dimensions = []
  586.  
  587. Input In [62], in get_conversion.<locals>.group_by_dimensions(df, dims, horizon_days)
  588.      55 # делим каждую «ячейку» в строке на размер когорты
  589.      56 # и получаем conversion rate
  590.      57 result = result.div(result['cohort_size'], axis=0)
  591. ---> 58 result = result[['cohort_size'] + list(range(horizon_days))]
  592.      59 result['cohort_size'] = cohort_sizes
  593.      60 return result
  594.  
  595. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py:3511, in DataFrame.__getitem__(self, key)
  596.    3509     if is_iterator(key):
  597.    3510         key = list(key)
  598. -> 3511     indexer = self.columns._get_indexer_strict(key, "columns")[1]
  599.    3513 # take() does not accept boolean indexers
  600.    3514 if getattr(indexer, "dtype", None) == bool:
  601.  
  602. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:5796, in Index._get_indexer_strict(self, key, axis_name)
  603.    5793 else:
  604.    5794     keyarr, indexer, new_indexer = self._reindex_non_unique(keyarr)
  605. -> 5796 self._raise_if_missing(keyarr, indexer, axis_name)
  606.    5798 keyarr = self.take(indexer)
  607.    5799 if isinstance(key, Index):
  608.    5800     # GH 42790 - Preserve name from an Index
  609.  
  610. File ~/Desktop/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:5859, in Index._raise_if_missing(self, key, indexer, axis_name)
  611.    5856     raise KeyError(f"None of [{key}] are in the [{axis_name}]")
  612.    5858 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
  613. -> 5859 raise KeyError(f"{not_found} not in index")
  614.  
  615. KeyError: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] not in index'
  616.  
  617.  
  618.  

Reply to "practicum"

Here you can reply to the paste above