Ко мне обратился коллега с вопросами про бизнес-метрики – средний чек и ARPU. Может ли показатель быть одинаков для двух метрик и не изменчив от месяца к месяцу, особенно в рамках одного бизнеса, у которого фиксированное кол-во продуктов и одна цена. Плюс, для чего бизнес старается растить данные метрики.
В этой статье я разобрался в бизнес-метриках и ответил на вопросы:
Что такое ARPU и средний чек? Как их рассчитывать? На какие вопросы они отвечают и для чего нужны?
Могут ли они ARPU и средний чек быть равны между собой? Будут ли отличаться в динамике месяц от месяца?
Что если в бизнесе кол-во продуктов фиксировано и все они с одинаковой ценой? Будет ли показатель от месяца к месяцу одинаков? А если рассчитывать среднюю выручку?
Разберемся обо всем по порядку. А для наглядности – рассчитаем данные метрики на реальных данных интернет-магазина.
Погнали!
Что такое ARPU и средний чек? Как их рассчитывать? На какие вопросы они отвечают и для чего нужны?
Wiki дает довольно простенькое определение метрик:
Средний чек – рассматривает отношение дохода ко всем транзакциям за определенный период. Это сумма revenue на количество всех транзакций за конкретный период.
ARPU – рассматривает отношение дохода к числу пользователей в определенный период. Это сумма revenue на количество пользователей, которые могли сделать, а могли и не сделать транзакцию в конкретный период.
Уже видим существенное отличие в знаменателе, у среднего чека это транзакции, у ARPU — это пользователи, которые могут совершать транзакции. Также видим, что в таком сравнении средний чек показывает большую точность в доходности бизнеса, так как в ARPU учитывается сегмент не платящих пользователей, которые могут оседать «мертвой» массой, искажающей картину.
Для повышения точности, лучше рассчитывать ARPPU(average revenue per paying user) — средний доход на платящего пользователя. Опять же, есть отличие в сравнении с средним чеком, так как в знаменателе расчета ARPPU учитываются именно пользователи, совершившие одну или более транзакций за конкретный (сравниваемый) период.
Итак, ARPU (average revenue per user) — выручка продукта за определенный период, разделенная на количество активных пользователей за этот же период. Например, переходящих по разделам сайта, кликающим на кнопки, скроллящим страницы, имеющим длительную сессию и т.д..
Метрика отвечает на вопрос: сколько выручки приносит средний активный пользователей за изучаемый период времени. То есть грубо означает средний доход от клиента за период времени (определение клиента зависит от бизнеса). Метрику можно использовать, когда нужно оцифровать ценность продукта для пользователей.
Но лучше взять ARPPU — выручка на платящих пользователей (например, совершающие покупки, подкл. платные услуги, принимающие офферы, апсейялящие тариф и т.д.). То есть, учитывает не пользователей, а тех кто совершал платежи, в течение времени. Метрика помогает оценить, например как восприняли покупатели изменения цен.
Средний чек помогает понять выручку по заказам, а не по клиентам. То есть это вся выручка за месяц, разделенная на количество заказов (транзакций). Она показывает сколько денег приходится на один заказ (не на одного покупателя).
Опять же, в одном заказе (транзакции) может быть несколько продуктов – например, как в маркетплейсах, когда в одной транзакции пользователя может быть несколько товаров из разных категорий и с разной ценой. Например: iPhone и мороженое Baskin Robins.
Таким образом, три метрики — это разные показатели, с разными формулами расчета и отвечающими на разные запросы бизнеса.
ARPU помогает оценить финансовый потенциал компании – чем он выше и ближе к ARPPU, тем больше пользователей готовы платить за продукт. Или привлечь инвесторов, или оценить изменения в тарифной сетке.
Учитывая количество платящих пользователей, можно оценить, за счет чего повышается доход. Доход может расти не за счет повышения цен, а за счет увеличения доли платящих пользователей.
Также с помощью ARPU можно проаналитить результаты рекламных кампаний и качество трафика.
Средний чек — один из основных показателей успешности развития компании. По-хорошему, он должен расти, а не уменьшаться.
Средний чек помогает оценить платежеспособность аудитории, выявить сезонность, оценить эффективность интерфейса сайта или приложения в сценариях целевых действий.
Даже если растет выручка, снижение среднего чека может говорить о том, что выручка растет за счет привлечения новых клиентов. Но в долгосрочной перспективе падает покупательская способность и необходимо корректировать цены.
Так, в месяц выхода нового iPhone существенно растет средний чек в категории товаров «гаджеты и техника». Или пользователи охотно и лучше всего оформляют подписку через тариф—конструктор, при этом в данном продукте самый низкий средний чек. Возможно, здесь нужно предоставить «коробочное» решение – тариф, который включает в себя самые популярные подписки и выше в цене.
Могут ли ARPU и средний чек быть равны между собой? Будут ли отличаться в динамике месяц от месяца?
Нам нужен датасет. Лезем на kaggle, и берем датасет какого-нибудь e-shop.
Для анализа и расчета метрик нам нужна примерно следующая таблица:
Дата|id_пользователя|id_заказа или транзакции или продукта|название продукта|цена
Я взял вот этот датасет.
# Загружаем датасет
df = pd.read_csv('sales_transactions.csv')
df.head()
Работа с исходным датасетом
Переименуем названия столбцов для удобства.
Проверим качество данных (наличие пропусков, некорректные записи, запредельные значения и т.д.).
Приведем id транзакций, продукта и пользователей к удобному формату.
Приведем дату к удобному формату.
Проведем небольшой EDA (какие страны есть в датасете, приведем цену к единой валюте и поймем, есть ли различия и т.д.).
# Переименовываем столбцы для удобства
df.set_axis(['transaction_id', 'date', 'product_id', 'product',
'price', 'quantity', 'user_id', 'country'], axis = 'columns', inplace = True)
# Количествово строк в датасете
df.shape
(536350, 8)
# Проверка на наличие пропусков
for row in df.columns:
print('Кол-во пропусков в столбце ', row, '=', df[row].isnull().sum())
Пропущено 55 записей id пользователей
Это нехорошо – скорее всего это тех.баг или еще что-то из-за чего нет айдишек. Проверим.
# Пропуски в id пользаков нельзя восстановить, поэтому заменим их на ноль.
df['user_id'] = df['user_id'].fillna(0)
# Посмотрим записи по таким пользователям
df[df['user_id'] == 0]
Ага, у пользователей с отсутствием id в большинстве случаев отрицательная запись по количеству товаров, и transaction_id начинается с буквы «C», что означает – «возврат товара». Из данной выборки всего одна запись с положительным количеством товаров, но так же без id пользователя.
298352 558245 6/27/2019 22734 Set Of 6 Ribbons Vintage Christmas 10.25 8 0.0 United Kingdom
В нашем датасете всего 536 350 строк, доля строк с пропущенными id пользователей составляет меньше одного процента. Их можно удалить, без потери качества данных.
Заодно проверим, сколько у нас возвратов товара.
# Если первая буква «C» в айдишке указывает на «возврат», найдем общее количество возвратов.
import re
def cancelation(x) :
x = str(x)
pattern = r'[C]'
return re.findall(pattern, x)
cancel = df['transaction_id'].apply(lambda x: True if 'C' in cancelation(x) else False)
print("Количество возвратов {}".format(cancel.sum()))
Количество возвратов 8 585
# Делаем отдельный столбец с обозначением возвратов. Понадобится для исключения «возвратов»
df['cancel'] = cancel
# Считаем
df['cancel'].value_counts(normalize=True)*100
Количество возвратов составляет 8 585 тыс. Это примерно полтора процента от основного датасета. Их мы тоже вычистим.
# Удалим значения с возвратами
index_cancel = df[df['cancel']==True].index
df.drop(index=index_cancel, inplace=True)
# Удалим значения с нулевыми id пользаков
df = df[df['user_id'] != 0]
Проверим, остались ли у нас отрицательные значения по количеству продуктов. Да и в целом, грубо прикинем по максимальным и минимальным значениям датасета (квартили)
Отрицательных значений нет, но мы видим, что есть запредельное значение по максимальному количеству товаров, 80 995 шт. чего-то. Взглянем детальней.
Ммм, один пользователь купил 80 тыс. маленьких птичек из крафтовой бумаги. Хах, у всех свои причуды. Посмотрим, есть ли еще какие-либо транзакции у данного пользователя.
Данный пользователь ранее покупал щетку и кисть для чистки кладовой. Возможно, это какой-нибудь офис-менеджер, сделавший огромный заказ (бумажных птичек) в качестве подарков сотрудникам компании или украшения офиса. То есть, это может быть реальный заказ, а не тех.баг. Однако, для нашего дальнейшего исследования по расчету среднего чека и ARPU, данное количество товара в транзакции будет выглядеть как «выброс» тянущий на себя среднее значение. Поэтому мы удалим данный заказ.
Но это очень плохая практика и на работе так лучше не делать. Потому что это реальный пользователь с настоящей покупкой, которая может оказаться «китом» среди остальных. И удаление таких пользователей приводит к искажению действительности в понимании показателей бизнеса.
Также убедимся, что у нас предоставлены данные за весь период и нет каких-либо пробелов. Для этого, сгруппируем количество транзакций (предположительно они были каждый день) по дате, и отрисуем график для наглядности.
# Смотрим период данных
print('Начало периода: ', df['date'].min())
print('Конец периода: ', df['date'].max())
cnt_transaction = df.groupby('date')['transaction_id'].count().reset_index()
sns.set(font_scale=1.2)
plot = cnt_transaction.plot( x = 'date', y = 'transaction_id',
grid = True, rot=45,
legend=False, figsize=(17,5))
plt.title('Кол-во транзакций в динамике')
plt.xlabel('')
plt.ylabel('Кол-во транзакций')
plt.ylim(bottom=0);
Начало периода: 2018-12-01 00:00:00
Конец периода: 2019-12-09 00:00:00
Отлично! Данные представлены за весь указанный период.
Цена на товары указана в фунтах стрелингов – об этом я узнал из описания к датасету. Для нашей задачи (нахождение среднего чека и ARPU) удобнее перевести цены товаров в рубли.
Для этого парсим с страницы банка актуальный курс по валютам. Получаем датасет, находим нужную валюту и значение курса, перемножаем на цену и записываем новую цену в рублях в отдельный столбец price_rubls
import requests
data = requests.get('https://www.cbr-xml-daily.ru/daily_json.js').json()
df['price_rubls'] = df['price'].apply(lambda x: x * data['Valute']['GBP']['Value']).astype(int)
Итак, датасет готов для работы.
Посмотрим какие страны представлены в датасете, выведем топ-15 по количеству записей.
df['country'].value_counts().head(15)
United Kingdom 477769
France 10393
Germany 10240
EIRE 7807
Belgium 2507
Spain 2386
Netherlands 2326
Switzerland 2303
Portugal 1838
Australia 1631
Norway 927
Austria 884
Iceland 787
Finland 686
Italy 624
Name: country, dtype: int64
Ого! Кажется, этот интернет-магазин продает по всему миру, а не только в Британии, как я думал ранее.
Во время работы с датасетом я обратил внимание, что у одной даты могут быть неуникальные транзакции, и в записях одной транзакции может находится несколько продуктов с разными product_id, но одинаковым user_id.
Также стоит обратить внимание на столбец quantity.
Таким образом, для нашей задачи нужно сагрегировать датасет в примерную витрину:
Средний чек
date | transaction_id | выручка с одной транзакции (сумма всех позиций (кол-во товаров (quantity) * цену(price_rubls)), внутри одной транзакции)
ARPU
Аналогичная витрина, только вместо transaction_id берем user_id
# Рассчитываем выручку с одного продукта внутри транзакции (количество продукта умнажаем на цену).
df['revenue_per_transaction'] = (df['quantity']*df['price_rubls']).astype(int)
Теперь посмотрим топ-15 стран по выручке за весь период. Прикинем, с каким масштабом мы имеем дело, чтобы в дальнейшем рассчитанные средний чек и ARPU соотносились со здравым смыслом.
country_group = (df.groupby('country', as_index=False)['revenue_per_transaction'].sum()
.sort_values(by='revenue_per_transaction', ascending=False)
.head(15))
plt.figure(figsize=(17,10))
sns.set_style('darkgrid')
sns.barplot(y = 'country',
x = 'revenue_per_transaction',
data = country_group,
palette = 'RdBu')
plt.title('Топ-15 стран по выручке')
plt.xlabel('Выручка')
plt.ylabel('');
country_group['revenue_per_transaction'] = (country_group['revenue_per_transaction'].apply(lambda x: '{0:,}'.
format(int(x)).replace(',', ' ')))
country_group
Ого! Запредельные числа. Вы только вдумайтесь, выручка Британии за год сотавила 6 179 180 204 (Шесть миллиардов сто семьдесят девять миллионов сто восемьдесят тысяч двести четыре)
Но вернемся к нашей основной задаче – сравнение среднего чека и ARPU. Будут ли они одинаковы? Будет ли метрика одинакова от месяца к месяцу?
А при фиксированном количестве продуктов и неизменной цене на данные продукты, будет ли показатель от месяца к месяцу одинаков?
Средний чек
Мы посчитали выручку для каждого продукта внутри транзакции. Теперь посчитаем итоговую выручку по всем позициям (продуктам) внутри транзакции и сгруппируем по дате.
# Для этого группируем по дате и транзакции, агрегируем сумму по посчтианной выручке за продукт
group_date_tr = (df.groupby(['date','transaction_id'], as_index=False).agg({'revenue_per_transaction':'sum'})
.sort_values(by='date'))
group_date_tr
Осталось перевести дату в месяц, сгруппировать данные по месяцу и для каждого месяца разделить итоговую сумму на количество транзакций.
# Переводим дату в месяц
group_date_tr['month'] = group_date_tr['date'].astype('datetime64[M]')
# Группируем по месяцу, рассчитываем количество уникальных транзакций и сумму по всем транзакциям
month_average_cheack = group_date_tr.groupby('month', as_index=False).agg({'transaction_id':'nunique',
'revenue_per_transaction':'sum'})
# Переводим количество транзакций в нужный формат
month_average_cheack['transaction_id'] = month_average_cheack['transaction_id'].astype(int)
# Рассчитываем средний чек. Сумма на количество
month_average_cheack['average_check'] = ((month_average_cheack['revenue_per_transaction'] / month_average_cheack['transaction_id'])
.astype(int))
# Представим на графике в динамике по месяцам
plot = month_average_cheack.plot(x = 'month', y = 'average_check',
grid=True, rot=45,
legend=False, figsize=(17,8))
plt.title('Средний чек в динамике по месяцам')
plt.xlabel('')
plt.ylabel('Средний чек')
plt.show();
Ага! Видим, что от месяца к месяцу средний чек отличается. Но визуализация подобрана неудачно. Сделаем более информативно
fig = px.bar(month_average_cheack, x='month', y='average_check', color='average_check',
title='Средний чек по месяцам')
fig.update_xaxes(title='Месяц')
fig.update_yaxes(title='Средний чек')
fig.show()
Теперь можно наглядно сравнить. И сделать вывод, что самый высокий средний чек был в январе 19го, далее была просадка. Также средний чек рос осенью 19го. Сезонность по декабрю и январю, и от года к году сравнить не можем, так как ограничены изначальным периодом датасета.
ARPU
Проделываем все то же самое, но с user_id. И в итоге сумму по всем транзакциям делим на количество уникальных пользователей.
В датасете представлены только пользователи совершившие покупку, то есть только платящие. И соответственно в данном случае ARPU=ARPPU
ARPU так же, как и средний чек отличается от месяца к месяцу. Также видим высокий показатель за январь 19го, но, что интересно, более плавный и последовательный рост с июня по август, в сравнении со средним чеком. Видимо цены не менялись, но прибавлялись пользователи.
Сравним средний чек с ARPU в динамике по месяцам. Для наглядности выведем метрики на одну визуализацию.
Вот и все.
Общий вывод: метрики отличаются друг от друга, и их показатели отличаются от месяца к месяцу.
Так, а если рассчитать среднюю выручку и сравнить средний чек месяц к месяцу?
Для этого группируем по месяцу, рассчитываем количество уникальных транзакций и среднюю выручку по всем транзакциям за этот месяц. Далее делим среднюю выручку на количество транзакций.
Пожалуйста. Мы получили усредненный показатель, но он также отличается от месяца к месяцу.
Что если в бизнесе кол-во продуктов фиксировано и все они с одинаковой ценой? Будет ли показатель от месяца к месяцу одинаков? А если рассчитывать среднюю выручку?
Так, ну а что, если у нас фиксированное количество продукта и неизменная цена? Да в принципе, ничего особенного, метрики также будут отличаться, потому что количество транзакций в случае среднего чека и количество пользователей для ARPU будут разными.
Но а все же, если количество пользователей будет одинаковым, и количество транзакций, и количество продуктов, и цена. Например, подписка на премиум-тариф от Кинопоиска (придумываю на ходу). У нас ограниченное и неизменное количество пользователей (закрытый клуб, новичков не пускаем, старички все в принятии и не уходят), всего один продукт (подписка на тариф), цена тоже зафиксирована (не снижаем по акциям, не поднимаем по инфляции). Тогда да, возможно. Но и формула при этом меняется – средняя выручка = среднему чек = ARPU. Но я такого пока не встречал.
Мой полный notebook можно скачать здесь
Комментарии (7)
Ivan22
17.09.2023 05:05+3Количество возвратов составляет 8 585 тыс. Это примерно полтора процента от основного датасета. Их мы тоже вычистим.
Нифига себе. Р-р-раз и получили завышенный ARPU за счет этих 8585 транзакций. А мужики-то думают, гадают, как ARPU повысить :)))
То есть, это может быть реальный заказ, а не тех.баг. Однако, для нашего дальнейшего исследования по расчету среднего чека и ARPU, данное количество товара в транзакции будет выглядеть как «выброс» тянущий на себя среднее значение. Поэтому мы удалим данный заказ.
Еще веселее - реальный заказ, реальный чек, но портит нам красивую картинку?? Что делать?
Правильно - подогнать данные под желаемый результат. Р-р-р-раз, и самый крупный чек летит в мусорку.
Для нашей задачи (нахождение среднего чека и ARPU) удобнее перевести цены товаров в рубли.
Для этого парсим с страницы банка актуальный курс по валютам.
Ну это вообще класика подгона данных под результат, транзакции у нас за целый год, а курс за один день!! Думаю уже все в курсе как сильно могут курсы (простите за каламбур) плясать не то что за год, за неделю.
nikolay_frants Автор
17.09.2023 05:05-1Нифига себе. Р-р-раз и получили завышенный ARPU за счет этих 8585 транзакций. А мужики-то думают, гадают, как ARPU повысить :)))
В смысле ? Для учебной задачи, мы вполне можем себе позволить срезать часть данных. По факту мы рассчитывали ARPPU, и в таком контексте транзакции с возвратами нам не нужны. Если из данной выборки пользователи совершали другие покупки, то они учитываются в датасете и последующий расчетах.
Еще веселее - реальный заказ, реальный чек, но портит нам красивую картинку?? Что делать?
Правильно - подогнать данные под желаемый результат. Р-р-р-раз, и самый крупный чек летит в мусорку.
А здесь-то чего? Абзацем ниже я пишу, что так делать не нужно, так как это искажает реальную картину. Но мы так делаем сейчас в рамках учебной задачи. (см. внимательнее статью)
Ну это вообще класика подгона данных под результат, транзакции у нас за целый год, а курс за один день!! Думаю уже все в курсе как сильно могут курсы (простите за каламбур) плясать не то что за год, за неделю.
Да, конечно. Не вижу здесь какой-либо грубой подгонки. Я закинул способ как можно взять реальный текущий курс, и сделать по нему перевод. Легко можно автоматизировать на каждый день.
Boilerplate
17.09.2023 05:05Ммм, считать средний чек продавая мороженки и айфоны. Даже не знаю, что может пойти не так. Вы не пробовали считать эффективность больниц по средней температуре? Тоже удобно, инновационно, и также бессмыленно. Нормальные метрики - валовая прибыль по категориям товаров и/или брендам, просто суммы продаж, количество новых покупателей, отношение сумм продаж к себестоимости остатков, и практически ручная оценка и поиск покупателей, которые перестали покупать с разбором причин. А средний чек - самая дурная характеристика из вохможных.
nikolay_frants Автор
17.09.2023 05:05Так статья и не о нахождении "супер-мега-крутых" метрик закрывающих все потребности бизнеса. И даже не о популяризации среднего чека с ARPU.
Прикольное перечисление "нормальных" метрик.
x67
>метрики отличаются друг от друга, и их показатели отличаются от месяца к месяцу
Честно говоря крайне слабый вывод для аналитика. Спасибо капитан! А можно пример где это важно?? А можно побольше выводов, о том как изменение метрик или их соотношения зависит от структуры спроса или иных факторов?
Зачем брать рандомный магазин с непонятной структурой спроса и неизвестным ретеншеном? Ну возьмите какой нибудь самокат/лавку и наглядно увидете, насколько это разные метрики. Возьмите любую приложеньку с freemium моделью и увидите такую же наглядную разницу между arpu/arppu
Robastik
✊
nikolay_frants Автор
Добрый день!)
Да конечно можно!) И добавить примеров, и побольше сделать выводов. И разбить пользователей на сегменты - по категориям купленных товаров, и посмотреть как меняется средний чек по категориям и от каких продуктов в конкретной категории это зависит.
Можно еще собрать когорты пользователей по неделям, и посмотреть ARPU по когортам. Да много чего еще можно придумать. Но я не думал делать этого в рамках текущей задачи.
Возможно, потом добавлю больше примеров как можно "играть" с средним чеком. Например, смоделировать кейс: "В Я.Маркете всего 5 категорий товаров, за один месяц средний чек вырос в каждой категории. Что в таком случае будет с общим ср.чеком Маркета ?"
Рандомный магазин взять проще всего, чтобы покрутить данные, и разобраться в сути вещей.
P.s.: я не капитан. Я адмирал !)))