Masz rację, że k-oznacza grupowania nie należy wykonywać z danymi różnych typów. Ponieważ k-średnie jest zasadniczo prostym algorytmem wyszukiwania do znalezienia partycji, która minimalizuje kwadratowe odległości euklidesowe w obrębie klastra między obserwacjami skupionymi i centroidem skupienia, należy go stosować tylko z danymi, w których kwadratowe odległości euklidesowe byłyby znaczące.
Kiedy twoje dane składają się ze zmiennych typów mieszanych, musisz użyć odległości Gowera. @Ttnphns użytkowników CV ma świetny przegląd odległości Gower jest tutaj . Zasadniczo obliczasz macierz odległości dla swoich rzędów dla każdej zmiennej kolejno, używając rodzaju odległości odpowiedniego dla tego typu zmiennej (np. Euklidesowy dla danych ciągłych itp.); końcowa odległość rzędu do jest (ewentualnie ważoną) średnią odległości dla każdej zmiennej. Należy pamiętać, że odległość Gowera nie jest w rzeczywistości metryką . Niemniej jednak, przy mieszanych danych, odległość Gowera jest w dużej mierze jedyną grą w mieście. ja i′
W tym momencie można użyć dowolnej metody klastrowania, która może działać na macierzy odległości, zamiast wymagać oryginalnej macierzy danych. (Zauważ, że k-średnie potrzebuje tego drugiego.) Najpopularniejsze wybory to podział na medoidy (PAM, który jest zasadniczo taki sam jak k-średnie, ale wykorzystuje najbardziej centralną obserwację niż środek ciężkości), różne hierarchiczne podejścia do grupowania (np. , mediana, pojedyncze połączenie i pełne połączenie; przy hierarchicznym klastrowaniu będziesz musiał zdecydować, gdzie „ wyciąć drzewo ”, aby uzyskać ostateczne przypisania klastrów) oraz DBSCAN, który pozwala na znacznie bardziej elastyczne kształty klastrów.
Oto prosta R
wersja demonstracyjna (nb, w rzeczywistości istnieją 3 klastry, ale dane w większości wyglądają na odpowiednie 2 klastry):
library(cluster) # we'll use these packages
library(fpc)
# here we're generating 45 data in 3 clusters:
set.seed(3296) # this makes the example exactly reproducible
n = 15
cont = c(rnorm(n, mean=0, sd=1),
rnorm(n, mean=1, sd=1),
rnorm(n, mean=2, sd=1) )
bin = c(rbinom(n, size=1, prob=.2),
rbinom(n, size=1, prob=.5),
rbinom(n, size=1, prob=.8) )
ord = c(rbinom(n, size=5, prob=.2),
rbinom(n, size=5, prob=.5),
rbinom(n, size=5, prob=.8) )
data = data.frame(cont=cont, bin=bin, ord=factor(ord, ordered=TRUE))
# this returns the distance matrix with Gower's distance:
g.dist = daisy(data, metric="gower", type=list(symm=2))
Możemy zacząć od przeszukiwania różnych liczb klastrów za pomocą PAM:
# we can start by searching over different numbers of clusters with PAM:
pc = pamk(g.dist, krange=1:5, criterion="asw")
pc[2:3]
# $nc
# [1] 2 # 2 clusters maximize the average silhouette width
#
# $crit
# [1] 0.0000000 0.6227580 0.5593053 0.5011497 0.4294626
pc = pc$pamobject; pc # this is the optimal PAM clustering
# Medoids:
# ID
# [1,] "29" "29"
# [2,] "33" "33"
# Clustering vector:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# 1 1 1 1 1 2 1 1 1 1 1 2 1 2 1 2 2 1 1 1 2 1 2 1 2 2
# 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
# 1 2 1 2 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2
# Objective function:
# build swap
# 0.1500934 0.1461762
#
# Available components:
# [1] "medoids" "id.med" "clustering" "objective" "isolation"
# [6] "clusinfo" "silinfo" "diss" "call"
Wyniki te można porównać z wynikami klastrowania hierarchicznego:
hc.m = hclust(g.dist, method="median")
hc.s = hclust(g.dist, method="single")
hc.c = hclust(g.dist, method="complete")
windows(height=3.5, width=9)
layout(matrix(1:3, nrow=1))
plot(hc.m)
plot(hc.s)
plot(hc.c)
Mediana metody sugeruje 2 (prawdopodobnie 3) skupienia, singiel obsługuje tylko 2, ale kompletna metoda może sugerować 2, 3 lub 4 moje oko.
Wreszcie możemy wypróbować DBSCAN. Wymaga to określenia dwóch parametrów: eps, „odległości osiągalności” (jak blisko muszą być ze sobą połączone dwie obserwacje) i minPts (minimalna liczba punktów, które należy połączyć ze sobą, zanim będziesz mógł nazwać je 'grupa'). Zasadniczą zasadą dla minPts jest użycie o jeden więcej niż liczby wymiarów (w naszym przypadku 3 + 1 = 4), ale posiadanie zbyt małej liczby nie jest zalecane. Domyślna wartość dbscan
to 5; trzymamy się tego. Jednym ze sposobów myślenia o odległości osiągalności jest sprawdzenie, jaki procent odległości jest mniejszy niż jakakolwiek podana wartość. Możemy to zrobić, badając rozkład odległości:
windows()
layout(matrix(1:2, nrow=1))
plot(density(na.omit(g.dist[upper.tri(g.dist)])), main="kernel density")
plot(ecdf(g.dist[upper.tri(g.dist)]), main="ECDF")
Odległości wydają się skupiać w dostrzegalne wizualnie grupy „bliżej” i „dalej”. Wydaje się, że wartość .3 najlepiej rozróżnia dwie grupy odległości. Aby zbadać wrażliwość wyjścia na różne wybory eps, możemy wypróbować również .2 i .4:
dbc3 = dbscan(g.dist, eps=.3, MinPts=5, method="dist"); dbc3
# dbscan Pts=45 MinPts=5 eps=0.3
# 1 2
# seed 22 23
# total 22 23
dbc2 = dbscan(g.dist, eps=.2, MinPts=5, method="dist"); dbc2
# dbscan Pts=45 MinPts=5 eps=0.2
# 1 2
# border 2 1
# seed 20 22
# total 22 23
dbc4 = dbscan(g.dist, eps=.4, MinPts=5, method="dist"); dbc4
# dbscan Pts=45 MinPts=5 eps=0.4
# 1
# seed 45
# total 45
Używanie eps=.3
daje bardzo czyste rozwiązanie, które (przynajmniej jakościowo) zgadza się z tym, co widzieliśmy z innych metod powyżej.
Ponieważ nie ma żadnego znaczącego skupienia 1 , powinniśmy uważać, aby dopasować obserwacje, które są nazywane „skupieniem 1” z różnych skupień. Zamiast tego możemy tworzyć tabele i jeśli większość obserwacji zwanych „skupieniem 1” w jednym dopasowaniu nosi nazwę „skupienia 2” w innym, przekonamy się, że wyniki są nadal zasadniczo podobne. W naszym przypadku różne skupienia są w większości bardzo stabilne i za każdym razem umieszczają te same obserwacje w tych samych skupieniach; różni się tylko kompletna hierarchiczna klastracja powiązań:
# comparing the clusterings
table(cutree(hc.m, k=2), cutree(hc.s, k=2))
# 1 2
# 1 22 0
# 2 0 23
table(cutree(hc.m, k=2), pc$clustering)
# 1 2
# 1 22 0
# 2 0 23
table(pc$clustering, dbc3$cluster)
# 1 2
# 1 22 0
# 2 0 23
table(cutree(hc.m, k=2), cutree(hc.c, k=2))
# 1 2
# 1 14 8
# 2 7 16
Oczywiście nie ma gwarancji, że jakakolwiek analiza klastra odzyska prawdziwe ukryte klastry w danych. Brak prawdziwych etykiet klastrowych (które byłyby dostępne np. W sytuacji regresji logistycznej) oznacza, że ogromna ilość informacji jest niedostępna. Nawet przy bardzo dużych zestawach danych klastry mogą nie być wystarczająco dobrze rozdzielone, aby można je było w pełni odzyskać. W naszym przypadku, ponieważ znamy prawdziwe członkostwo w klastrze, możemy porównać to z danymi wyjściowymi, aby zobaczyć, jak dobrze to zrobiło. Jak zauważyłem powyżej, w rzeczywistości istnieją 3 ukryte klastry, ale zamiast tego dane wyglądają jak 2 klastry:
pc$clustering[1:15] # these were actually cluster 1 in the data generating process
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# 1 1 1 1 1 2 1 1 1 1 1 2 1 2 1
pc$clustering[16:30] # these were actually cluster 2 in the data generating process
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# 2 2 1 1 1 2 1 2 1 2 2 1 2 1 2
pc$clustering[31:45] # these were actually cluster 3 in the data generating process
# 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
# 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2