Задача кластеризации подразумевает объединение наблюдений в группы так, чтобы все члены группы были похожи друг на друга, но в то же время они заметно отличались от всех членов других групп.
Давайте посмотрим на пример. Перед вами игрушечный data frame.
В каждой строчке у нас наблюдения, а столбцы это переменные, которые описывают наши наблюдения. Можно представить, что наблюдения это люди, у которых есть пронумерованные карты от 1 до 10. А переменные это описание масти каждой пронумерованной карты. Можете ли вы разбить этих людей на группы? Почему вы так решили?
На самом деле этих людей можно разбить на 3 группы (синие, зеленые и желтые). Можно показать их вместе, чтобы было более понятно почему они разбиты именно на эти 3 группы.
Люди в каждой группе имеют одинаковые масти пронумерованных карт. Например, в “желтой” группе у всех 2 карта это черви.
Кластеризация зачастую это вспомогательная модель, которая может помочь нам достичь нашей цели. Пример из ритейл. Магазин решил повысить цены так, чтобы увеличить выручку компании. Но повышать цену на все товары очень глупо, так как покупатели могут просто уйти в другой магазин, тем самым продажи магазина упадут. В итоге мы получим не увеличение выручки, а ее уменьшение. Но товары можно разбить, например, на две группы: эластичные и неэластичные. Продажи неэластичных товаров при увеличении цены падают не так сильно как у эластичных. Здесь задача кластеризации состоит в том, чтобы разбить все товары на 2 группы.
Чтобы решить задачу кластеризации, нужно выполнить следующие этапы.
Насколько похожи два отдельно взятых наблюдения? Наверное, это один из главных вопросов, на который нужно ответить. Обычно в кластеризации для ответа на этот вопрос используют метрики расстояния. Чем меньше расстояние между наблюдениями тем больше они похожи друг на друга.
Давайте вспомним одну из метрик расстояния, которое мы проходили еще в школе. Это евклидово расстояние. Предположим, у нас есть 3 точки с координатами x и y.
Найдем евклидово расстояние между первой и второй точками по формуле:
\[ d(p_1, p_2) = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} \]
В R есть встроенная функция dist
, которая находит расстояние между всеми объектами.
## 1 2 3
## 1 0.000000 5.099020 4.472136
## 2 5.099020 0.000000 1.414214
## 3 4.472136 1.414214 0.000000
Изобразим их.
df %>%
mutate(name = paste0("p", 1:nrow(df))) %>%
ggplot(aes(x = x, y = y, label = name)) +
geom_point() +
geom_text(hjust = 0, nudge_x = 0.05) +
theme_light()
Если мы работаем с большим количество координат, то формула будет следующей:
\[ p_i = (p_1^i, ..., p_n^i) \]
\[ d(p_i, p_j) = \sqrt{(p_1^i - p_1^j)^2 + ... (p_n^i - p_n^j)^2} = \sqrt{\sum_{k=1}^{n}{(p_k^i - p_k^j)^2}} \]
Давайте разберемся почему скалирование признаков очень важно для нас. Предположим, что мы хотим кластеризовать лиюдей по росту и весу. Рост у нас будет в футах (ft), а вес в английских фунтах (lbs).
Второй или третий ближе к первому?
## 1 2
## 2 2.000000
## 3 2.000000 2.828427
Логически 2 намного ближе к 1 чем 3. Но если посчитать евклидово растояние, то получится, что растояния будут равны. Это произошло из-за того, что наши признаки (рост и вес) имеют разный масштаб. Чтобы избавиться от масшатаба, нужно отскалировать наши признаки.
Самые известные способы скалирования:
\[ feature_{scaled} = \frac{feature - mean(feature)}{sd(feature)} \]
Чтобы выполнить такое скалирование, понадобится встроенная функция scale
.
## # A tibble: 3 x 2
## h[,1] w[,1]
## <dbl> <dbl>
## 1 -0.577 -0.577
## 2 -0.577 1.15
## 3 1.15 -0.577
## 1 2
## 2 1.732051
## 3 1.732051 2.449490
У вас может быть очень много признаков, но это не значит, что нужно их все использовать в кластеризации. Важно выкинуть переменные, которые могут быть просто шумом.