В электронной коммерции или розничном бизнесе отношения между бизнесом и клиентами не являются договорными. В бесконтрактном мире клиенты уходят, но делают это молча; им не нужно говорить нам, что они уезжают. Это значительно усложняет расчет CLV. Мы должны посмотреть на время, прошедшее с момента последней транзакции клиента, и задать вопрос: жив ли клиент, но бездействует, или клиент «мертв» («жив» означает, что клиенты взаимодействуют с нами, «умереть» означает, что они становятся неактивными как клиенты. )?

Итак, вот и мы: моделируем пожизненную ценность клиента для внеконтрактного бизнеса.

Анализ основан на Dr. Питер Фейдер из Уортона . Мы не будем углубляться в математику CLV в этом анализе. Однако, если вам интересно, вот и Бумага.

Данные

Данные, которые мы будем использовать, те же, что и при проведении Анализ корзины и Сегментация клиентов - Набор данных для розничной торговли в Интернете, которые можно загрузить из Репозитория машинного обучения UCI.

import pandas as pd
import warnings
warnings.filterwarnings('ignore')
df = pd.read_excel("Online_Retail.xlsx")
df.head()

Как обычно, нам нужно выполнить некоторую очистку, затем создать новый фрейм данных, который содержит только CustomerID, InvoiceDate (удалить время) и добавить новый столбец - продажи:

import datetime as dt
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate']).dt.date
df = df[pd.notnull(df['CustomerID'])]
df = df[(df['Quantity']>0)]
df['Sales'] = df['Quantity'] * df['UnitPrice']
cols_of_interest = ['CustomerID', 'InvoiceDate', 'Sales']
df = df[cols_of_interest]
print(df.head())
print(df.head())
print(df['CustomerID'].nunique())

Определение модели CLV

Для моделей CLV используется следующая номенклатура:

  • Частота представляет собой количество повторных покупок, сделанных клиентом. Это означает, что это на единицу меньше, чем общее количество покупок.
  • T представляет возраст клиента в любых выбранных единицах времени (ежедневно в нашем наборе данных). Это равно промежутку времени между первой покупкой клиента и концом исследуемого периода.
  • Recency представляет возраст покупателя, когда он совершал последние покупки. Это равно промежутку времени между первой покупкой клиента и его последней покупкой. (Таким образом, если они совершили только 1 покупку, давность равна 0.)

Следующий анализ проводится на Python с использованием пакета Lifetimes, разработанного Cameron Davidson-Pilon, специалистом по обработке данных из Shopify, и код в значительной степени заимствован из документации Lifetimes.

Данные Исследовать

from lifetimes.plotting import *
from lifetimes.utils import *
from lifetimes.estimation import *
data = summary_data_from_transaction_data(df, 'CustomerID', 'InvoiceDate', monetary_value_col='Sales', observation_period_end='2011-12-9')
data.head()

В нашем анализе 4339 клиентов.

CustomerID 12346 совершил только 1 покупку (без повторения), поэтому его частота и давность равны 0, а его возраст - 325 дней (например, промежуток времени между его первой покупкой и окончанием периода анализа).

data['frequency'].plot(kind='hist', bins=50)
print(data['frequency'].describe())
print(sum(data['frequency'] == 0)/float(len(data)))

Среди всех клиентов, по нашим данным, более 35% совершили покупку только один раз (без повторения).

Анализ частоты / давности с использованием модели BG / NBD

from lifetimes import BetaGeoFitter
bgf = BetaGeoFitter(penalizer_coef=0.0)
bgf.fit(data['frequency'], data['recency'], data['T'])
print(bgf)

‹Lifetimes.BetaGeoFitter: обследовано 4339 испытуемых, a: 0,00, альфа: 69,01, b: 1,18, r: 0,83›

Визуализация нашей матрицы частоты / давности

Подумайте: покупатель совершал покупку каждый день в течение четырех недель подряд, а потом мы не получали от него никаких известий в течение нескольких месяцев. Каковы шансы, что он еще «жив»? Довольно маленький, правда? С другой стороны, покупатель, который исторически совершал покупки раз в квартал, а затем в последний квартал, скорее всего, все еще жив. Мы можем визуализировать эту взаимосвязь, используя матрицу частоты / новизны, которая вычисляет ожидаемое количество транзакций, которые искусственный клиент должен совершить в следующий период времени, учитывая его давность (возраст на момент последней покупки) и частоту (количество повторных транзакций, которые он совершил. сделал).

from lifetimes.plotting import plot_frequency_recency_matrix
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,8))
plot_frequency_recency_matrix(bgf)

Если покупатель совершил 120 покупок, и его последняя покупка была сделана, когда ему было примерно 350 дней (то есть, давность: продолжительность между ее первой транзакцией и ее последней транзакцией составляет 350 дней), тогда он наш лучший клиент (внизу справа) .

Клиенты, которые приобрели много и совершили покупку недавно, вероятно, станут лучшими покупателями в будущем. У нас их никогда не будет достаточно.

Покупатели, которые приобрели много, но не недавно (верхний правый угол), вероятно, ушли.

Есть также другой тип клиентов (около (40, 300)), который представляет покупателя, который покупает нечасто, и мы не видели его в последнее время, поэтому он может купить снова. Однако мы не уверены, ушел ли он или просто между покупками.

Мы можем предсказать, какие клиенты наверняка живы:

from lifetimes.plotting import plot_probability_alive_matrix
fig = plt.figure(figsize=(12,8))
plot_probability_alive_matrix(bgf)

Покупатели, купившие недавно, почти наверняка «живы».

Покупатели, которые приобрели много, но не в последнее время, скорее всего, выбыли из программы. И чем больше они покупали в прошлом, тем больше вероятность, что они отказались от него. Они представлены в правом верхнем углу.

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

t = 1
data['predicted_purchases'] = bgf.conditional_expected_number_of_purchases_up_to_time(t, data['frequency'], data['recency'], data['T'])
data.sort_values(by='predicted_purchases').tail(5)

Выше перечислены 5 наших основных клиентов, от которых модель ожидает, что они совершат покупки в течение следующего дня. Столбец predicted_purchases представляет ожидаемое количество покупок, а три других столбца представляют их текущие RF-метрики. Модель BG / NBD предполагает, что эти люди будут совершать больше покупок в ближайшем будущем, поскольку они являются нашими лучшими клиентами в настоящее время.

Оценка соответствия модели

from lifetimes.plotting import plot_period_transactions
plot_period_transactions(bgf)

Неплохо, модель не отстой. Итак, мы можем продолжить наш анализ.

Теперь мы разделим набор данных на набор данных периода калибровки и набор данных удержания. Это важно, поскольку мы хотим протестировать, как наша модель работает с данными, которые еще не были просмотрены (точно так же, как перекрестная проверка в практике машинного обучения).

from lifetimes.utils import calibration_and_holdout_data
summary_cal_holdout = calibration_and_holdout_data(df, 'CustomerID', 'InvoiceDate',
                                        calibration_period_end='2011-06-08',
                                        observation_period_end='2011-12-9' )   
print(summary_cal_holdout.head())

from lifetimes.plotting import plot_calibration_purchases_vs_holdout_purchases
bgf.fit(summary_cal_holdout['frequency_cal'], summary_cal_holdout['recency_cal'], summary_cal_holdout['T_cal'])
plot_calibration_purchases_vs_holdout_purchases(bgf, summary_cal_holdout)

На этом графике мы разделяем данные как на период выборки (калибровка), так и период проверки (выдержка). Период выборки - начало 2011–06–08 гг .; период валидации охватывает период с 2011–06–09 по 2011–12–09 годы. График группирует всех клиентов в период калибровки по количеству их повторных покупок (ось x), а затем усредняет их повторные покупки в период удержания (ось y). Зеленая и синяя линии представляют собой прогноз модели и фактический результат по оси Y соответственно. Как мы видим, наша модель способна очень точно предсказать поведение клиентской базы вне выборки, модель занижает оценку при 4 покупках и после 5 покупок.

Прогнозы клиентских транзакций

На основе истории покупателя теперь мы можем предсказать, как могут выглядеть будущие покупки человека:

t = 10
individual = data.loc[12347]
bgf.predict(t, individual['frequency'], individual['recency'], individual['T'])

0.1572775101289126

Наша модель предсказывает, что будущая транзакция клиента 12347 составит 0,157 через 10 дней.

Истории вероятностей клиентов

Учитывая историю транзакций клиентов, мы можем рассчитать их историческую вероятность того, что они будут живы, в соответствии с нашей обученной моделью. Например, мы хотим взглянуть на историю транзакций наших лучших клиентов и увидеть вероятность того, что они останутся в живых:

from lifetimes.plotting import plot_history_alive
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,8))
id = 14606
days_since_birth = 365
sp_trans = df.loc[df['CustomerID'] == id]
plot_history_alive(bgf, days_since_birth, sp_trans, 'InvoiceDate')

Наш лучший клиент определенно жив, однако у него было несколько перерывов во второй половине 2011 года.

fig = plt.figure(figsize=(12,8))
id = 14729
days_since_birth = 365
sp_trans = df.loc[df['CustomerID'] == id]
plot_history_alive(bgf, days_since_birth, sp_trans, 'InvoiceDate')

С другой стороны, этот покупатель совершил покупку только один раз и ушел в течение длительного времени со 100% вероятностью того, что он мертв.

Оценка общей ценности клиента с помощью гамма-гамма модели денежной ценности

Теперь мы учитываем экономическую стоимость каждой сделки. Чтобы оценить это, мы используем подмодель гамма-гамма, представленную доктором Питером Фейдером и доктором Брюсом Харди из Wharton, чтобы спрогнозировать вероятные расходы на транзакцию в будущем на уровне клиента.

Мы оцениваем только тех клиентов, которые совершили хотя бы одну повторную покупку у нас. Таким образом, мы оцениваем 2 790 клиентов.

returning_customers_summary = data[data['frequency']>0]
print(returning_customers_summary.head())
print(len(returning_customers_summary))

from lifetimes import GammaGammaFitter
ggf = GammaGammaFitter(penalizer_coef = 0)
ggf.fit(returning_customers_summary['frequency'],
        returning_customers_summary['monetary_value'])
print(ggf)

‹Время жизни. GammaGammaFitter: обследовано 2790 испытуемых, p: 2,10, q: 3,45, v: 485,57›

После применения модели Gamma-Gamma теперь мы можем оценить среднюю стоимость транзакции для каждого клиента. Уф!

print(ggf.conditional_expected_average_profit(
        data['frequency'],
        data['monetary_value']
    ).head(10))

Блокнот Jupyter можно найти на Github. Счастливые выходные!