Esse projeto é a aplicação de 2 conceitos que aprendi essa semana em cursos diferentes que venho fazendo.
No bootcamp - Analista de dados do IGTI (Instituto de Gestão e Tecnologia da Informação) revisei os conceitos do algorítimo k-means e fiz um exercício muito visual mostrando como ocorre a clusterização dos dados.
No Data Science Career Path do Codecademy, fiz um projeto de visualização 3D da constelação de Orion e o gráfico ficou muito legal.
Achei interessante fazer o exercício de unir esses dois conceitos e ver como ficaria o resultado do k-means em dados tridimensionais. Será que é possível?
Para entender melhor o que quero fazer, segue a visualização final dos dois projetos:
a) Constelação de Orion
'''Pacotes'''
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from math import sqrt
'''Coodenadas da constelação'''
x = [-0.41, 0.57, 0.07, 0.00, -0.29, -0.32,-0.50,-0.23, -0.23]
y = [4.12, 7.71, 2.36, 9.10, 13.35, 8.13, 7.19, 13.25,13.43]
z = [2.06, 0.84, 1.56, 2.07, 2.36, 1.72, 0.66, 1.25,1.38]
'''Plot'''
fig_3d = plt.figure()
fig_3d.add_subplot(1,1,1, projection='3d').scatter(x,y,z)
plt.show()
Observação: No jupyter-notebook dá para rotacionar mas aqui no google colab o gráfico veio como uma figura estática.
b) Algorítimo k-means 2d
'''Pacotes'''
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
'''Dataset (dados aleatórios)'''
X, y = make_blobs(n_samples=500, centers= 50, random_state=0)
'''Plot dos dados originais'''
plt.close()
plt.scatter(X[:,0] , X[:,1])
plt.show()
Utilizamos a curva curva wcss (Within-Cluster-Sum-of-Squares) para encontrar o número de clusters ideais, chegamos a conclusão de que 5 seria o número ótimo
'''Função Número ideal de clusters (wcss)'''
def calculate_wcss(data):
wcss = []
for i in range(1,30):
kmeans = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10)
kmeans.fit(data)
wcss.append(kmeans.inertia_)
return wcss
'''Cálculo do wcss'''
wcss = calculate_wcss(X)
'''Plot da curva css'''
plt.close()
plt.plot(range(1,30), wcss)
plt.title('Elbow Method')
plt.xlabel('Number of clusters')
plt.ylabel('WCSS')
plt.show()
'''Plot dos clusters'''
kmeans = KMeans(n_clusters=5, init='k-means++', max_iter=300, n_init=10, random_state=0)
pred_y = kmeans.fit_predict(X)
plt.scatter(X[:,0], X[:,1], c=pred_y)
plt.scatter(kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], s=50, c='red')
plt.show()
'''Dataset (dados aleatórios)'''
X, y = make_blobs(n_samples=800, centers= 100, random_state=0, n_features=3)
# Para mudar para dados 3d bastou adicionar o n_features = 3.
# Par melhorar a distribuição dos dados no espaço 3d aumentei o n_samples para 800 e o n_centers para 100.
'''Plot dos dados originais'''
fig_3d = plt.figure()
fig_3d.add_subplot(1,1,1, projection='3d').scatter(X[:,0], X[:,1], X[:,2])
plt.show()
'''Número ideal de clusters - Curva wcss'''
wcss = []
for i in range(1,30):
kmeans = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10)
kmeans.fit(X)
wcss.append(kmeans.inertia_)
# 5 clusters continua sendo a divisão ideal para esse dataset
'''Plot da curva css'''
plt.plot(range(1,30), wcss)
plt.title('Elbow Method')
plt.xlabel('Number of clusters')
plt.ylabel('WCSS')
plt.show()
Quantidade ideal de clusters
Esse tópico não havia sido abordado inicialmente. Acontece que o Elbow method é geralmente é passado como um método visual: "Ah! Dá uma olhada aqui e são mais ou menos 7 clusters" e eu não gosto de resultados inexatos. Procurei na internet alguma função que pudesse me passar a fórmula exata. Cheguei em uma solução muito interessante: https://jtemporal.com/kmeans-and-elbow-method/
'''Pacote math'''
from math import sqrt
'''Função optimal n_clusters'''
def optimal_number_of_clusters(wcss):
x1, y1 = 2, wcss[0]
x2, y2 = 30, wcss[len(wcss)-1] # Devemos mudar aqui conforme o número de iterações que nós fizemos. No caso, 30. Algumas pessoas fazem mais e outras menos.
distances = []
for i in range(len(wcss)):
x0 = i+2
y0 = wcss[i]
numerator = abs((y2-y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)
denominator = sqrt((y2 - y1)**2 + (x2 - x1)**2)
distances.append(numerator/denominator)
return distances.index(max(distances)) + 1
'''Cálculo do n_clusters ideal'''
n_ideal = optimal_number_of_clusters(wcss)
print(n_ideal)
# Aqui descobrimos que o n_clusters ideal é 8. Vamos fazer com 5 apenas para montar uma visualização melhor.
'''Plot dos clusters'''
kmeans = KMeans(n_clusters=5, init='k-means++', max_iter=300, n_init=10, random_state=0)
pred_y = kmeans.fit_predict(X)
fig_3d = plt.figure()
fig_3d.add_subplot(1,1,1, projection='3d').scatter(X[:,0], X[:,1], X[:,2], c=pred_y)
plt.show()
O exercício era para ter acabado aqui mas fiquei curiosa em saber como é a evolução do cluster à cada iteração em uma amostra 3D. Sabendo que n_init
é o número de vezes que um centroide é esolhido e max_iter
é o número máximo de iterações que o k-means irá fazer, vou fazer uma escolha de centroide e X iterações e plotar em subplots utilizando um loop for:
'''Pacote para fazer o download de imagens do colab'''
from google.colab import files
'''Plot dos clusters'''
for i in range(1,11):
kmeans = KMeans(n_clusters=5, init='k-means++', max_iter=i, n_init=1, random_state=0)
pred_y = kmeans.fit_predict(X)
fig_3d = plt.figure()
fig_3d.add_subplot(1,1,1, projection='3d').scatter(X[:,0], X[:,1], X[:,2], c=pred_y)
plt.title('Iteration {}'.format(i))
plt.savefig('kmeans{}.jpg'.format(i))
files.download('kmeans{}.jpg'.format(i))
plt.close()
Para finalizar, fiz um pequeno vídeo com os plots gerados para as 10 iterações, dê uma conferida:
from IPython.display import YouTubeVideo
YouTubeVideo('1HhTusjL42k', width=560, height=315)
Ao final do experimento aprendi algumas coisas interessantes: