Сегментация клиентов — это мощный инструмент для компаний, стремящихся оптимизировать свои усилия в области маркетинга и продаж. Разделяя клиентов на более мелкие группы на основе общих характеристик или поведения, компании могут адаптировать свои кампании к конкретным сегментам, потенциально повышая их эффективность. Одним из распространенных подходов к сегментации клиентов является использование алгоритмов кластеризации. Алгоритмы кластеризации группируют точки данных в кластеры на основе сходства между точками. Существует несколько популярных алгоритмов кластеризации, в том числе K-Means, иерархическая кластеризация и DBSCAN.

K-Means — это итеративный алгоритм, который делит набор данных на определенное количество кластеров в зависимости от расстояния от центра тяжести каждого кластера. Чтобы использовать K-Means для сегментации клиентов, предприятия могут вводить данные о клиентах в алгоритм и указывать количество кластеров, которые они хотят создать. Затем алгоритм сгруппирует клиентов в кластеры на основе общих характеристик.

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

DBSCAN (Пространственная кластеризация приложений с шумом на основе плотности) — это алгоритм, который группирует точки, которые плотно упакованы вместе, и помечает точки, которые изолированы как шум. Чтобы использовать DBSCAN для сегментации клиентов, предприятия могут вводить данные о клиентах в алгоритм и указывать минимальное количество точек, необходимых для формирования кластера, и максимальное расстояние между точками в кластере. Затем алгоритм сгруппирует клиентов в кластеры на основе общих характеристик.

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

А теперь попрактикуемся в Python.

Бизнес-задача: сегментация клиентского портфеля

FLO хочет сегментировать своих клиентов и определять маркетинговые стратегии в соответствии с этими сегментами. С этой целью будет определено поведение клиентов и сформированы группы в соответствии с кластерами этого поведения.

История набора данных: покупательское поведение клиентов FLO

Набор данных включает последние покупки Flo в OmniChannel (как онлайн, так и офлайн) в 2020–2021 годах. 12 переменных 19 945 наблюдений 2,7 МБ

  • master_id: уникальный номер клиента
  • order_channel: какой канал торговой платформы используется (Android, ios, Desktop, Mobile)
  • last_order_channel: канал, на котором была сделана последняя покупка
  • first_order_date: Дата первой покупки, сделанной покупателем.
  • last_order_date: дата последней покупки клиента.
  • last_order_date_online: дата последней покупки, сделанной клиентом на онлайн-платформе.
  • last_order_date_offline: дата последней покупки, сделанной клиентом на офлайн-платформе.
  • order_num_total_ever_online: общее количество покупок, сделанных клиентом на онлайн-платформе.
  • order_num_total_ever_offline: Общее количество покупок, совершенных клиентом в автономном режиме.
  • customer_value_total_ever_offline: общая сумма, уплаченная клиентом за офлайн-покупки.
  • customer_value_total_ever_online: общая сумма, уплаченная покупателем за покупки в Интернете.
  • интересуемый_in_categories_12: Список категорий, в которых покупатель совершил покупку за последние 12 месяцев.

1. Загрузка необходимых библиотек и данных

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans      # K-Means
from sklearn.preprocessing import MinMaxScaler
from yellowbrick.cluster import KElbowVisualizer     # Elbow method 
from scipy.cluster.hierarchy import linkage, dendrogram  # hierarchical clustering
from sklearn.preprocessing import StandardScaler    
from sklearn.preprocessing import LabelEncoder
from sklearn.cluster import AgglomerativeClustering     # hierarchical clustering

pd.set_option('display.max_columns', None)
pd.set_option('display.width', 500)
import warnings 
warnings.simplefilter(action='ignore', category=Warning)

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
df = pd.read_csv('/kaggle/input/flodata/flo_data_20k.csv')

df['tenure'] = (pd.to_datetime('today') - pd.to_datetime(df['first_order_date'])).dt.days
df['recency'] = (pd.to_datetime('today') - pd.to_datetime(df['last_order_date'])).dt.days
df['frequency'] = df['order_num_total_ever_online'] + df['order_num_total_ever_offline']
df['monetary'] = df['customer_value_total_ever_online'] + df['customer_value_total_ever_offline']

df["order_num_total"] = df["order_num_total_ever_online"] + df["order_num_total_ever_offline"]
df["customer_value_total"] = df["customer_value_total_ever_offline"] + df["customer_value_total_ever_online"]

2. Исследовательский анализ данных

def check_df(dataframe, head=5):
    print("##################### Shape #####################")
    print(dataframe.shape)
    print("##################### Types #####################")
    print(dataframe.dtypes)
    print("##################### Head #####################")
    print(dataframe.head(head))
    print("##################### Tail #####################")
    print(dataframe.tail(head))
    print("##################### NA #####################")
    print(dataframe.isnull().sum())
    print("##################### Quantiles #####################")
    print(dataframe.quantile([0, 0.05, 0.50, 0.95, 0.99, 1]).T)

check_df(df)
##################### Shape #####################
(19945, 18)
##################### Types #####################
master_id                             object
order_channel                         object
last_order_channel                    object
first_order_date                      object
last_order_date                       object
last_order_date_online                object
last_order_date_offline               object
order_num_total_ever_online          float64
order_num_total_ever_offline         float64
customer_value_total_ever_offline    float64
customer_value_total_ever_online     float64
interested_in_categories_12           object
tenure                                 int64
recency                                int64
frequency                            float64
monetary                             float64
order_num_total                      float64
customer_value_total                 float64
dtype: object
##################### Head #####################
                              master_id order_channel last_order_channel first_order_date last_order_date last_order_date_online last_order_date_offline  order_num_total_ever_online  order_num_total_ever_offline  customer_value_total_ever_offline  customer_value_total_ever_online       interested_in_categories_12  tenure  recency  frequency  monetary  order_num_total  customer_value_total
0  cc294636-19f0-11eb-8d74-000d3a38a36f   Android App            Offline       2020-10-30      2021-02-26             2021-02-21              2021-02-26                          4.0                           1.0                             139.99                            799.38                           [KADIN]     784      665        5.0    939.37              5.0                939.37
1  f431bd5a-ab7b-11e9-a2fc-000d3a38a36f   Android App             Mobile       2017-02-08      2021-02-16             2021-02-16              2020-01-10                         19.0                           2.0                             159.97                           1853.58  [ERKEK, COCUK, KADIN, AKTIFSPOR]    2144      675       21.0   2013.55             21.0               2013.55
2  69b69676-1a40-11ea-941b-000d3a38a36f   Android App        Android App       2019-11-27      2020-11-27             2020-11-27              2019-12-01                          3.0                           2.0                             189.97                            395.35                    [ERKEK, KADIN]    1122      756        5.0    585.32              5.0                585.32
3  1854e56c-491f-11eb-806e-000d3a38a36f   Android App        Android App       2021-01-06      2021-01-17             2021-01-17              2021-01-06                          1.0                           1.0                              39.99                             81.98               [AKTIFCOCUK, COCUK]     716      705        2.0    121.97              2.0                121.97
4  d6ea1074-f1f5-11e9-9346-000d3a38a36f       Desktop            Desktop       2019-08-03      2021-03-07             2021-03-07              2019-08-03                          1.0                           1.0                              49.99                            159.99                       [AKTIFSPOR]    1238      656        2.0    209.98              2.0                209.98
##################### Tail #####################
                                  master_id order_channel last_order_channel first_order_date last_order_date last_order_date_online last_order_date_offline  order_num_total_ever_online  order_num_total_ever_offline  customer_value_total_ever_offline  customer_value_total_ever_online interested_in_categories_12  tenure  recency  frequency  monetary  order_num_total  customer_value_total
19940  727e2b6e-ddd4-11e9-a848-000d3a38a36f   Android App            Offline       2019-09-21      2020-07-05             2020-06-05              2020-07-05                          1.0                           2.0                             289.98                            111.98          [ERKEK, AKTIFSPOR]    1189      901        3.0    401.96              3.0                401.96
19941  25cd53d4-61bf-11ea-8dd8-000d3a38a36f       Desktop            Desktop       2020-03-01      2020-12-22             2020-12-22              2020-03-01                          1.0                           1.0                             150.48                            239.99                 [AKTIFSPOR]    1027      731        2.0    390.47              2.0                390.47
19942  8aea4c2a-d6fc-11e9-93bc-000d3a38a36f       Ios App            Ios App       2019-09-11      2021-05-24             2021-05-24              2019-09-11                          2.0                           1.0                             139.98                            492.96                 [AKTIFSPOR]    1199      578        3.0    632.94              3.0                632.94
19943  e50bb46c-ff30-11e9-a5e8-000d3a38a36f   Android App        Android App       2019-03-27      2021-02-13             2021-02-13              2021-01-08                          1.0                           5.0                             711.79                            297.98          [ERKEK, AKTIFSPOR]    1367      678        6.0   1009.77              6.0               1009.77
19944  740998d2-b1f7-11e9-89fa-000d3a38a36f   Android App        Android App       2019-09-03      2020-06-06             2020-06-06              2019-09-03                          1.0                           1.0                              39.99                            221.98          [KADIN, AKTIFSPOR]    1207      930        2.0    261.97              2.0                261.97
##################### NA #####################
master_id                            0
order_channel                        0
last_order_channel                   0
first_order_date                     0
last_order_date                      0
last_order_date_online               0
last_order_date_offline              0
order_num_total_ever_online          0
order_num_total_ever_offline         0
customer_value_total_ever_offline    0
customer_value_total_ever_online     0
interested_in_categories_12          0
tenure                               0
recency                              0
frequency                            0
monetary                             0
order_num_total                      0
customer_value_total                 0
dtype: int64
##################### Quantiles #####################
                                     0.00    0.05     0.50      0.95       0.99      1.00
order_num_total_ever_online          1.00    1.00     2.00    10.000    20.0000    200.00
order_num_total_ever_offline         1.00    1.00     1.00     4.000     7.0000    109.00
customer_value_total_ever_offline   10.00   39.99   179.98   694.222  1219.9468  18119.14
customer_value_total_ever_online    12.99   63.99   286.46  1556.726  3143.8104  45220.13
tenure                             575.00  779.20  1221.00  2644.000  3175.0000   3630.00
recency                            572.00  579.00   681.00   905.000   930.0000    937.00
frequency                            2.00    2.00     4.00    12.000    22.0000    202.00
monetary                            44.98  175.48   545.27  1921.924  3606.3556  45905.10
order_num_total                      2.00    2.00     4.00    12.000    22.0000    202.00
customer_value_total                44.98  175.48   545.27  1921.924  3606.3556  45905.10
def cat_summary(dataframe, col_name, plot=False):
    print(pd.DataFrame({col_name: dataframe[col_name].value_counts(),
                        "Ratio": 100 * dataframe[col_name].value_counts() / len(dataframe)}))
    print("##########################################")
    if plot:
        sns.countplot(x=dataframe[col_name], data=dataframe)
        plt.show(block=True)
def num_summary(dataframe, numerical_col, plot=False):
    quantiles = [0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 0.95, 0.99]
    print(dataframe[numerical_col].describe(quantiles).T)

    if plot:
        dataframe[numerical_col].hist(bins=20)
        plt.xlabel(numerical_col)
        plt.title(numerical_col)
        plt.show(block=True)
def target_summary_with_num(dataframe, target, numerical_col):
    print(dataframe.groupby(target).agg({numerical_col: "mean"}), end="\n\n\n")

def target_summary_with_cat(dataframe, target, categorical_col):
    print(pd.DataFrame({"TARGET_MEAN": dataframe.groupby(categorical_col)[target].mean()}), end="\n\n\n")
def correlation_matrix(df, cols):
    fig = plt.gcf()
    fig.set_size_inches(10, 8)
    plt.xticks(fontsize=10)
    plt.yticks(fontsize=10)
    fig = sns.heatmap(df[cols].corr(), annot=True, linewidths=0.5, annot_kws={'size': 12}, linecolor='w', cmap='RdBu')
    plt.show(block=True)
  • df: это кадр данных, содержащий столбцы, для которых вы хотите рассчитать корреляции.
  • cols: это список имен столбцов, которые вы хотите включить в матрицу корреляции.
  • fig: Эта переменная хранит ссылку на текущий объект фигуры. Объект фигуры представляет общее окно или страницу, на которой будет отображаться график.
  • fig.set_size_inches: Этот метод устанавливает размер объекта фигуры в дюймах.
  • plt.xticks и plt.yticks: эти функции управляют внешним видом меток осей x и y.
  • sns.heatmap: Эта функция из библиотеки Seaborn создает тепловую карту, визуализирующую корреляции между столбцами в фрейме данных. Параметр annot указывает, следует ли аннотировать ячейки корреляциями, а параметр annot_kws управляет внешним видом аннотаций. Параметр linewidths управляет шириной линий между ячейками, а параметр linecolor задает цвет линий. Параметр cmap указывает цветовую карту, используемую для тепловой карты.
  • plt.show: Эта функция отображает график. Параметр block указывает, следует ли блокировать сценарий, пока окно графика не будет закрыто.
def grab_col_names(dataframe, cat_th=10, car_th=20):
    """
It gives the names of categorical, numerical and categorical but cardinal variables in the data set.
Note: Categorical variables with numerical appearance are also included in categorical variables.

     parameters
     ------
         dataframe: dataframe
                 The dataframe from which variable names are to be retrieved
         cat_th: int, optional
                 class threshold for numeric but categorical variables
         car_th: int, optinal
                 class threshold for categorical but cardinal variables

     Returns
     ------
         cat_cols: list
                 Categorical variable list
         num_cols: list
                 Numeric variable list
         cat_but_car: list
                 Categorical view cardinal variable list

     Examples
     ------
         import seaborn as sns
         df = sns.load_dataset("iris")
         print(grab_col_names(df))


     notes
     ------
         cat_cols + num_cols + cat_but_car = total number of variables
         num_but_cat is inside cat_cols.
         The sum of the 3 returned lists equals the total number of variables: cat_cols + num_cols + cat_but_car = number of variables

    """

    # cat_cols, cat_but_car
    cat_cols = [col for col in dataframe.columns if dataframe[col].dtypes == "O"]
    num_but_cat = [col for col in dataframe.columns if dataframe[col].nunique() < cat_th and
                   dataframe[col].dtypes != "O"]
    cat_but_car = [col for col in dataframe.columns if dataframe[col].nunique() > car_th and
                   dataframe[col].dtypes == "O"]
    cat_cols = cat_cols + num_but_cat
    cat_cols = [col for col in cat_cols if col not in cat_but_car]

    # num_cols
    num_cols = [col for col in dataframe.columns if dataframe[col].dtypes != "O"]
    num_cols = [col for col in num_cols if col not in num_but_cat]

    # print(f"Observations: {dataframe.shape[0]}")
    # print(f"Variables: {dataframe.shape[1]}")
    # print(f'cat_cols: {len(cat_cols)}')
    # print(f'num_cols: {len(num_cols)}')
    # print(f'cat_but_car: {len(cat_but_car)}')
    # print(f'num_but_cat: {len(num_but_cat)}')
    return cat_cols, num_cols, cat_but_car
     
    cat_cols, num_cols, cat_but_car = grab_col_names(df, cat_th=5, car_th=20)
    print(cat_cols)
    print(num_cols)
    print(cat_but_car)
  1. Сначала функция создает список cat_cols столбцов, которые являются категориальными переменными (т. е. их тип данных — «O» для «объекта»).
  2. Затем он создает список num_but_cat столбцов, которые являются числовыми, но категориальными (т. е. имеют менее cat_th уникальных значений).
  3. Затем он создает список cat_but_car столбцов, которые являются категориальными, но количественными (т. е. имеют более car_th уникальных значений).
  4. Он добавляет список num_but_cat в список cat_cols и удаляет все имена столбцов, которые также находятся в списке cat_but_car.
  5. Он создает список num_cols числовых переменных, которых нет в списке num_but_cat.

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

cat_cols
['order_channel', 'last_order_channel']
num_cols
['order_num_total_ever_online',
 'order_num_total_ever_offline',
 'customer_value_total_ever_offline',
 'customer_value_total_ever_online',
 'tenure',
 'recency',
 'frequency',
 'monetary',
 'order_num_total',
 'customer_value_total']
# Correlation of numerical variables with each other
correlation_matrix(df, num_cols)

3. Предварительная обработка данных и разработка функций

def outlier_thresholds(dataframe, col_name, q1=0.25, q3=0.75):
    quartile1 = dataframe[col_name].quantile(q1)
    quartile3 = dataframe[col_name].quantile(q3)
    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

def check_outlier(dataframe, col_name, q1=0.25, q3=0.75):
    low_limit, up_limit = outlier_thresholds(dataframe, col_name, q1, q3)
    if dataframe[(dataframe[col_name] > up_limit) | (dataframe[col_name] < low_limit)].any(axis=None):
        return True
    else:
        return False

def one_hot_encoder(dataframe, categorical_cols, drop_first=False):
    dataframe = pd.get_dummies(dataframe, columns=categorical_cols, drop_first=drop_first)
    return dataframe

def label_encoder(dataframe, binary_col):
    labelencoder = LabelEncoder()
    dataframe[binary_col] = labelencoder.fit_transform(dataframe[binary_col])
    return dataframe

binary_cols = [col for col in df.columns if df[col].dtype not in [int, float]
               and df[col].nunique() == 2]
for col in binary_cols:
    df = label_encoder(df, col)

def one_hot_encoder(dataframe, categorical_cols, drop_first=True):
    dataframe = pd.get_dummies(dataframe, columns=categorical_cols, drop_first=drop_first)
    return dataframe

ohe_cols = [col for col in df.columns if
            25 >= df[col].nunique() > 2]

df = one_hot_encoder(df, ohe_cols)
cat_cols, num_cols, cat_but_car = grab_col_names(df)
  • outlier_thresholds: эта функция принимает фрейм данных, имя столбца и необязательные параметры q1 и q3 (значения по умолчанию — 0,25 и 0,75 соответственно). Он вычисляет межквартильный диапазон (IQR) столбца и возвращает верхний и нижний пределы для обнаружения выбросов на основе IQR. Выброс определяется как значение, которое более чем в 1,5 раза превышает IQR выше верхнего квартиля или ниже нижнего квартиля.
  • replace_with_thresholds: эта функция принимает фрейм данных и имя столбца. Он вызывает функцию outlier_thresholds, чтобы получить верхний и нижний пределы для обнаружения выбросов в столбце. Затем он заменяет все значения в столбце, которые выше верхнего предела или ниже нижнего предела, на соответствующий предел.
  • check_outlier: эта функция принимает фрейм данных, имя столбца и необязательные параметры q1 и q3 (значения по умолчанию — 0,25 и 0,75 соответственно). Он вызывает функцию outlier_thresholds, чтобы получить верхний и нижний пределы для обнаружения выбросов в столбце. Затем он проверяет, есть ли в столбце какие-либо значения выше верхнего предела или ниже нижнего предела, и возвращает True, если они найдены, или False, если ничего не найдено.
  • one_hot_encoder: эта функция принимает кадр данных, список имен столбцов и необязательный параметр drop_first (значение по умолчанию — True). Он преобразует категориальные переменные в указанных столбцах в фиктивные переменные, используя функцию pd.get_dummies из библиотеки pandas. Параметр drop_first указывает, следует ли отбрасывать первую фиктивную переменную в каждом столбце, чтобы избежать ловушки фиктивной переменной.
  • label_encoder: эта функция принимает фрейм данных и имя столбца. Он преобразует значения в указанном столбце в числовые значения, используя класс LabelEncoder из библиотеки sklearn.
  • binary_cols: Эта строка кода создает список имен столбцов для двоичных переменных (т. е. переменных только с двумя уникальными значениями) в фрейме данных.
  • label_encoder: Эта строка кода вызывает функцию label_encoder для каждого столбца в списке binary_cols, преобразуя значения в этих столбцах в числовые значения.
  • one_hot_encoder: эта строка кода вызывает функцию one_hot_encoder для фрейма данных, используя список имен столбцов для категориальных переменных с относительно небольшим количеством уникальных значений.
  • grab_col_names: эта строка кода вызывает функцию grab_col_names в кадре данных, возвращая списки имен столбцов для категориальных переменных, числовых переменных и категориальных переменных, которые также являются кардинальными (имеют большое количество уникальных значений).
df.shape
(19945, 23)

4. Сегментация клиентов с помощью K-Means

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

sc = StandardScaler()
X = sc.fit_transform(df[num_cols])
X = pd.DataFrame(X, columns=num_cols)
X.head()

Для работы алгоритма K-средних необходимо определить оптимальные кластеры. Для этого можно использовать метод локтя. Для метода локтя необходимо рассчитать значения WCSS кластеров. Значения WCSS уменьшаются с увеличением количества кластеров. Глядя на график этих значений, снижение замедляется после точки. После этого момента увеличить количество кластеров уже не получится. Эта точка называется точкой локтя.

wcss = []  # We created a list to hold WCSS values.
for k in range(1, 15):  # We looped the numbers from 1 to 15.
    kmeans = KMeans(n_clusters=k).fit(X)  # We ran the K-Means algorithm.
    wcss.append(kmeans.inertia_)  # We added the WCSS values to the wcss list.

plt.plot(range(1, 15), wcss, 'bx-')  # We plotted the WCSS values.
plt.xlabel('k values')
plt.ylabel('WCSS')  
plt.title('The Elbow Method')  
plt.show()

kmeans = KMeans()
elbow = KElbowVisualizer(kmeans, k=(2, 20))
elbow.fit(X)
elbow.show(block=True)

elbow.elbow_value_

kmeans = KMeans(n_clusters=elbow.elbow_value_, init='k-means++').fit(X)
kmeans.cluster_centers_  # Indicates the centers of clusters.
kmeans.n_clusters  # Indicates the number of clusters.
8
kmeans.labels_
array([6, 4, 2, ..., 6, 7, 2], dtype=int32)
kmeans.inertia_  # Displays the WCSS value.
65766.97770803791
kmeans.get_params()  # With get_params() we can see the parameters of the kmeans model.
{'algorithm': 'auto',
 'copy_x': True,
 'init': 'k-means++',
 'max_iter': 300,
 'n_clusters': 8,
 'n_init': 10,
 'random_state': None,
 'tol': 0.0001,
 'verbose': 0}

Создайте свою модель и сегментируйте своих клиентов

clusters_kmeans = kmeans.labels_    # Indicates which observation the clusters belong to.
X["cluster"] = clusters_kmeans   # We added a variable named cluster_no to X.
X.head()

X.groupby('cluster').agg(['mean', 'median', 'count', 'std']).T

X['cluster'] = X['cluster'] + 1
X.head()

X["cluster"].value_counts()                 # It shows the number of observations belonging to each cluster.
X["cluster"].value_counts() / len(X) * 100
7    43.474555
3    27.480572
4     9.917272
8     8.578591
1     8.373026
5     2.070694
6     0.070193
2     0.035097
Name: cluster, dtype: float64
sns.countplot(x='cluster', data=X)
plt.show()

5. Сегментация клиентов с помощью иерархической кластеризации

Метод иерархической кластеризации (HC) – это метод кластеризации, используемый для разделения точек данных на группы со схожими характеристиками. Этот метод используется для группировки точек данных друг о друге. Каждая из этих групп называется кластером.

Метод HC также называют методом матрицы ссылок. Этот метод используется для группировки точек данных в соответствии со степенью их связи друг с другом. Этот метод создает матрицу связности для измерения степени взаимосвязи между точками данных. Эта матрица показывает степени связности между точками данных и группирует точки данных в соответствии с этими степенями.

linkage_matrix = linkage(X, method='ward')

# Create the dendrogram
dend = dendrogram(linkage_matrix)

# Show the dendrogram
plt.show()

В этом фрагменте создается матрица ссылок, которая измеряет степень сходства точек данных с помощью функции linkage() библиотеки scipy. Затем, используя эту матрицу ссылок, создается дендограмма методом HC с использованием функции dendrogram() библиотеки scipy. Эта дендограмма отображается с помощью функции show() библиотеки matplotlib. В этом фрагменте параметр метода указан как «ward», поэтому в качестве метода HC используется метод hc_ward. Значение этого параметра может принимать различные значения, такие как «одиночное», «полное» или «среднее», и в соответствии с этими значениями можно получить дендограмму, применяя метод HC различными методами.

hc_average = linkage(X, "average")  # We created a connection matrix with the average method.
hc_ward = linkage(X, "ward")  # We created a connection matrix with the ward method.
hc_complete = linkage(X, "complete")  # We created a connection matrix with the complete method.
hc_single = linkage(X, "single")  # We created a connection matrix with the single method.
hc_centroid = linkage(X, "centroid")  # We created a connection matrix with the centroid method.
plt.figure(figsize=(7, 5))
plt.title("Hierarchical Clustering Dendrogram")
plt.xlabel("Observation Units")
plt.ylabel("Distances")
dendrogram(hc_average,
           truncate_mode="lastp",
           p=10,
           show_contracted=True,
           leaf_font_size=10)
plt.show()

Мы используем класс AgglomerativeClustering из scikit-learn для выполнения иерархической кластеризации набора данных X. Мы устанавливаем количество кластеров равным 3 и используем евклидово расстояние и метод связывания Уорда для кластеризации.

Метод fit_predict подбирает модель к данным и возвращает метки кластеров для каждой выборки в наборе данных. Метки кластера хранятся в массиве clusters_hc.

После запуска этого кода мы можем использовать массив clusters_hc, чтобы увидеть метки кластеров для каждой выборки в наборе данных. Например, если первая выборка имеет метку кластера 0, она принадлежит первому кластеру. Если второй образец имеет метку кластера 1, он принадлежит второму кластеру и так далее.

hc = AgglomerativeClustering(n_clusters=3, affinity='euclidean', linkage='ward')
clusters_hc = hc.fit_predict(X)
clusters_hc
X.groupby('cluster_hc').agg(['mean', 'median', 'count', 'std']).T

X["cluster_hc"].value_counts()                 # It shows the number of observations belonging to each cluster.
X["cluster_hc"].value_counts() / len(X) * 100
2    52.053146
3    35.853597
1    12.093256
Name: cluster_hc, dtype: float64
sns.countplot(x='cluster_hc', data=X)
plt.show()

6. Сегментация клиентов с помощью DBSCAN

DBSCAN (Пространственная кластеризация приложений с шумом на основе плотности) — это алгоритм кластеризации, основанный на плотности точек данных. Его можно использовать для сегментации клиентов путем поиска групп похожих клиентов на основе их характеристик или атрибутов.

from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.8, min_samples=15)
clusters = dbscan.fit_predict(X)
print(clusters)
df['dbscan_cluster'] = clusters
df.head()

Используйте класс DBSCAN из scikit-learn, чтобы подогнать модель под ваши данные. Вам нужно указать два важных параметра: eps и min_samples. eps — максимальное расстояние между двумя точками в одном кластере, а min_samples — минимальное количество точек, необходимое для формирования кластера.

X["cluster_dbscan"].value_counts()
1    8662
2    5451
0    2215
5    1289
3    1170
4    1148
6      10
Name: cluster_dbscan, dtype: int64
sns.countplot(x='cluster_dbscan', data=X)
plt.show()

Заключение

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

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

Пожалуйста, не стесняйтесь связаться со мной, если вам нужна дополнительная информация.

Рекомендации

  1. https://miuul.com/makine-ogrenmesi
  2. https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html
  3. https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html