Руководство по прогнозированию ценности жизни клиента (CLV) в Python с использованием метода BG-NBD и гамма-гаммы.

Пожизненная ценность клиента — это общая сумма, которую клиент потратит с момента приобретения до прекращения отношений с бизнесом. В моем последнем проекте я рассчитывал CLV с помощью Python.

Если вы хотите это увидеть:



На этот раз мы будем делать прогноз.

В чем разница между «предсказанием» и «расчетом»

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

Какие шаги?

  • Подготовка данных
  • Прогнозирование ожидаемых продаж с помощью модели BG-NBD
  • Ожидаемая средняя прибыль с гамма-гамма-моделью
  • Расчет CLTV с BG-NBD и гамма-гамма-моделью
  • Создание сегментов по CLTV

1. Подготовка данных

  • Информация о наборе данных

Компания электронной коммерции хочет сегментировать своих клиентов и определить маркетинговые стратегии в соответствии с этими сегментами.
Набор данных:

https://archive.ics.uci.edu/ml/datasets/Online+Retail+II

Набор данных показывает продажи британского интернет-магазина в период с 12.01.2009 по 12.09.2011.

InvoiceNo: номер счета. Номинал. 6-значный целочисленный номер, который однозначно присваивается каждой транзакции. Если этот код начинается с буквы «с», это означает отмену.
StockCode: код товара (товара). Номинал. 5-значный целочисленный номер, который уникально присваивается каждому отдельному продукту.
Описание: название продукта (элемента). Номинальное.
Количество: количество каждого продукта (элемента) за транзакцию. Числовой.
InvoiceDate: дата и время выставления счета. Числовой. День и время создания транзакции.
UnitPrice: цена за единицу. Числовой. Цена товара за единицу в фунтах стерлингов (£).
CustomerID: номер клиента. Номинал. 5-значный целочисленный номер, который присваивается каждому клиенту.
Страна: название страны. Номинал. Название страны, в которой проживает клиент.

  • Необходимая библиотека и функции
pip install lifetimes
pip install sqlalchemy
from sqlalchemy import create_engine
import datetime as dt
import pandas as pd
import matplotlib.pyplot as plt
from lifetimes import BetaGeoFitter
from lifetimes import GammaGammaFitter
from lifetimes.plotting import plot_period_transactions

def outlier_thresholds(dataframe, variable):
 quartile1 = dataframe[variable].quantile(0.01)
 quartile3 = dataframe[variable].quantile(0.99)
 interquantile_range = quartile3 — quartile1
 up_limit = quartile3 + 1.5 * interquantile_range
 low_limit = quartile1–1.5 * interquantile_range
 return low_limit, up_limit
def replace_with_thresholds(dataframe, variable):
    low_limit, up_limit = outlier_thresholds(dataframe, variable)
    dataframe.loc[(dataframe[variable] < low_limit), variable] = low_limit
    dataframe.loc[(dataframe[variable] > up_limit), variable] = up_limit
# Additional info; 'outlier_thresholds' function, is so useful function you can use it in your data preparation process, it finds out outlier_thresholds and equals it either up_limit or low_limit. They are simultaneously used with replace_with_thresholds function. With the help of these two function, We will equalize our outlier thresholds to determined low_limit and up_limit values without taking a long time.
#Where you can find the dataset https://archive.ics.uci.edu/ml/datasets/Online+Retail+II
df_ = pd.read_excel(“online_retail_II.xlsx”,
 sheet_name=”Year 2010–2011")
df = df_.copy()
df.shape
OUT = (541910, 8)
df.describe().T

df.dropna(inplace=True)
#For focusing on CLV prediction ,I fastly deleted all missing values.
df = df[~df["Invoice"].str.contains("C", na=False)]
# removing Cancellations from the data
replace_with_thresholds(df, "Quantity")
replace_with_thresholds(df, "Price")
#After our transactions, Let's check it out how describe looks like.
df.describe().T

  • Подготовка структуры жизненных данных
df[“TotalPrice”] = df[“Quantity”] * df[“Price”]
df.head()

Давность = Время, прошедшее с момента последней покупки [Еженедельно]
[Если вы читали другую статью, о которой я упоминал выше, мы рассчитали давность в соответствии с сегодняшним днем ​​[потому что это было расчет, И мы рассчитываем текущее значение CLV], но здесь receny будет рассчитываться индивидуально для каждого клиента.
T = за какое время до даты анализа была сделана первая покупка [Еженедельно]
Частота = общее количество повторных покупок
monetary_value = средний доход за покупку

cltv_df = df.groupby(‘Customer ID’).agg({‘InvoiceDate’: [lambda date: (date.max() — date.min()).days,
 lambda date: (today_date — date.min()).days],
 ‘Invoice’: lambda num: num.nunique(),
 ‘TotalPrice’: lambda TotalPrice: TotalPrice.sum()})
cltv_df.head(8)

cltv_df.columns = cltv_df.columns.droplevel(0)
cltv_df.columns = ['recency', 'T', 'frequency', 'monetary']
cltv_df.head(5)

Мы создали наши переменные R-T-F-M, но осталось еще несколько проблем. Теперь нам нужно преобразовать в соответствующий формат данных.

Например; Денежный показатель должен быть «Средним доходом», но он показывает нам общий доход клиента. Итак, с помощью базовых кодов Python мы преобразуем его.

#Expressing "monetary value" as average earnings per purchase
cltv_df["monetary"] = cltv_df["monetary"] / cltv_df["frequency"]
# selection of monetary values greater than zero
cltv_df = cltv_df[cltv_df["monetary"] > 0]
# Expression of "recency "and "T" in weekly terms
cltv_df["recency"] = cltv_df["recency"] / 7
cltv_df["T"] = cltv_df["T"] / 7
#frequency must be greater than 1.
cltv_df = cltv_df[(cltv_df['frequency'] > 1)]
After all trasformation ,lets check how our data looks like now.
ctlv_df.head()

Мы организовали весь набор данных для запуска в модели BG-NBD.

2. Создание модели BG-NBD/прогнозирование ожидаемых продаж с помощью модели BG-NBD

bgf = BetaGeoFitter(penalizer_coef=0.001)
#setting up the model
bgf.fit(cltv_df['frequency'],
        cltv_df['recency'],
        cltv_df['T'])
#fitting of all dataset

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

  • Кого из 10 клиентов мы ожидаем больше всего в неделю?
bgf.conditional_expected_number_of_purchases_up_to_time(1,
                                                        cltv_df['frequency'],
                                                        cltv_df['recency'],
                                                        cltv_df['T']).sort_values(ascending=False).head(10)
#1 = 1 week 

Давайте назначим его переменной и отобразим в наших данных.

cltv_df["expected_purc_1_week"] = bgf.predict(1,
                                              cltv_df['frequency'],
                                              cltv_df['recency'],
                                              cltv_df['T'])
#conditional_expected_number_of_purchases_up_to_time and predict are basically same.
cltv_df.head(5)

  • Кого из 10 клиентов мы ожидаем больше всего в течение 1 месяца?
bgf.predict(4,
            cltv_df['frequency'],
            cltv_df['recency'],
            cltv_df['T']).sort_values(ascending=False).head(10)

cltv_df[“expected_purc_1_month”] = bgf.predict(4,
 cltv_df[‘frequency’],
 cltv_df[‘recency’],
 cltv_df[‘T’])
cltv_df.sort_values("expected_purc_1_month", ascending=False).head(10)
#Sorted the variables accourding to 1month expected purchase prediction.

  • Какое ожидаемое количество продаж всей компании за 1 месяц?
bgf.predict(4,cltv_df['frequency'],cltv_df['recency'],cltv_df['T']).sum()
OUTPUT = 1777.1450731636987
#It is a transaction amount that is expected in the next month.
  • Оценка результатов прогноза
plot_period_transactions(bgf)
plt.show()

Мы видим более или менее успешное прогнозирование нашей модели.

3. Определение модели ГАММА-ГАММА/ожидаемой средней прибыли с помощью модели Гамма-Гамма

Итак, во второй главе мы предсказали нашу прибыль с помощью модели BG-NBD, для модели BG-NBD мы использовали 3 переменные: частоту, недавность и T. В модели Гамма-Гамма нам нужны только 2 переменные, частотная и денежная. Итак, приступим к анализу.

ggf = GammaGammaFitter(penalizer_coef=0.01)
ggf.fit(cltv_df['frequency'], cltv_df['monetary'])
#As I mentioned above, we only use two variables . We used 3 variables on BG-NBD model.
ggf.conditional_expected_average_profit(cltv_df['frequency'],
                                       cltv_df['monetary']).head(10)

ggf.conditional_expected_average_profit(cltv_df[‘frequency’],
 cltv_df[‘monetary’]).sort_values(ascending=False).head(10)
#Let's sorted values from largest to smallest

cltv_df["expected_average_profit"] = ggf.conditional_expected_average_profit(cltv_df['frequency'],                                                                cltv_df['monetary'])
cltv_df.head(10)

cltv_df.sort_values("expected_average_profit",ascending=False).head(10)
#Sorted values according to expected_avarage_profit

4. Расчет CLTV с BG-NBD и гамма-гамма-моделью

Теперь мы смешаем обе модели и получим чистые, проанализированные значения CLTV.

cltv = ggf.customer_lifetime_value(bgf,
 cltv_df[‘frequency’],
 cltv_df[‘recency’],
 cltv_df[‘T’],
 cltv_df[‘monetary’],
 time=3, # 3 Months
 freq=”W”, # Frequency of T ,in this case it is 'weekly'
 discount_rate=0.01)
cltv.head(5)

cltv.shape
OUTPUT = (2845,)
cltv = cltv.reset_index()
cltv.head()

cltv.sort_values(by="clv", ascending=False).head(10)

Мы нашли список самых важных для меня клиентов за 3 месяца, в порядке от большего к меньшему.

cltv_final = cltv_df.merge(cltv, on="Customer ID", how="left")
#merging our real dataset and cltv_df data.
cltv_final

cltv_final.sort_values(by=”clv”, ascending=False).head(10)
#Sorting values by 'clv' variable.

  • Стандартизация CLTV
scaler = MinMaxScaler(feature_range=(0, 1)) 
#score between 0-1 ,1 is best 0 is worst. You can change by your wish, if you want you can score between 0-100 .
scaler.fit(cltv_final[[“clv”]])
cltv_final[“scaled_clv”] = scaler.transform(cltv_final[[“clv”]])
cltv_final.sort_values(by="scaled_clv", ascending=False).head()
#So let's observe it.

5. Создание сегментов с помощью CLTV

# Let's divide the customers into 4 groups:
cltv_final["segment"] = pd.qcut(cltv_final["scaled_clv"], 4, labels=["D", "C", "B", "A"])
cltv_final.head()

cltv_final.sort_values(by="scaled_clv", ascending=False).head(10)

Конец

Итак, с помощью CLV мы сегментировали наших клиентов, теперь мы можем генерировать разные маркетинговые стратегии для разных сегментов. Таким образом, мы можем более эффективно использовать наш маркетинговый бюджет.

Если вы хотите узнать больше о CLV, вы можете посмотреть мою другую статью:



Спасибо за чтение.

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку здесь.