Analisi dei cluster in R: determinare il numero ottimale di cluster


428

Essendo un principiante in R, non sono molto sicuro di come scegliere il miglior numero di cluster per fare un'analisi k-mean. Dopo aver tracciato un sottoinsieme di dati sottostanti, quanti cluster saranno appropriati? Come posso eseguire l'analisi del dendro del cluster?

n = 1000
kk = 10    
x1 = runif(kk)
y1 = runif(kk)
z1 = runif(kk)    
x4 = sample(x1,length(x1))
y4 = sample(y1,length(y1)) 
randObs <- function()
{
  ix = sample( 1:length(x4), 1 )
  iy = sample( 1:length(y4), 1 )
  rx = rnorm( 1, x4[ix], runif(1)/8 )
  ry = rnorm( 1, y4[ix], runif(1)/8 )
  return( c(rx,ry) )
}  
x = c()
y = c()
for ( k in 1:n )
{
  rPair  =  randObs()
  x  =  c( x, rPair[1] )
  y  =  c( y, rPair[2] )
}
z <- rnorm(n)
d <- data.frame( x, y, z )

4
Se non sei completamente legato ai kmean, puoi provare l'algoritmo di clustering DBSCAN, disponibile nel fpcpacchetto. È vero, devi quindi impostare due parametri ... ma ho scoperto che fpc::dbscanpoi fa un ottimo lavoro nel determinare automaticamente un buon numero di cluster. Inoltre può effettivamente generare un singolo cluster se questo è ciò che i dati ti dicono - alcuni dei metodi nelle eccellenti risposte di @ Ben non ti aiuteranno a determinare se k = 1 è effettivamente il migliore.
Stephan Kolassa,

Risposte:


1020

Se la tua domanda è how can I determine how many clusters are appropriate for a kmeans analysis of my data?, ecco alcune opzioni. L' articolo di Wikipedia sul determinare il numero di cluster presenta una buona recensione di alcuni di questi metodi.

Innanzitutto, alcuni dati riproducibili (i dati nella Q sono ... per me poco chiari):

n = 100
g = 6 
set.seed(g)
d <- data.frame(x = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))), 
                y = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))))
plot(d)

inserisci qui la descrizione dell'immagine

Uno . Cerca una curva o un gomito nella somma della trama del ghiaione con errore al quadrato (SSE). Vedi http://www.statmethods.net/advstats/cluster.html e http://www.mattpeeples.net/kmeans.html per ulteriori informazioni. La posizione del gomito nella trama risultante suggerisce un numero adeguato di cluster per i kmean:

mydata <- d
wss <- (nrow(mydata)-1)*sum(apply(mydata,2,var))
  for (i in 2:15) wss[i] <- sum(kmeans(mydata,
                                       centers=i)$withinss)
plot(1:15, wss, type="b", xlab="Number of Clusters",
     ylab="Within groups sum of squares")

Potremmo concludere che 4 cluster sarebbero indicati con questo metodo: inserisci qui la descrizione dell'immagine

Due . Puoi fare il partizionamento attorno ai medoidi per stimare il numero di cluster usando la pamkfunzione nel pacchetto fpc.

library(fpc)
pamk.best <- pamk(d)
cat("number of clusters estimated by optimum average silhouette width:", pamk.best$nc, "\n")
plot(pam(d, pamk.best$nc))

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

# we could also do:
library(fpc)
asw <- numeric(20)
for (k in 2:20)
  asw[[k]] <- pam(d, k) $ silinfo $ avg.width
k.best <- which.max(asw)
cat("silhouette-optimal number of clusters:", k.best, "\n")
# still 4

Tre . Criterio di Calinsky: un altro approccio per diagnosticare quanti cluster soddisfano i dati. In questo caso proviamo da 1 a 10 gruppi.

require(vegan)
fit <- cascadeKM(scale(d, center = TRUE,  scale = TRUE), 1, 10, iter = 1000)
plot(fit, sortg = TRUE, grpmts.plot = TRUE)
calinski.best <- as.numeric(which.max(fit$results[2,]))
cat("Calinski criterion optimal number of clusters:", calinski.best, "\n")
# 5 clusters!

inserisci qui la descrizione dell'immagine

Quattro . Determinare il modello e il numero ottimali di cluster in base al criterio informativo bayesiano per massimizzare le aspettative, inizializzato dal clustering gerarchico per modelli di miscele gaussiane parametrizzate

# See http://www.jstatsoft.org/v18/i06/paper
# http://www.stat.washington.edu/research/reports/2006/tr504.pdf
#
library(mclust)
# Run the function to see how many clusters
# it finds to be optimal, set it to search for
# at least 1 model and up 20.
d_clust <- Mclust(as.matrix(d), G=1:20)
m.best <- dim(d_clust$z)[2]
cat("model-based optimal number of clusters:", m.best, "\n")
# 4 clusters
plot(d_clust)

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Cinque . Cluster di propagazione dell'affinità (AP), consultare http://dx.doi.org/10.1126/science.1136800

library(apcluster)
d.apclus <- apcluster(negDistMat(r=2), d)
cat("affinity propogation optimal number of clusters:", length(d.apclus@clusters), "\n")
# 4
heatmap(d.apclus)
plot(d.apclus, d)

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Sei . Statistica del gap per la stima del numero di cluster. Vedi anche un po 'di codice per un piacevole output grafico . Prova qui 2-10 cluster:

library(cluster)
clusGap(d, kmeans, 10, B = 100, verbose = interactive())

Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
Clustering Gap statistic ["clusGap"].
B=100 simulated reference sets, k = 1..10
 --> Number of clusters (method 'firstSEmax', SE.factor=1): 4
          logW   E.logW        gap     SE.sim
 [1,] 5.991701 5.970454 -0.0212471 0.04388506
 [2,] 5.152666 5.367256  0.2145907 0.04057451
 [3,] 4.557779 5.069601  0.5118225 0.03215540
 [4,] 3.928959 4.880453  0.9514943 0.04630399
 [5,] 3.789319 4.766903  0.9775842 0.04826191
 [6,] 3.747539 4.670100  0.9225607 0.03898850
 [7,] 3.582373 4.590136  1.0077628 0.04892236
 [8,] 3.528791 4.509247  0.9804556 0.04701930
 [9,] 3.442481 4.433200  0.9907197 0.04935647
[10,] 3.445291 4.369232  0.9239414 0.05055486

Ecco il risultato dell'implementazione della statistica gap di Edwin Chen: inserisci qui la descrizione dell'immagine

Sette . Potresti anche trovare utile esplorare i tuoi dati con clustergrammi per visualizzare l'assegnazione dei cluster, vedi http://www.r-statistics.com/2010/06/clustergram-visualization-and-diagnostics-for-cluster-analysis-r- codice / per maggiori dettagli.

Otto . Il pacchetto NbClust fornisce 30 indici per determinare il numero di cluster in un set di dati.

library(NbClust)
nb <- NbClust(d, diss=NULL, distance = "euclidean",
        method = "kmeans", min.nc=2, max.nc=15, 
        index = "alllong", alphaBeale = 0.1)
hist(nb$Best.nc[1,], breaks = max(na.omit(nb$Best.nc[1,])))
# Looks like 3 is the most frequently determined number of clusters
# and curiously, four clusters is not in the output at all!

inserisci qui la descrizione dell'immagine

Se la tua domanda è how can I produce a dendrogram to visualize the results of my cluster analysis, allora dovresti iniziare con questi: http://www.statmethods.net/advstats/cluster.html http://www.r-tutor.com/gpu-computing/clustering/hierarchical-cluster-analysis http://gastonsanchez.wordpress.com/2012/10/03/7-ways-to-plot-dendrograms-in-r/ E vedi qui per metodi più esotici: http://cran.r-project.org/ web / views / Cluster.html

Ecco alcuni esempi:

d_dist <- dist(as.matrix(d))   # find distance matrix 
plot(hclust(d_dist))           # apply hirarchical clustering and plot

inserisci qui la descrizione dell'immagine

# a Bayesian clustering method, good for high-dimension data, more details:
# http://vahid.probstat.ca/paper/2012-bclust.pdf
install.packages("bclust")
library(bclust)
x <- as.matrix(d)
d.bclus <- bclust(x, transformed.par = c(0, -50, log(16), 0, 0, 0))
viplot(imp(d.bclus)$var); plot(d.bclus); ditplot(d.bclus)
dptplot(d.bclus, scale = 20, horizbar.plot = TRUE,varimp = imp(d.bclus)$var, horizbar.distance = 0, dendrogram.lwd = 2)
# I just include the dendrogram here

inserisci qui la descrizione dell'immagine

Anche per i dati ad alta dimensione è la pvclustlibreria che calcola i valori di p per il clustering gerarchico tramite ricampionamento bootstrap multiscala. Ecco l'esempio della documentazione (non funzionerà su dati di dimensioni così basse come nel mio esempio):

library(pvclust)
library(MASS)
data(Boston)
boston.pv <- pvclust(Boston)
plot(boston.pv)

inserisci qui la descrizione dell'immagine

Qualcuno di questo aiuta?


Per l'ultimo dendogramma (Clend Dendogram con AU / BP) a volte è conveniente disegnare rettangoli attorno ai gruppi con valori p relativamente alti: pvrect (fit, alpha = 0.95)
Igor Elbert

Questo e 'esattamente quello che stavo cercando. Sono nuovo di R e mi ci sarebbe voluto molto tempo per trovarlo. Grazie @Ben per aver risposto in modo così dettagliato. Potete per favore guidarmi su dove posso trovare la logica dietro ciascuno di questi metodi, come quale metrica o criterio stanno usando per determinare il numero ottimale di cluster o in che modo ognuno di essi è diverso l'uno dall'altro. Il mio capo vuole che lo dica, così possiamo decidere quale dei metodi usare. Grazie in anticipo.
nasia jaffri,

1
@Aleksandr Blekh Potresti anche provare a trasformare qualsiasi metodo grafico in analitico. Ad esempio, io uso il metodo "gomito" (menzionato per la prima volta nella risposta), ma provo a trovarlo analiticamente. Il punto del gomito potrebbe essere il punto con la massima curvatura. Per dati discreti, è il punto con la differenza centrale massima del secondo ordine (da analogico a derivata massima del secondo ordine per dati continui). Vedi stackoverflow.com/a/4473065/1075993 e stackoverflow.com/q/2018178/1075993 . Immagino che anche altri metodi grafici possano essere convertiti in analitici.
Andrey Sapegin,

1
@AndreySapegin: Potrei, ma: 1) francamente, non la considero una soluzione elegante (IMHO, nella maggior parte dei casi, i metodi visivi dovrebbero rimanere visivi, mentre quelli analitici dovrebbero rimanere analitici); 2) Ho trovato una soluzione analitica a questo, usando uno o più Rpacchetti (è sul mio GitHub - sei il benvenuto a dare un'occhiata); 3) la mia soluzione sembra funzionare abbastanza bene, inoltre, è passato un po 'di tempo e ho già finalizzato il mio software di tesi, il rapporto di tesi (tesi) e attualmente mi sto preparando per la difesa :-). Indipendentemente da ciò, apprezzo molto il tuo commento e link. Ti auguro il meglio!
Aleksandr Blekh,

1
2,2 milioni di righe sono nel mio set di dati di clustering corrente. Nessuno di questi pacchetti R funziona su questo, mi aspetto. Hanno appena pop il mio computer e poi cade dalla mia esperienza. Tuttavia, sembra che l'autore conosca le sue cose per piccoli dati e per il caso generale, indipendentemente dalla capacità del software. Nessun punto dedotto a causa dell'ovvio buon lavoro dell'autore. Per favore, sappiate solo che la vecchia R è orribile a 2,2 milioni di file - provate voi stessi se non vi fidate di me. H2O aiuta ma è limitato a un piccolo giardino recintato di felice.
Geoffrey Anderson,

21

È difficile aggiungere anche una risposta così elaborata. Anche se credo che dovremmo menzionarlo identifyqui, in particolare perché @Ben mostra molti esempi di dendrogrammi.

d_dist <- dist(as.matrix(d))   # find distance matrix 
plot(hclust(d_dist)) 
clusters <- identify(hclust(d_dist))

identifyti consente di scegliere in modo interattivo i cluster da un dendrogramma e memorizza le tue scelte in un elenco. Premi Esc per uscire dalla modalità interattiva e tornare alla R console. Si noti che l'elenco contiene gli indici, non i rownames (al contrario di cutree).


10

Al fine di determinare k-cluster ottimale nei metodi di clustering. Di solito utilizzo il Elbowmetodo accompagnato dall'elaborazione parallela per evitare il consumo di tempo. Questo codice può campionare in questo modo:

Metodo del gomito

elbow.k <- function(mydata){
dist.obj <- dist(mydata)
hclust.obj <- hclust(dist.obj)
css.obj <- css.hclust(dist.obj,hclust.obj)
elbow.obj <- elbow.batch(css.obj)
k <- elbow.obj$k
return(k)
}

Esecuzione di gomito parallela

no_cores <- detectCores()
    cl<-makeCluster(no_cores)
    clusterEvalQ(cl, library(GMD))
    clusterExport(cl, list("data.clustering", "data.convert", "elbow.k", "clustering.kmeans"))
 start.time <- Sys.time()
 elbow.k.handle(data.clustering))
 k.clusters <- parSapply(cl, 1, function(x) elbow.k(data.clustering))
    end.time <- Sys.time()
    cat('Time to find k using Elbow method is',(end.time - start.time),'seconds with k value:', k.clusters)

Funziona bene.


2
Le funzioni gomito e css provengono dal pacchetto GMD: cran.r-project.org/web/packages/GMD/GMD.pdf
Rohan,

6

Splendida risposta di Ben. Tuttavia sono sorpreso che il metodo Affinity Propagation (AP) sia stato qui suggerito solo per trovare il numero di cluster per il metodo k-mean, dove in generale AP fa un lavoro migliore raggruppando i dati. Si prega di consultare il documento scientifico che supporta questo metodo in Science qui:

Frey, Brendan J. e Delbert Dueck. "Clustering passando messaggi tra punti dati." scienza 315.5814 (2007): 972-976.

Quindi, se non sei di parte riguardo a k-medie, ti suggerisco di usare direttamente AP, che raggrupperà i dati senza richiedere il numero di cluster:

library(apcluster)
apclus = apcluster(negDistMat(r=2), data)
show(apclus)

Se le distanze euclidee negative non sono appropriate, è possibile utilizzare altre misure di somiglianza fornite nello stesso pacchetto. Ad esempio, per le somiglianze basate sulle correlazioni di Spearman, questo è ciò di cui hai bisogno:

sim = corSimMat(data, method="spearman")
apclus = apcluster(s=sim)

Si noti che queste funzioni per le somiglianze nel pacchetto AP sono fornite solo per semplicità. In effetti, la funzione apcluster () in R accetterà qualsiasi matrice di correlazioni. Lo stesso prima con corSimMat () può essere fatto con questo:

sim = cor(data, method="spearman")

o

sim = cor(t(data), method="spearman")

a seconda di cosa vuoi raggruppare sulla tua matrice (righe o colonne).


6

Questi metodi sono fantastici, ma quando si cerca di trovare k per set di dati molto più grandi, questi possono essere pazzi lentamente in R.

Una buona soluzione che ho trovato è il pacchetto "RWeka", che ha un'implementazione efficiente dell'algoritmo X-Means, una versione estesa di K-Means che si ridimensiona meglio e determinerà il numero ottimale di cluster per te.

Innanzitutto, assicurati che Weka sia installato sul tuo sistema e che XMeans sia installato tramite lo strumento di gestione dei pacchetti di Weka.

library(RWeka)

# Print a list of available options for the X-Means algorithm
WOW("XMeans")

# Create a Weka_control object which will specify our parameters
weka_ctrl <- Weka_control(
    I = 1000,                          # max no. of overall iterations
    M = 1000,                          # max no. of iterations in the kMeans loop
    L = 20,                            # min no. of clusters
    H = 150,                           # max no. of clusters
    D = "weka.core.EuclideanDistance", # distance metric Euclidean
    C = 0.4,                           # cutoff factor ???
    S = 12                             # random number seed (for reproducibility)
)

# Run the algorithm on your data, d
x_means <- XMeans(d, control = weka_ctrl)

# Assign cluster IDs to original data set
d$xmeans.cluster <- x_means$class_ids

6

Una soluzione semplice è la libreria factoextra. È possibile modificare il metodo di clustering e il metodo per calcolare il numero migliore di gruppi. Ad esempio, se vuoi conoscere il miglior numero di cluster per un k- significa:

Dati: mtcars

library(factoextra)   
fviz_nbclust(mtcars, kmeans, method = "wss") +
      geom_vline(xintercept = 3, linetype = 2)+
      labs(subtitle = "Elbow method")

Infine, otteniamo un grafico come:

inserisci qui la descrizione dell'immagine


2

Le risposte sono fantastiche Se si desidera dare la possibilità a un altro metodo di clustering, è possibile utilizzare il clustering gerarchico e vedere come i dati vengono suddivisi.

> set.seed(2)
> x=matrix(rnorm(50*2), ncol=2)
> hc.complete = hclust(dist(x), method="complete")
> plot(hc.complete)

inserisci qui la descrizione dell'immagine

A seconda di quante classi hai bisogno puoi tagliare il tuo dendrogramma;

> cutree(hc.complete,k = 2)
 [1] 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 2 1 1 1
[26] 2 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 2

Se digiti ?cutreevedrai le definizioni. Se il tuo set di dati ha tre classi sarà semplicemente cutree(hc.complete, k = 3). L'equivalente per cutree(hc.complete,k = 2)è cutree(hc.complete,h = 4.9).


Preferisco Wards al completo.
Chris,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.