Сегментация клиентов в Python Практический подход

Python в действии Практический подход к сегментации клиентов

 

Сегментация клиентов может помочь бизнесам настраивать свои маркетинговые усилия и повышать удовлетворенность клиентов. Вот как.

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

В этом учебнике мы исследуем сегментацию клиентов с использованием Python, комбинируя две основные техники: RFM анализ (Recency, Frequency, Monetary) и K-Means кластеризацию. RFM анализ предоставляет структурированный фреймворк для оценки поведения клиентов, а K-Means кластеризация предлагает основанный на данных подход к группировке клиентов в значимые сегменты. Мы будем работать с реальными данными из розничной отрасли: набор данных Online Retail из репозитория машинного обучения UCI.

От предварительной обработки данных до анализа и визуализации кластеров, мы будем писать код на каждом шаге. Итак, приступим!

 

Наш подход: RFM анализ и K-Means кластеризация

 

Давайте начнем с постановки цели: применяя RFM анализ и K-Means кластеризацию к этому набору данных, мы хотим получить инсайты в поведение и предпочтения клиентов.

RFM анализ – это простой, но мощный метод для количественной оценки поведения клиентов. Он оценивает клиентов на основе трех ключевых измерений:

  • Recency (R): Как недавно конкретный клиент совершил покупку?
  • Frequency (F): Как часто они совершают покупки?
  • Monetary Value (M): Сколько денег они тратят?

Мы используем информацию в наборе данных для вычисления значений для R, F и M. Затем мы сопоставим эти значения с общепринятой шкалой оценок RFM от 1 до 5.

Если вы хотите, вы можете дополнительно исследовать и анализировать с использованием этих оценок RFM. Но мы попытаемся выявить сегменты клиентов с похожими RFM характеристиками. И для этого мы используем K-Means кластеризацию, алгоритм машинного обучения без учителя, который группирует похожие точки данных в кластеры.

Итак, приступим к написанию кода!

🔗 Ссылка на блокнот Google Colab.

 

Шаг 1 – Импорт необходимых библиотек и модулей

 

Сначала давайте импортируем необходимые библиотеки и конкретные модули по мере необходимости:

import pandas as pdimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeans

 

Нам понадобятся pandas и matplotlib для исследования и визуализации данных, а класс KMeans из модуля cluster scikit-learn для выполнения K-Means кластеризации.

 

Шаг 2 – Загрузка набора данных

 

Как упоминалось ранее, мы будем использовать набор данных Online Retail. Набор данных содержит записи о клиентах: транзакционную информацию, включая даты покупок, количество, цены и идентификаторы клиентов.

Давайте считаем данные из оригинального файла Excel по его URL в pandas dataframe.

# Загрузка набора данных из репозитория UCIurl = "https://archive.ics.uci.edu/ml/machine-learning-databases/00352/Online%20Retail.xlsx"data = pd.read_excel(url)

 

В качестве альтернативы вы можете скачать набор данных и считать файл Excel в pandas dataframe.

 

Шаг 3 – Исследование и очистка набора данных

 

Теперь давайте начнем изучать набор данных. Посмотрите на первые несколько строк набора данных:

data.head()

 

 

Теперь вызовите метод describe() для фрейма данных, чтобы лучше понять числовые признаки:

data.describe()

 

Мы видим, что столбец «CustomerID» в настоящее время представляет собой значение с плавающей запятой. При очистке данных мы приведем его к целому числу:  

Также обратите внимание, что набор данных довольно шумный. Столбцы «Quantity» и «UnitPrice» содержат отрицательные значения:

  

Давайте подробнее рассмотрим столбцы и их типы данных:

data.info()

 

Мы видим, что в наборе данных более 541 тыс. записей, а столбцы «Description» и «CustomerID» содержат пропущенные значения:  Давайте получим количество пропущенных значений в каждом столбце:

# Проверяем наличие пропущенных значений в каждом столбце missing_values = data.isnull().sum()print(missing_values)

 

Как и ожидалось, столбцы «CustomerID» и «Description» содержат пропущенные значения: 

  

Для нашего анализа нам не нужно описание продукта, содержащееся в столбце «Description». Однако нам нужен «CustomerID» для следующих шагов в нашем анализе. Поэтому давайте удалим записи с отсутствующим «CustomerID»:

# Удалить строки с отсутствующими CustomerIDdata.dropna(subset=['CustomerID'], inplace=True)

 

Также вспомним, что значения столбцов «Quantity» и «UnitPrice» должны быть строго неотрицательными. Но они содержат отрицательные значения. Так что давайте также удалим записи с отрицательными значениями для «Quantity» и «UnitPrice»:

# Удалить строки с отрицательными Quantity и Pricedata = data[(data['Quantity'] > 0) & (data['UnitPrice'] > 0)]

 

Также преобразуем «CustomerID» в целое число:

data['CustomerID'] = data['CustomerID'].astype(int)# Проверка преобразования типа данныхprint(data.dtypes)

 

 

Шаг 4 – Рассчет Recency, Frequency и Monetary Value

 

Начнем с определения сравнительной даты snapshot_date, которая находится на день позже наиболее поздней даты в столбце «InvoiceDate»:

snapshot_date = max(data['InvoiceDate']) + pd.DateOffset(days=1)

 

Затем создайте столбец «Total», который содержит Quantity * UnitPrice для всех записей:

data['Total'] = data['Quantity'] * data['UnitPrice']

 

Для расчета Recency, Frequency и MonetaryValue мы вычисляем следующее – сгруппированное по CustomerID:

  • Для Recency мы вычисляем разницу между самой последней датой покупки и сравнительной датой (snapshot_date). Это дает количество дней с момента последней покупки клиента. Таким образом, меньшие значения указывают на то, что клиент недавно совершил покупку. Но когда речь идет о значениях Recency, мы бы хотели, чтобы клиенты, которые недавно покупали, имели более высокий балл Recency, верно? Мы справимся с этим на следующем шаге.
  • Поскольку Frequency показывает, насколько часто клиент совершает покупки, мы вычислим его как общее количество уникальных счетов или транзакций, совершенных каждым клиентом.
  • Monetary value количественно представляет сумму денег, которую клиент тратит. Поэтому мы найдем среднее значение общей денежной стоимости по транзакциям.
rfm = data.groupby('CustomerID').agg({   'InvoiceDate': lambda x: (snapshot_date - x.max()).days,   'InvoiceNo': 'nunique',   'Total': 'sum'})

 

Давайте переименуем столбцы для удобочитаемости:

rfm.rename(columns={'InvoiceDate': 'Recency', 'InvoiceNo': 'Frequency', 'Total': 'MonetaryValue'}, inplace=True)rfm.head()

 

 

Шаг 5 – Отобразить значения RFM на шкалу от 1 до 5

 

Теперь давайте отобразим значения столбцов “Recency”, “Frequency” и “MonetaryValue” на шкалу от 1 до 5; одно из {1,2,3,4,5}.

Суть в том, чтобы присвоить значения пяти разным группам и отобразить каждую группу на значение. Для определения границ групп давайте воспользуемся квантильными значениями столбцов “Recency”, “Frequency” и “MonetaryValue”:

rfm.describe()

 

 

Вот как мы определяем границы групп:

# Рассчитываем пользовательские границы групп для оценок Recency, Frequency и Monetary scoresrecency_bins = [rfm['Recency'].min()-1, 20, 50, 150, 250, rfm['Recency'].max()]frequency_bins = [rfm['Frequency'].min() - 1, 2, 3, 10, 100, rfm['Frequency'].max()]monetary_bins = [rfm['MonetaryValue'].min() - 3, 300, 600, 2000, 5000, rfm['MonetaryValue'].max()]

 

Теперь, когда мы определили границы групп, давайте отобразим оценки соответствующим меткам в диапазоне от 1 до 5 (включительно):

# Рассчитываем оценку Recency на основе пользовательских границ rfm['R_Score'] = pd.cut(rfm['Recency'], bins=recency_bins, labels=range(1, 6), include_lowest=True)# Реверсируем оценки Recency, чтобы более высокие значения указывали на более недавние покупкиrfm['R_Score'] = 5 - rfm['R_Score'].astype(int) + 1# Рассчитываем оценки Frequency и Monetary на основе пользовательских границrfm['F_Score'] = pd.cut(rfm['Frequency'], bins=frequency_bins, labels=range(1, 6), include_lowest=True).astype(int)rfm['M_Score'] = pd.cut(rfm['MonetaryValue'], bins=monetary_bins, labels=range(1, 6), include_lowest=True).astype(int)

 

Обратите внимание, что R_Score на основе групп равен 1 для недавних покупок и 5 для всех покупок, сделанных более 250 дней назад. Но мы хотим, чтобы самые недавние покупки имели R_Score равный 5, а покупки, сделанные более 250 дней назад, имели R_Score равный 1.

Чтобы получить желаемое отображение, мы делаем: 5 - rfm['R_Score'].astype(int) + 1.

Давайте посмотрим на первые несколько строк столбцов R_Score, F_Score и M_Score:

# Выводим первые несколько строк DataFrame RFM для проверки оценокprint(rfm[['R_Score', 'F_Score', 'M_Score']].head(10))

 

 

При желании вы можете использовать эти оценки R, F и M для проведения глубокого анализа или использовать кластеризацию для выявления сегментов с похожими характеристиками RFM. Мы выберем последнее!

 

Шаг 6 – Выполнение кластеризации методом K-средних

 

Кластеризация K-Means чувствительна к масштабу признаков. Поскольку значения R, F и M находятся на одном масштабе, мы можем продолжать выполнять кластеризацию без дальнейшего масштабирования признаков. 

Давайте извлечем оценки R, F и M для выполнения кластеризации K-Means:

# Извлечь оценки RFM для кластеризации K-meansX = rfm[['R_Score', 'F_Score', 'M_Score']]

 

Затем нам нужно найти оптимальное количество кластеров. Для этого давайте запустим алгоритм K-Means для ряда значений K и используем метод elbow, чтобы выбрать оптимальный К:

# Рассчитать инерцию (сумму квадратов расстояний) для разных значений kinertia = []for k in range(2, 11):    kmeans = KMeans(n_clusters=k, n_init=10, random_state=42)    kmeans.fit(X)    inertia.append(kmeans.inertia_)# Построить график elbow кривойplt.figure(figsize=(8, 6), dpi=150)plt.plot(range(2, 11), inertia, marker='o')plt.xlabel('Количество кластеров (k)')plt.ylabel('Инерция')plt.title('График elbow для кластеризации K-Means')plt.grid(True)plt.show()

 

Мы видим, что кривая сгибается при 4 кластерах. Итак, давайте разделим базу клиентов на четыре сегмента.

  

Мы установили K равным 4. Итак, давайте запустим алгоритм K-Means, чтобы получить назначения кластеров для всех точек в наборе данных:

# Выполнить кластеризацию K-means с лучшим Kbest_kmeans = KMeans(n_clusters=4, n_init=10, random_state=42)rfm['Cluster'] = best_kmeans.fit_predict(X)

 

Шаг 7 – Интерпретация кластеров для выявления сегментов клиентов

 

Теперь, когда у нас есть кластеры, давайте попытаемся их характеризовать на основе оценок RFM.

# Сгруппировать по кластеру и рассчитать средние значенияcluster_summary = rfm.groupby('Cluster').agg({    'R_Score': 'mean',    'F_Score': 'mean',    'M_Score': 'mean'}).reset_index()

 

Средние значения оценок R, F и M для каждого кластера уже должны дать вам представление о характеристиках. 

print(cluster_summary)

 

 

Но давайте визуализируем средние значения оценок R, F и M для кластеров, чтобы было легче интерпретировать:

colors = ['#3498db', '#2ecc71', '#f39c12','#C9B1BD']# Построить среднюю оценку RFM для каждого кластерапр_figure(figsize=(10, 8),dpi=150)# Построить среднюю оценку Recencyplt.subplot(3, 1, 1)bars = plt.bar(cluster_summary.index, cluster_summary['R_Score'], color=colors)plt.xlabel('Кластер')plt.ylabel('Средний Recency')plt.title('Средний Recency для каждого кластера')plt.grid(True, linestyle='--', alpha=0.5)plt.legend(bars, cluster_summary.index, title='Кластеры')# Построить среднюю оценку Frequencyplt.subplot(3, 1, 2)bars = plt.bar(cluster_summary.index, cluster_summary['F_Score'], color=colors)plt.xlabel('Кластер')plt.ylabel('Средняя Frequency')plt.title('Средняя Frequency для каждого кластера')plt.grid(True, linestyle='--', alpha=0.5)plt.legend(bars, cluster_summary.index, title='Кластеры')# Построить среднюю оценку Monetaryplt.subplot(3, 1, 3)bars = plt.bar(cluster_summary.index, cluster_summary['M_Score'], color=colors)plt.xlabel('Кластер')plt.ylabel('Средний Monetary')plt.title('Средняя денежная единица для каждого кластера')plt.grid(True, linestyle='--', alpha=0.5)plt.legend(bars, cluster_summary.index, title='Кластеры')plt.tight_layout()plt.show()

 

 

Заметьте, как клиенты в каждом из сегментов могут быть характеризованы на основе значений актуальности, частоты и денежных стоимостей:

  • Кластер 0: Из всех четырех кластеров, этот кластер имеет наивысшую актуальность, частоту и денежные значения. Давайте назовем клиентов в этом кластере чемпионами (или сильными покупателями).
  • Кластер 1: Этот кластер характеризуется умеренной актуальностью, частотой и денежными значениями. Эти клиенты все еще тратят больше и покупают чаще, чем кластеры 2 и 3. Давайте назовем их лояльными клиентами.
  • Кластер 2: Клиенты в этом кластере обычно тратят меньше. Они не часто покупают и также не совершали покупок в последнее время. Скорее всего, это неактивные или находящиеся в риске клиенты.
  • Кластер 3: Этот кластер характеризуется высокой актуальностью, относительно нижней частотой и умеренными денежными значениями. Таким образом, это недавние клиенты, которые могут стать долгосрочными клиентами.

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

  • Для Чемпионов/Сильных покупателей: Предлагайте персонализированные специальные скидки, привилегии доступа и другие премиальные льготы, чтобы они чувствовали себя ценными и оцененными.
  • Для Лояльных клиентов: Кампании по оценке, бонусы за рекомендацию и вознаграждения за лояльность.
  • Для Клиентов, находящихся в риске: Усилия по повторному привлечению, включающие скидки или акции, чтобы стимулировать покупки.
  • Для Недавних клиентов: Целевые кампании, позволяющие им узнать о бренде и предоставляющие скидки на последующие покупки.

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

Давайте визуализируем распределение различных кластеров с помощью круговой диаграммы:

cluster_counts = rfm['Cluster'].value_counts()colors = ['#3498db', '#2ecc71', '#f39c12','#C9B1BD']# Вычислить общее количество клиентовtotal_customers = cluster_counts.sum()# Вычислить процент клиентов в каждом кластереpercentage_customers = (cluster_counts / total_customers) * 100labels = ['Чемпионы (сильные покупатели)','Лояльные клиенты','Клиенты, находящиеся в риске','Недавние клиенты']# Создать круговую диаграммуplt.figure(figsize=(8, 8),dpi=200)plt.pie(percentage_customers, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors)plt.title('Процент клиентов в каждом кластере')plt.legend(cluster_summary['Cluster'], title='Кластер', loc='upper left')plt.show()

 

 

Вот и все! В этом примере у нас достаточно равномерное распределение клиентов по сегментам. Поэтому мы можем инвестировать время и усилия в удержание существующих клиентов, повторное привлечение клиентов, находящихся в риске, и обучение недавних клиентов.

 

Сводка

 

И это все! Мы прошли путь от более чем 154 тыс. записей о клиентах к 4 кластерам за 7 простых шагов. Я надеюсь, вы понимаете, как клиентская сегментация позволяет принимать решения, основанные на данных, которые влияют на рост бизнеса и удовлетворенность клиентов, обеспечивая:

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

В качестве следующего шага попробуйте применить этот подход к другому набору данных, задокументируйте свой путь и поделитесь с сообществом! Но помните, что эффективная сегментация клиентов и организация целевых кампаний требует хорошего понимания вашей клиентской базы и ее эволюции. Поэтому требуется периодический анализ для усовершенствования стратегий со временем.

 

Благодарность за набор данных

Датасет Онлайн Розница распространяется на условиях лицензии Creative Commons Attribution 4.0 International (CC BY 4.0):

Онлайн-розница. (2015). Репозиторий машинного обучения UCI. https://doi.org/10.24432/C5BW33. Bala Priya C – разработчик и технический писатель из Индии. Ей нравится работать в пересечении математики, программирования, науки о данных и создания контента. Ее областями интереса и экспертизы являются DevOps, наука о данных и обработка естественного языка. Она любит чтение, письмо, кодирование и кофе! В настоящее время она работает над изучением и обменом своими знаниями с сообществом разработчиков, создавая учебники, руководства, мнения и многое другое.

[Bala Priya C](https://twitter.com/balawc27) – разработчик и технический писатель из Индии. Ей нравится работать в пересечении математики, программирования, науки о данных и создания контента. Ее областями интереса и экспертизы являются DevOps, наука о данных и обработка естественного языка. Она любит чтение, письмо, кодирование и кофе! В настоящее время она работает над изучением и обменом своими знаниями с сообществом разработчиков, создавая учебники, руководства, мнения и многое другое.