йерархично групиране с Gower distance - hclust() и philentropy::distance()

Имам смесен набор от данни (категорични и непрекъснати променливи) и бих искал да направя йерархично групиране с помощта на разстоянието на Gower.

Базирам кода си на пример от https://www.r-bloggers.com/hierarchical-clustering-in-r-2/, който използва база R dist() за евклидово разстояние. Тъй като dist() не изчислява разстоянието на Gower, опитах да използвам 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