иерархическая кластеризация с расстоянием Гауэра — hclust() и philentropy::distance()

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

Я основываю свой код на примере с https://www.r-bloggers.com/hierarchical-clustering-in-r-2/, где используется основание R dist() для евклидова расстояния. Поскольку dist() не вычисляет расстояние Гауэра, я попытался использовать philentropy::distance() для его вычисления, но это не сработало.

Спасибо за любую помощь!

# Data
data("mtcars")
mtcars$cyl <- as.factor(mtcars$cyl)

# Hierarchical clustering with Euclidean distance - works 
clusters <- hclust(dist(mtcars[, 1:2]))
plot(clusters)

# Hierarchical clustering with Gower distance - doesn't work
library(philentropy)
clusters <- hclust(distance(mtcars[, 1:2], method = "gower"))
plot(clusters)

person LLL    schedule 24.06.2018    source источник
comment
Функция philentropy::distance возвращает матрицу, попробуйте преобразовать ее в объект dist с помощью as.dist перед кластеризацией   -  person Esther    schedule 24.06.2018
comment
Я попробовал кластеры ‹- hclust(as.dist(distance(mtcars[, 1:2], method = gower))), и все равно выдает ошибку в DistMatrixWithoutUnitMAT(x, gower, test.na): несовместимо с запрошенным тип: [тип=символ; цель=двойная]   -  person LLL    schedule 24.06.2018


Ответы (4)


Ошибка в самой функции distance.

Я не знаю, преднамеренно это или нет, но текущая реализация philentropy::distance с методом "gower" не может обрабатывать любые смешанные типы данных, поскольку первая операция заключается в переносе data.frame, создавая символьную матрицу, которая затем выдает опечатка при передаче в функцию DistMatrixWithoutUnit.

Вместо этого вы можете попробовать использовать функцию daisy из cluster.

library(cluster)

x <- mtcars[,1:2]

x$cyl <- as.factor(x$cyl)

dist <- daisy(x, metric = "gower")

cls <- hclust(dist)

plot(cls)

РЕДАКТИРОВАТЬ: Для дальнейшего использования похоже, что philentropy будет обновлен, чтобы в следующей версии была улучшена обработка типов. Из виньетки

В будущих версиях philentropy я оптимизирую функцию Distance(), чтобы внутренние проверки правильности типа данных и правильных входных данных занимали меньше времени завершения, чем базовая функция dist().

person Esther    schedule 24.06.2018

ЛЛЛ; Извините, я не знаю английского и не могу объяснить. Теперь это попытка. Но код хороший ;-)

library(philentropy)
clusters <- hclust(
                   as.dist(
                          distance(mtcars[, 1:2], method = "gower")))
plot(clusters)

Хороший вид

person Juan Abasolo    schedule 28.06.2018

Вы можете сделать это довольно эффективно с пакетом gower

library(gower)

d <- sapply(1:nrow(mtcars), function(i) gower_dist(mtcars[i,],mtcars))
d <- as.dist(d)
h <- hclust(d)
plot(h)
person Community    schedule 06.08.2018

Большое спасибо за этот замечательный вопрос и спасибо всем вам, кто дал отличные ответы.

Просто чтобы решить проблему для будущих читателей:

# import example data
data("mtcars")
# store example subset with correct data type 
mtcars_subset <- tibble::tibble(mpg = as.numeric(as.vector(mtcars$mpg)), 
                                cyl = as.numeric(as.vector(mtcars$cyl)), 
                                disp = as.numeric(as.vector(mtcars$disp)))

# transpose data.frame to be conform with philentropy input format
mtcars_subset <- t(mtcars_subset)

# cluster
clusters <- hclust(as.dist(philentropy::distance(mtcars_subset, method = "gower")))
plot(clusters)

# When using the developer version on GitHub you can also specify 'use.row.names = TRUE'
clusters <- hclust(as.dist(philentropy::distance(mtcars_subset, method = "gower", 
use.row.names = TRUE)))
plot(clusters)

Как видите, теперь кластеризация работает отлично.

Проблема в том, что в примере набора данных столбец cyl хранит factor значений, а не double значений, как требуется для функции philentropy::distance(). Поскольку базовый код написан на Rcpp, несоответствующие типы данных вызовут проблемы. Как правильно заметила Эстер, в будущих версиях пакета я реализую лучший способ проверки безопасности типов.

head(tibble::as.tibble(mtcars))

# A tibble: 6 x 11
mpg cyl    disp    hp  drat    wt  qsec    vs    am  gear  carb
<dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1  21   6       160   110  3.9   2.62  16.5     0     1     4     4
2  21   6       160   110  3.9   2.88  17.0     0     1     4     4
3  22.8 4       108    93  3.85  2.32  18.6     1     1     4     1
4  21.4 6       258   110  3.08  3.22  19.4     1     0     3     1
5  18.7 8       360   175  3.15  3.44  17.0     0     0     3     2
6  18.1 6       225   105  2.76  3.46  20.2     1     0     3     1

Чтобы преодолеть это ограничение, я сохранил интересующие столбцы из набора данных mtcars в отдельном data.frame/tibble и преобразовал все столбцы в двойные значения с помощью as.numeric(as.vector(mtcars$mpg)).

Результирующее подмножество data.frame теперь хранит только требуемые значения double.

mtcars_subset

# A tibble: 32 x 3
 mpg   cyl  disp
<dbl> <dbl> <dbl>
1  21       6  160 
2  21       6  160 
3  22.8     4  108 
4  21.4     6  258 
5  18.7     8  360 
6  18.1     6  225 
7  14.3     8  360 
8  24.4     4  147.
9  22.8     4  141.
10  19.2     6  168.
# … with 22 more rows

Также обратите внимание, что если вы предоставите функции philentropy::distance() только 2 входных вектора, то будет возвращено только одно значение расстояния, и функция hclust() не сможет вычислить какие-либо кластеры с одним значением. Поэтому я добавил третий столбец disp, чтобы включить визуализацию кластеров.

Надеюсь, это поможет.

person HajkD    schedule 13.02.2019