Introdução
Técnicas de clusterização são largamente utilizadas para o reconhecimento de padrões em conjuntos de dados. Elas são consideradas técnicas de aprendizado de máquina não-supervisionado, pois sua aplicação se dá, em geral, em dados onde as classes das observações não são conhecidas. Por esse motivo, auxiliam na descoberta de agrupamentos - os chamados clusters - que possam fazer sentido para os dados em questão. Desse modo, ao final da aplicação do método, os clusters são constituídos por objetos (observações) que compartilham características semelhantes entre si, permitindo sua diferenciação dos demais.
Sobre o K-means
Nesse contexto, a técnica conhecida como K-means é uma das mais utilizadas para esta finalidade. O algoritmo do k-means inicia com centróides (como são conhecidos os centros de cada cluster) aleatórios, a partir dos quais é iniciado um processo iterativo para otimizar o ponto usado como centróide que será considerado ao final. O centróide “ótimo” para cada cluster seria aquele que permitisse a melhor diferenciação dos demais. O ideal é que os objetos pertencentes a aquele grupo estejam bem próximos do centróide. Quanto mais homogêneo for cada cluster, melhor sua diferenciação quanto aos demais.
Assim sendo, essa estratégia pode ser aplicada como forma de descobrir a classe de objetos cuja classe antes era desconhecida, ou mesmo para o processo de descoberta de conhecimento.
Sobre o conjunto de dados
O presente trabalho utiliza um conjunto de dados com características físico-químicas de vinhos. Algumas dessas características estão diretamente ligadas com a composição do produto, como por exemplo a acidez, alcalinidade, a intensidade da cor, o teor alcoólico e, ainda, a presença de diversas substâncias como magnésio e ácido málico. Os vinhos presentes nos dados em questão são provenientes de cultivos realizados em uma região específica da Itália, que não foi informada no repositório onde os dados foram coletados.
Originalmente, o conjunto de dados apresentava 14 variáveis, sendo a 14ª delas sobre o tipo de vinho em questão. Como estamos, aqui, interessados na aplicação de uma técnica não-supervisionada, parte-se do princípio que essas classes não são conhecidas. Por esse motivo, a última coluna já se encontrava removida quando coletada. Assim, a clusterização pode ser realizada sem a influência do conhecimento prévio das classes, que é o correto.
Vejamos um pequeno resumo da estrutura dos nossos dados:
wines=read.csv("wine-clustering.csv")str(wines)'data.frame': 178 obs. of 13 variables:
$ Alcohol : num 14.2 13.2 13.2 14.4 13.2 ...
$ Malic_Acid : num 1.71 1.78 2.36 1.95 2.59 1.76 1.87 2.15 1.64 1.35 ...
$ Ash : num 2.43 2.14 2.67 2.5 2.87 2.45 2.45 2.61 2.17 2.27 ...
$ Ash_Alcanity : num 15.6 11.2 18.6 16.8 21 15.2 14.6 17.6 14 16 ...
$ Magnesium : int 127 100 101 113 118 112 96 121 97 98 ...
$ Total_Phenols : num 2.8 2.65 2.8 3.85 2.8 3.27 2.5 2.6 2.8 2.98 ...
$ Flavanoids : num 3.06 2.76 3.24 3.49 2.69 3.39 2.52 2.51 2.98 3.15 ...
$ Nonflavanoid_Phenols: num 0.28 0.26 0.3 0.24 0.39 0.34 0.3 0.31 0.29 0.22 ...
$ Proanthocyanins : num 2.29 1.28 2.81 2.18 1.82 1.97 1.98 1.25 1.98 1.85 ...
$ Color_Intensity : num 5.64 4.38 5.68 7.8 4.32 6.75 5.25 5.05 5.2 7.22 ...
$ Hue : num 1.04 1.05 1.03 0.86 1.04 1.05 1.02 1.06 1.08 1.01 ...
$ OD280 : num 3.92 3.4 3.17 3.45 2.93 2.85 3.58 3.58 2.85 3.55 ...
$ Proline : int 1065 1050 1185 1480 735 1450 1290 1295 1045 1045 ...
Como vimos, o dataset é composto por 13 variáveis, todas elas numéricas, e possui um total de 178 observações.
As variáveis são:
- Alcohol: teor alcoólico
- Malic_Acid: concentração de ácido málico
- Ash: restos de matéria inorgânica após evaporação
- Ash_Alcanity: alcalinidade dos restos de matéria inorgânica após evaporação
- Magnesium: concentração de magnésio
- Total_Phenols: concentração total de fenóis
- Flavanoids: concentração de flavonóides
- Nonflavanoid_Phenols: concentração de fenóis não-flavonóides
- Proanthocyanins: concentração de um tipo específico de fenol encontrado na uva
- Color_Intensity: intensidade da cor do vinho
- Hue: tonalidade do vinho
- OD280: concentração de uma proteína conhecida como OD280
- Proline: concentração da substância prolina
Conforme já citado, é esperado que vinhos do mesmo tipo compartilhem valores parecidos para essas variáveis. Isso será descoberto após a correta aplicação do método k-means.
Análise exploratória
Um importante passo do trabalho com qualquer tipo de algoritmo de aprendizado de máquina é a análise exploratória dos dados, pois é imprescindível um conhecimento sobre o dado que se pretende trabalhar.
Resumo estatístico dos dados
Abaixo, tem-se um resumo estatístico dos dados, com média, mediana, mínimos, máximos e respectivos quartis para cada uma das covariáveis:
summary(wines) Alcohol Malic_Acid Ash Ash_Alcanity
Min. :11.03 Min. :0.740 Min. :1.360 Min. :10.60
1st Qu.:12.36 1st Qu.:1.603 1st Qu.:2.210 1st Qu.:17.20
Median :13.05 Median :1.865 Median :2.360 Median :19.50
Mean :13.00 Mean :2.336 Mean :2.367 Mean :19.49
3rd Qu.:13.68 3rd Qu.:3.083 3rd Qu.:2.558 3rd Qu.:21.50
Magnesium Total_Phenols Flavanoids Nonflavanoid_Phenols
Min. : 70.00 Min. :0.980 Min. :0.340 Min. :0.1300
1st Qu.: 88.00 1st Qu.:1.742 1st Qu.:1.205 1st Qu.:0.2700
Median : 98.00 Median :2.355 Median :2.135 Median :0.3400
Mean : 99.74 Mean :2.295 Mean :2.029 Mean :0.3619
3rd Qu.:107.00 3rd Qu.:2.800 3rd Qu.:2.875 3rd Qu.:0.4375
Proanthocyanins Color_Intensity Hue OD280
Min. :0.410 Min. : 1.280 Min. :0.4800 Min. :1.270
1st Qu.:1.250 1st Qu.: 3.220 1st Qu.:0.7825 1st Qu.:1.938
Median :1.555 Median : 4.690 Median :0.9650 Median :2.780
Mean :1.591 Mean : 5.058 Mean :0.9574 Mean :2.612
3rd Qu.:1.950 3rd Qu.: 6.200 3rd Qu.:1.1200 3rd Qu.:3.170
Proline
Min. : 278.0
1st Qu.: 500.5
Median : 673.5
Mean : 746.9
3rd Qu.: 985.0
[ reached getOption("max.print") -- omitted 1 row ]
Distribuição das variáveis
Pode-se obter, ainda, um boxplot para as variáveis. Isso será de ajuda para entender melhor como essas variáveis estão distribuídas e seus espaços de variação.
wines %>%
gather(Attributes, values, c(1:4, 6:12)) %>%
ggplot(aes(x=reorder(Attributes, values, FUN=median), y=values, fill=Attributes)) +
geom_boxplot(show.legend=FALSE) +
labs(title="Boxplots - Características dos Vinhos") +
theme_bw() +
theme(axis.title.y=element_blank(),
axis.title.x=element_blank()) +
ylim(0, 35) +
coord_flip()Um importante aspecto que pode ser observado é que o espaço de variação das variáveis é bastante distinto para algumas delas; ou seja: há uma grande diferença de escala. O algoritmo k-means é consideravelmente sensível a essas mudanças de escala. Por esse motivo, será adequada a padronização das variáveis, para que o desempenho do método não seja prejudicado. Isso será feito mais adiante.
Presença de correlações
A matriz de correlações fornece evidências sobre como as variáveis influenciam umas nas outras. Vejamos:
# Correlation matrix
corrplot(cor(wines), type="upper", method="number", tl.cex=0.5,number.cex=0.6)A princípio, as únicas correlações fortes observadas são:
- Total_Phenols x Flavonoids (0.86). Essa correlação alta faz total sentido, uma vez que os flavonóides também são compostos que fazem parte da classe dos fenóis. Assim sendo, muito provavelmente as duas variáveis explicam a mesma coisa. Apesar disso, não serão removidas de nossa análise.
- OD280 x Total_Phenols (0,70) e OD280 x Flavonoids (0,79). As relações podem indicar que essas variáveis explicam aspectos parecidos do vinho (ou o mesmo aspecto).
Pré-processamento
Padronização dos dados
Conforme previamente visto, o fato de que os espaços de variação das covariáveis são muito distintos pode (e provavelmente irá) afetar o desempenho do k-means. Por esse motivo, é conveniente a padronização dos dados. Assim sendo, cada variável conservará exatamente a mesma informação obtida inicialmente, porém com espaços de variação que podem ser diretamente comparáveis com as demais.
wines_std <- as.data.frame(scale(wines))Tenha-se em mente, portanto, que desse ponto em diante está sendo utilizada a versão padronizada do dataset.
Aplicação do K-means
Número de clusters
Uma vez que não se conhece, inicialmente, a quantidade de clusters presentes no conjunto de dados, se faz necessária essa descoberta. Ou seja, é preciso descobrir qual valor de “K” melhor se aplica ao conjunto de dados para que a clusterização seja bem sucedida.
Isso será feito com base nos valores das distâncias entre clusters e distâncias intra clusters. Ambas são calculadas com base na soma de quadrados das distâncias entre clusters distintos e entre as observações de um mesmo cluster, respectivamente.
entre_clusters=numeric()
intra_clusters=numeric()
set.seed(2022)
for(i in 1:10){
entre_clusters[i] = kmeans(wines_std, centers=i)$betweenss
intra_clusters[i] = kmeans(wines_std, centers=i)$tot.withinss
}
plot1 = qplot(1:10, entre_clusters, geom=c("point", "line"),
xlab="Quantidade de clusters",
ylab="Soma de quadrados entre clusters") +
scale_x_continuous(breaks=seq(0, 10, 1)) +
theme_bw()
plot2 = qplot(1:10, intra_clusters, geom=c("point", "line"),
xlab="Quantidade de clusters",
ylab="Soma de quadrados intra cluster") +
scale_x_continuous(breaks=seq(0, 10, 1)) +
theme_bw()
grid.arrange(plot1, plot2, ncol=2)Escolha de K
A escolha para o valor de K se baseia no seguinte trade-off:
O valor de K deve ser tal que a soma de quadrados entre clusters seja a maior possível e a soma de quadrados intra cluster seja a menor possível. Isso é desejado porque quanto maior a soma de quadrado entre clusters, significa que eles estão mais distantes entre si, e portanto mais fáceis de serem diferenciados. E quanto menor a soma de quadrados intra clusters, significa que os objetos dentro de um cluster estão bastante próximos, tornando-o mais homogêneo.
No entanto, a escolha deve também levar em consideração que o ganho pela adição de um cluster a mais nem sempre é vantajoso. Isto é: vale a pena adicionar mais um cluster em comparação ao aumento ou diminuição das somas de quadrados? Se o ganho for considerável, então aumenta-se o valor de K escolhido. Caso contrário, mantém-se o valor menor, pois uma quantidade pequena de clusters facilita a visualização e a identificação das classes no resultado.
Em nosso caso, o valor de K=3 parece o mais adequado. Isso é percebido analisando as inclinações nos gráficos. De k=3 para k=4, o “ganho” (aumento ou diminuição) das somas de quadrados não é muito significativo, então mantemos k=3.
Obtenção dos clusters
Vamos então à aplicação do algoritmo K-means com K=3, para a obtenção dos clusters.
set.seed(2022)
wines_k3 = kmeans(wines_std, centers=3)
aggregate(wines_std, by=list(wines_k3$cluster), mean) Group.1 Alcohol Malic_Acid Ash Ash_Alcanity Magnesium
1 1 0.1644436 0.8690954 0.1863726 0.5228924 -0.07526047
2 2 -0.9234669 -0.3929331 -0.4931257 0.1701220 -0.49032869
3 3 0.8328826 -0.3029551 0.3636801 -0.6084749 0.57596208
Total_Phenols Flavanoids Nonflavanoid_Phenols Proanthocyanins
1 -0.97657548 -1.21182921 0.72402116 -0.77751312
2 -0.07576891 0.02075402 -0.03343924 0.05810161
3 0.88274724 0.97506900 -0.56050853 0.57865427
Color_Intensity Hue OD280 Proline
1 0.9388902 -1.1615122 -1.2887761 -0.4059428
2 -0.8993770 0.4605046 0.2700025 -0.7517257
3 0.1705823 0.4726504 0.7770551 1.1220202
Resultados
Características em cada cluster
A aplicação do método K-means com K=3 resulta na obtenção de uma clusterização em 3 grupos distintos. Vejamos agora de que forma esses grupos se comportam.
Obs: os gráficos foram separados para facilitar a visualização.
ggpairs(cbind(wines_std, Cluster=as.factor(wines_k3$cluster)),
columns=1:6, aes(colour=Cluster, alpha=0.5),
lower=list(continuous="points"),
upper=list(continuous="blank"),
axisLabels="none", switch="both") +
theme_bw()ggpairs(cbind(wines_std, Cluster=as.factor(wines_k3$cluster)),
columns=7:13, aes(colour=Cluster, alpha=0.5),
lower=list(continuous="points"),
upper=list(continuous="blank"),
axisLabels="none", switch="both") +
theme_bw()Através dos dois gráficos anteriores, é possível visualizar, nas diagonais, a forma da distribuição de cada uma das variáveis dentro de seus respectivos clusters. Isso ajuda a identificar quais dessas características mais se destacam em cada um dos três tipos de vinho.
As características dos vinhos que mais diferenciam os clusters são:
- Teor alcoólico;
- Concentração de ácido málico;
- Concentração total de fenóis;
- Concentração de flavonóides;
- Intensidade da cor;
Para as demais variáveis a diferenciação não é difícil, porém há algumas interseções que mostram similaridades entre dois ou três tipos de vinho simultaneamente.
Clusterização em geral
Com a finalidade de visualizar o resultado final “macro” da clusterização (e não apenas variável a variável), é interessante a visualização a seguir, construída no espaço 2-dimensional:
fviz_cluster(wines_k3, data = wines_std,
ellipse = FALSE,
star.plot = FALSE,
repel = TRUE,
ggtheme = theme_minimal(),
) Embora não se saiba (para a finalidade do trabalho) quais são, originalmente, os tipos de cada vinho, chegou-se a um resultado bastante satisfatório com K=3, no qual se apresentam três clusters consideravelmente homogêneos, bem definidos e separados dos demais.
No entanto, uma observação importante é cabível: o conjunto de dados original apresentava vinhos em exatamente três tipos/segmentos distintos. Portanto, a análise aqui realizada foi capaz de chegar exatamente à mesma quantidade de classes utilizada originalmente.
Obtenção da medida silhouette
Outra forma de confirmar o número de clusters mais adequado é através da medida de silhouette. Em resumo, é uma medida que reflete a qualidade da clusterização para cada valor de k, informando quanto, em média, cada objeto está bem encaixado em seu respectivo cluster.
fviz_nbclust(wines_std, kmeans, method = "silhouette") Valores mais altos para a média da silhouette indicam clusters com melhor qualidade. Assim sendo, novamente, o valor ideal escolhido seria k=3.
Conclusão
O presente trabalho explorou a utilização da técnica de clusterização conhecida como K-means, que aplica um algoritmo para a criação de clusters (agrupamentos) que permitem identificar objetos com características semelhantes. A técnica foi aplicada à clusterização de vinhos, e mostrou resultados muito satisfatórios com K=3, gerando clusters bem definidos e que permitem a diferenciação entre os 3 tipos de vinho identificados.
Tendo em mãos tais resultados, trabalhos futuros poderiam explorar outras técnicas de aprendizado de máquina, combinando com técnicas supervisionadas e utilizando os tipos de vinhos descobertos na análise atual. Essa combinação de técnicas poderia permitir a identificação dos vinhos com maior assertividade.