Introdução

Análise de Cluster é uma técnica multivariada cuja finalidade é agregar objetos com base nas características que eles possuem. O resultado são grupos que exibem máxima homogeneidade de objetos dentro de grupos e, ao mesmo tempo, máxima heterogeneidade entre os grupos. A maioria dos métodos de análise de cluster requer uma medida de similaridade entre os elementos a serem agrupados, normalmente expressa como uma função distância ou métrica.

Análise Exploratória

knitr::kable(apply(wine[,-1], 2, summary))
V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14
Min. 11.03000 0.740000 1.360000 10.60000 70.00000 0.980000 0.34000 0.1300000 0.410000 1.28000 0.4800000 1.270000 278.0000
1st Qu. 12.36250 1.602500 2.210000 17.20000 88.00000 1.742500 1.20500 0.2700000 1.250000 3.22000 0.7825000 1.937500 500.5000
Median 13.05000 1.865000 2.360000 19.50000 98.00000 2.355000 2.13500 0.3400000 1.555000 4.69000 0.9650000 2.780000 673.5000
Mean 13.00062 2.336348 2.366517 19.49494 99.74157 2.295112 2.02927 0.3618539 1.590899 5.05809 0.9574494 2.611685 746.8933
3rd Qu. 13.67750 3.082500 2.557500 21.50000 107.00000 2.800000 2.87500 0.4375000 1.950000 6.20000 1.1200000 3.170000 985.0000
Max. 14.83000 5.800000 3.230000 30.00000 162.00000 3.880000 5.08000 0.6600000 3.580000 13.00000 1.7100000 4.000000 1680.0000

BoxPlot Comparativo

Dados Originais

#BoxPlot
plot_ly(y = wine$V2, name = "var 2", type = "box") %>%
  add_trace(y = wine$V3, name = "var 3")%>%
  add_trace(y = wine$V4, name = "var 4")%>%
  add_trace(y = wine$V5, name = "var 5")%>%
  add_trace(y = wine$V6, name = "var 6")%>%
  add_trace(y = wine$V7, name = "var 7")%>%
  add_trace(y = wine$V8, name = "var 8")%>%
  add_trace(y = wine$V9, name = "var 9")%>%
  add_trace(y = wine$V10, name = "var 10")%>%
  add_trace(y = wine$V11, name = "var 11")%>%
  add_trace(y = wine$V12, name = "var 12")%>%
  add_trace(y = wine$V13, name = "var 13")%>%
  add_trace(y = wine$V14, name = "var 14")

Dados Padronizados

WP <- scale(wine[,-1]) %>% data.frame()
plot_ly(y = WP$V2, name = "var 2", type = "box") %>%
  add_trace(y = WP$V3, name = "var 3")%>%
  add_trace(y = WP$V4, name = "var 4")%>%
  add_trace(y = WP$V5, name = "var 5")%>%
  add_trace(y = WP$V6, name = "var 6")%>%
  add_trace(y = WP$V7, name = "var 7")%>%
  add_trace(y = WP$V8, name = "var 8")%>%
  add_trace(y = WP$V9, name = "var 9")%>%
  add_trace(y = WP$V10, name = "var 10")%>%
  add_trace(y = WP$V11, name = "var 11")%>%
  add_trace(y = WP$V12, name = "var 12")%>%
  add_trace(y = WP$V13, name = "var 13")%>%
  add_trace(y = WP$V14, name = "var 14")

Matriz de Distâncias

Distancia Euclidiana - Dados originais

A distância euclidiana entre dois elementos \(X = (X_1, X_2,..., X_p)\) e \(Y=(Y_1, Y_2,..., X_p)\) é definida por:

\[ d_{XY} = \sqrt{(x_1 - y_1)^2 + (x_2-y_2)^2 + ... +(x_p-y_p)^2)}\]

d = dist(wine, method = "euclidean") # matriz de distancias
m = as.matrix(d)
fviz_dist(d, 
   gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Distancia Euclidiana - Dados padronizados

A distância euclidiana entre dois elementos \(X = (X_1, X_2,..., X_p)\) e \(Y=(Y_1, Y_2,..., X_p)\) é definida por:

\[ d_{XY} = \sqrt{(x_1 - y_1)^2 + (x_2-y_2)^2 + ... +(x_p-y_p)^2}\]

D = dist(WP) # matriz de distancias
m = as.matrix(D)
fviz_dist(D, 
   gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Distancia Euclidiana ao Quadrado - Dados padronizados

A distância euclidiana ao quadrado entre dois elementos \(X = (X_1, X_2,..., X_p)\) e \(Y=(Y_1, Y_2,..., X_p)\) é definida por:

\[ d_{XY} = (x_1 - y_1)^2 + (x_2-y_2)^2 + ... +(x_p-y_p)^2\]

D = dist(WP)^2 # matriz de distancias
m = as.matrix(D)
fviz_dist(D, 
   gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Distancia de Manhattan - Dados padronizados

A distância de manhattan entre dois elementos \(X = (X_1, X_2,..., X_p)\) e \(Y=(Y_1, Y_2,..., X_p)\) é definida por:

\[ d_{XY} = |x_1 - y_1| + |x_2-y_2| + ... +|x_p-y_p|\]

d_manhattan = dist(WP, method = "manhattan")
fviz_dist(d_manhattan, 
   gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Distancia Minkowski - Dados padronizados

A distância de é a generalização da distância euclidiana entre dois elementos \(X = (X_1, X_2,..., X_p)\) e \(Y=(Y_1, Y_2,..., X_p)\) e é definida por:

\[ d_{XY} = (|x_1 - y_1|^r + |x_2-y_2|^r + ... +|x_p-y_p|^r)^\frac{1}{r}\]

d_minkwski = dist(WP, method = "minkowski")
fviz_dist(d_minkwski, 
          gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Índice de Validação: Silhueta

Definida para cada objeto em análise \[s(i) = \frac{b(i)-a(i)}{max(a(i),b(i))}\]

onde

\(a(i)\) é distância média do i-ésimo objeto em relação a todos os objetos do mesmo grupo ;

\(b(i)\) é a distância média do i-ésimo objeto em relação a todos os objetos do grupo vizinho mais próximo a ele.

A silhueta média (\(SM\)) é a medida de qualidade efetivamente utilizada para avaliar a estrutura de agrupamento.

knitr::kable(cbind("SM" = c("0.71 - 1.00","0.51 - 0.70","0.26 - 0.50","< 0.25"), 
            "Interpretação" = c("Grupos produzidos pelo algoritmo possuem uma estrutura muito robusta
","Grupos possuem uma estrutura razoável
","Os grupos encontrados possuem uma estrutura fraca e pode ser artificial
","Nenhuma estrutura foi descoberta
")))
SM Interpretação
0.71 - 1.00 Grupos produzidos pelo algoritmo possuem uma estrutura muito robusta
0.51 - 0.70 Grupos possuem uma estrutura razoável
0.26 - 0.50 Os grupos encontrados possuem uma estrutura fraca e pode ser artificial
< 0.25 Nenhuma estrutura foi descoberta

Algoritmos de Agrupamento

Por critério de comparação, consideramos a distância euclidiana ao quadrado em ambos os algoritmos apresentados.

Método Hierárquico Aglomerativo

Os métodos hierárquicos da análise de cluster tem como principal característica um algoritmo capaz de fornecer mais de um tipo de partição dos dados. Ele gera vários agrupamentos possíveis, onde um cluster pode ser mesclado a outro em determinado passo do algoritmo.

Esses métodos não exigem que já se tenha um número inicial de clusters e são considerados inflexíveis uma vez que não se pode trocar um elemento de grupo. Eles podem ser classificados em dois tipos: Aglomerativos e Divisivos.

Métodos Aglomerativos: nesse caso, todos os elementos começam separados e vão sendo agrupados em etapas, um a um, até que tenhamos um único cluster com todos os elementos. O número ideal de clusters é escolhido dentre todas as opções.

Métodos Divisivos: no método divisivo todos os elementos começam juntos em um único cluster, e vão sendo separados um a um, até que cada elemento seja seu próprio cluster. Assim como no método aglomerativo, escolhemos o número ótimo de clusters dentre todas as possíveis combinações.

As variáveis podem ser quantitativas, qualitativas ou uma distribuição de ambas. O critério de agregação é a diminuição da homogeneidade para o cluster que está sendo mesclado. A homogeneidade de um cluster é a soma da razão de correlação (para variáveis qualitativas) e da correlação quadrática (para variáveis quantitativas) entre as variáveis e o centro do cluster (centroide) que é o primeiro componente principal de PCA mix.

PCA mix é definido para uma distribuição de variáveis qualitativas e quantitativas e inclui análises de componentes principais comuns (PCA) e análise de correspondência múltipla (MCA) como casos especiais. Os valores em falta são substituídos por médias para variáveis quantitativas e por zeros na matriz de indicadores para a variável qualitativa.

Vamos separar o dataset em duas partes, qualitativa como “a” e quantitativa como “x” Vamos também ignorar o ID do cliente para efietos de clusterização, poiis queremos nesse momento entender o dataset e como ele se comporta. Avaliando para \(k = 2,3,...,M\), onde M é a raiz quadrada do número de objetos na base de dados. Este limite superior é utilizado na literatura para delimitar a análise.

k <- 2:floor(sqrt(nrow(wine)))
D <- dist(WP)^2
CL_H = hclust(D, method = "ward.D")
Dendograma

Usa-se o dendograma para visualizar o processo de clusterização passo a passo, assim como analisar os níveis de distância dos clusters formados. Um bom ponto de decisão da clusterização final é onde os valores de distância mudam consideravelmente. Para a decisão do agrupamento final também devem ser avaliados se os clusters formados fazem sentido para o problema.

Avaliando o dendograma, há uma estrutura indicando 3 grupos neste conjunto de dados.

fviz_dend(CL_H, k = 3,# Cut in four groups
          cex = 0.5, # label size
          k_colors = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
          color_labels_by_k = TRUE, # color labels by groups
          rect = TRUE # Add rectangle around groups
)

Silhueta

Avaliando o resultado da silhueta média, também indicou 3 grupos como o melhor resultado.

# Silhueta
best_H <- sapply(k, function(k) mean(silhouette(cutree(CL_H, k = k), D)[,3]))
ggplot() + geom_line(aes(k, best_H), color = grey(0.5), size = 1.5) + geom_point(aes(k, best_H), color = grey(0.5), size = 2) + theme_minimal() + 
  geom_label_repel(aes(k, best_H, label = paste('k =',k), 
                      color = k==k[which.max(best_H)]), 
                  force = 0, nudge_y = -0.05,
                  size = 4,
                  arrow = arrow(length = unit(0.03, "npc"), type = "closed", ends = "first")) + 
  scale_y_continuous(limits = c(0,max(best_H)+0.05)) + labs(x = NULL, y = "Silhueta Média") + guides(color = FALSE) +
  theme(panel.grid.major.x = element_blank(),
        panel.grid.minor.x = element_blank(), 
        axis.title.y = element_text(size = 12)) +
  scale_x_discrete(NULL)

A silhueta para cada objeto apresentou alguns valores abaixo de zero, esta situação pode ser contornada realizando um ajuste de realocação. Função função disponível nos APÊNDICE I.

Silhueta
clust_H <- cutree(CL_H, k = 3)
sil <- silhouette(clust_H, D)
bar_sil(sil)

Silhueta Ajustada
ajust_clust <- ajuste_sil(sil, D)
bar_sil(ajust_clust)

Análise dos grupos

Visualmente considerando as duas dimensões com maior variância explicada, tempos:

Silhueta
dim_cluster(WP, clust_H, "Set1")

Silhueta Ajustada
clust_H_ajuste <- ajust_clust[,1]
dim_cluster(WP, clust_H_ajuste, "Set1")

Método Não-Hierárquico - K-means

Os métodos não-hierárquicos da análise de cluster são caracterizados pela necessidade de definir uma partição inicial e pela flexibilidade, uma vez que os elementos podem ser trocados de grupo durante a execução do algoritmo.

O procedimento geral adotado para os métodos não hierárquicos é: - Escolher uma partição inicial (baseada em conhecimentos anteriores do problema); - Realizar o deslocamento do objeto de seu grupo para outros grupos; - Verificar o valor do critério utilizado, decidindo pela clusterização que apresentar melhoria. Esse processo é repetido até que não se obtenha mais nenhuma melhoria com os deslocamentos.

Os métodos das k-médias e o Fuzzy c-Médias são alguns exemplos conhecidos desses métodos, que tem como vantagem a possibilidade de mover um elemento de um cluster para o outro, o que não é possível no método hierárquico.

Usualmente, os métodos não hierárquicos são mais eficientes na análise de bancos de dados com maior número de observações.

Silhueta

Avaliando o resultado da silhueta média, também indicou 3 grupos como o melhor resultado.

# Silhueta
best_K <- sapply(k, function(k) mean(silhouette(kmeans(WP, k, nstart = 30, iter.max = 30)$cluster, D)[,3]))
ggplot() + geom_line(aes(k, best_K), color = grey(0.5), size = 1.5) + geom_point(aes(k, best_K), color = grey(0.5), size = 2) + theme_minimal() + 
  geom_label_repel(aes(k, best_K, label = paste('k =',k), 
                      color = k==k[which.max(best_K)]), 
                  force = 0, nudge_y = -0.05,
                  size = 4,
                  arrow = arrow(length = unit(0.03, "npc"), type = "closed", ends = "first")) + 
  scale_y_continuous(limits = c(0,max(best_K)+0.05)) + labs(x = NULL, y = "Silhueta Média") + guides(color = FALSE) +
  theme(panel.grid.major.x = element_blank(),
        panel.grid.minor.x = element_blank(), 
        axis.title.y = element_text(size = 12)) +
  scale_x_discrete(NULL)

Silhueta para cada objeto

Silhueta
clust_K <- kmeans(WP, 3, nstart = 30, iter.max = 30)$cluster
sil <- silhouette(clust_K, D)
bar_sil(sil)

Silhueta Ajustada
ajust_clust <- ajuste_sil(sil, D)
bar_sil(ajust_clust)

Análise dos grupos

Visualmente considerando as duas dimensões com maior variância explicada, tempos:

Silhueta
dim_cluster(WP, clust_K, "Set1")

Silhueta Ajustada
clust_K_ajuste <- ajust_clust[,1]
dim_cluster(WP, clust_H_ajuste, "Set1")

APÊNDICE I

ajuste_sil <- function(sil, D, best = F, max.null = 100){
  neg <- which(sil[,3]<0)
  i = 0
  while(length(neg) != 0 & i < max.null){
    p <- sample(neg,1) 
    aux <- sil
    aux[p,1] <- aux[p,2]
    aux <- silhouette(aux[,1], D)
    if (!best | (best & mean(aux[, 3]) > mean(sil[, 3]))) {
      sil <- aux
      # cat(mean(sil[, 3]), "; \n ")
      i = 0
    } else i = i + 1
    neg <- which(sil[,3]<0)
  }
  return(sil)
}