Sie haben Recht, dass k-means Clustering nicht mit Daten gemischter Typen durchgeführt werden sollte. Da k-means im Wesentlichen ein einfacher Suchalgorithmus ist, um eine Partition zu finden, die die euklidischen Abstände innerhalb des Clusters zum Clusterschwerpunkt minimiert, sollte er nur für Daten verwendet werden, bei denen euklidische Abstände im Quadrat von Bedeutung sind.
ichich′
An dieser Stelle können Sie eine beliebige Clustermethode verwenden, die über eine Distanzmatrix ausgeführt werden kann, anstatt die ursprüngliche Datenmatrix zu benötigen. (Beachten Sie, dass k-means das Letztere benötigt.) Die beliebtesten Optionen sind die Aufteilung um Medoide (PAM, was im Wesentlichen das Gleiche wie k-means ist, aber die zentralste Beobachtung anstelle des Schwerpunkts verwendet), verschiedene hierarchische Clustering-Ansätze (z. B. , Median, Single-Linkage und Complete-Linkage: Beim hierarchischen Clustering müssen Sie entscheiden, wo Sie den Baum abschneiden möchten , um die endgültigen Clusterzuweisungen zu erhalten.) DBSCAN ermöglicht flexiblere Clusterformen.
Hier ist eine einfache R
Demo (nb, es gibt tatsächlich 3 Cluster, aber die Daten sehen meistens so aus, als wären 2 Cluster angemessen):
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))
Wir können beginnen, indem wir mit PAM eine unterschiedliche Anzahl von Clustern durchsuchen:
# 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"
Diese Ergebnisse können mit den Ergebnissen der hierarchischen Gruppierung verglichen werden:
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)
Die Medianmethode schlägt 2 (möglicherweise 3) Cluster vor, die einzelne unterstützt nur 2, aber die vollständige Methode könnte meinem Auge 2, 3 oder 4 vorschlagen.
Schließlich können wir DBSCAN versuchen. Dazu müssen zwei Parameter angegeben werden: eps, die Erreichbarkeitsentfernung (wie eng zwei Beobachtungen sein müssen, um miteinander verbunden zu werden) und minPts (die Mindestanzahl von Punkten, die miteinander verbunden werden müssen, bevor Sie sie als a bezeichnen möchten) 'cluster'). Eine Faustregel für minPts ist, eine Nummer mehr als die Anzahl der Dimensionen zu verwenden (in unserem Fall 3 + 1 = 4), aber eine zu kleine Nummer zu haben, wird nicht empfohlen. Der Standardwert für dbscan
ist 5; wir werden dabei bleiben. Eine Möglichkeit, über die Erreichbarkeitsentfernung nachzudenken, besteht darin, festzustellen, wie viel Prozent der Entfernungen unter einem bestimmten Wert liegen. Wir können das tun, indem wir die Verteilung der Entfernungen untersuchen:
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")
Die Entfernungen selbst scheinen sich in visuell wahrnehmbare Gruppen von "näher" und "weiter entfernt" zu gruppieren. Ein Wert von 0,3 scheint die beiden Entfernungsgruppen am saubersten zu unterscheiden. Um die Empfindlichkeit der Ausgabe für verschiedene EPS-Optionen zu untersuchen, können Sie auch .2 und .4 ausprobieren:
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
Die Verwendung eps=.3
ergibt eine sehr saubere Lösung, die (zumindest qualitativ) mit dem übereinstimmt, was wir aus den obigen Methoden gesehen haben.
Da es keine aussagekräftige Cluster-1-Einheit gibt , sollten wir vorsichtig sein, um herauszufinden, welche Beobachtungen aus verschiedenen Cluster-1-Gruppen als "Cluster 1" bezeichnet werden. Stattdessen können wir Tabellen bilden, und wenn die meisten Beobachtungen, die in einer Anpassung als "Cluster 1" bezeichnet werden, in einer anderen als "Cluster 2" bezeichnet werden, sehen wir, dass die Ergebnisse im Wesentlichen immer noch ähnlich sind. In unserem Fall sind die verschiedenen Cluster meist sehr stabil und bringen jedes Mal die gleichen Beobachtungen in die gleichen Cluster. Nur die vollständige hierarchische Verknüpfung der Cluster unterscheidet sich:
# 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
Natürlich gibt es keine Garantie dafür, dass bei einer Clusteranalyse die tatsächlich latenten Cluster in Ihren Daten wiederhergestellt werden. Das Fehlen der richtigen Cluster-Labels (die beispielsweise in einer logistischen Regressionssituation verfügbar wären) bedeutet, dass eine enorme Menge an Informationen nicht verfügbar ist. Selbst bei sehr großen Datenmengen sind die Cluster möglicherweise nicht gut genug voneinander getrennt, um perfekt wiederhergestellt werden zu können. In unserem Fall können wir, da wir die wahre Cluster-Mitgliedschaft kennen, diese mit der Ausgabe vergleichen, um zu sehen, wie gut sie funktioniert hat. Wie ich oben erwähnt habe, gibt es tatsächlich 3 latente Cluster, aber die Daten geben stattdessen das Aussehen von 2 Clustern an:
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