<jupyter_start><jupyter_text>Praca domowa 5 - klasteryzacja<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>Wczytujemy zbiorek<jupyter_code>data = pd.read_csv('./../../clustering.csv', header=None)
plt.scatter(data.loc[:,0], data.loc[:,1])<jupyter_output><empty_output><jupyter_text>Jak ja to potencjalnie widzę? ![image](./klastry.jpg) Z takiego grupowania na oko, tak właśnie widziałbym podział w zależności od ilości klastrów. Potencjalnie grupy zaznaczone tym samym kolorem możnaby połączyć. Zobaczmy jednak jakie wyniki dają modele klasteryzujące.  Pierwszy testowany model - KMeans Zbiór jest na tyle prosty, że algorytm K-średnich powinien poradzić sobie przyzwoicie.<jupyter_code>from sklearn.cluster import KMeans
model_1 = KMeans(n_clusters=8, random_state=0).fit(data)<jupyter_output><empty_output><jupyter_text>Zobaczmy wstępny wynik<jupyter_code>plt.scatter(data.loc[:,0], data.loc[:,1], c=model_1.labels_, marker = 'o')<jupyter_output><empty_output><jupyter_text>Jak widać algorytm K-średnich na domyślnych parametrach, wyznaczył dokładnie takie klastry jak ja na pierwszy rzut oka. Sprawdźmy zatem jaką ilość podpowiada nam znana metoda łokcia!  KMeans i metoda łokcia Skorzystajmy z gotowej metryki do obliczania odległości punktów wewnątrz klastrów, bo... czemu nie?<jupyter_code>def count_wcss_scores(data, k_max):
    #  WCSS = within-cluster sum of squares
    scores = []
    for k in range(1, k_max+1):
        model = KMeans(n_clusters=k, random_state=0)
        model.fit(data)
        wcss = model.score(data) * -1 # score returns -WCSS
        scores.append(wcss)
    return scores
wcss_vec = count_wcss_scores(data, 15) # możemy zrobić i dla 15, nie powinno zająć to dużo czasu
x_ticks = list(range(1, len(wcss_vec) + 1))
plt.plot(x_ticks, wcss_vec, 'bx-')
plt.xlabel('k')
plt.ylabel('Within-cluster sum of squares')
plt.title('The Elbow Method showing the optimal k')
plt.show()<jupyter_output><empty_output><jupyter_text>I zgodnie z przewidywaniami. Silne wypłaszczenie zaczęło się na k = 5 a kończy na k = 8. Później jest jużpodobnie. Co oznacza, że metoda łokcia sprawdza siętu świetnie. Możemy zobaczyć, że każde z tych doborów k z przedziału [5,8], niesie za sobą jakąś ciekawą informację i ma sens.<jupyter_code>fig, axes = plt.subplots(2, 2, figsize=(20, 20))
for k in range(5, 9):
    model = KMeans(n_clusters=k, random_state=0).fit(data)
    i, j = (k-1)//2%2, (k-1)%2
    axes[i][j].scatter(data.loc[:,0], data.loc[:,1], c=model.labels_, marker = 'o')
    axes[i][j].set(aspect = 'equal')
    axes[i][j].set_title('k=' + str(k))<jupyter_output><empty_output><jupyter_text>Jak widzimy każdy z tych podziałów zachowuje swój własny sens. Ciężko powiedzieć, że któryś jest dobry albo zły. Myślę, że przy danych dwuwymiarowych często spotyka się takie sytuacje.  Drugi model - klastrowanie aglomeracyjne Sprawdźmy jak radzi sobie nieco bardziej zaawansowana metoda, taka jak AgglomerativeClustering<jupyter_code>from sklearn.cluster import AgglomerativeClustering
model_2 = AgglomerativeClustering(n_clusters=8).fit(data)
plt.scatter(data.loc[:,0], data.loc[:,1], c=model_2.labels_, marker = 'o')
# domyślna metoda 'ward' minimalizuję wariancję między potencjalnie łączonymi klastrami<jupyter_output><empty_output><jupyter_text>Wstępny wynik jest bardzo podobny, chociaż można odnieść wrażenie, że podział jest "gładszy"(tzn. nie ma wyraźnych linii cięć pomiędzy grupami).  Szukanie ilości klastrów metodą silhouette'a Tutaj oczywiście też skorzystam z funkcji pisanych na laboratoriach.<jupyter_code>def count_clustering_scores(data, cluster_num, model, score_fun):
    if isinstance(cluster_num, int):
        cluster_num_iter = [cluster_num]
    else:
        cluster_num_iter = cluster_num
        
    scores = []    
    for k in cluster_num_iter:
        model_instance = model(n_clusters=k)
        labels = model_instance.fit_predict(data)
        wcss = score_fun(data, labels)
        scores.append(wcss)
    
    if isinstance(cluster_num, int):
        return scores[0]
    else:
        return scores
from sklearn.metrics import silhouette_score

cluster_num_seq = range(2, 15)
silhouette_vec = count_clustering_scores(data, cluster_num_seq, KMeans, silhouette_score)
plt.plot(cluster_num_seq, silhouette_vec, 'bx-')
plt.xlabel('k')
plt.ylabel('Silhouette score')
plt.show()<jupyter_output><empty_output><jupyter_text>Zgodnie z metryką silhouette, dobrze trafiono z użyciem 8 klastrów, ale wysoko punktuje też użycia 7 i 9. Spójrzmy na wykresy.<jupyter_code>fig, axes = plt.subplots(3, figsize=(20, 20))
for k in range(7, 10):
    model = AgglomerativeClustering(n_clusters=k).fit(data)
    i = k-7
    axes[i].scatter(data.loc[:,0], data.loc[:,1], c=model.labels_, marker = 'o')
    axes[i].set(aspect = 'equal')
    axes[i].set_title('k=' + str(k))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10792818236836796, 'fraction_numerical': 0.012507565059511802, 'mean_word_length': 5.283903675538657, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '661225', 'license': 'Apache-2.0', 'n_tokens_mistral': 2018, 'n_tokens_neox': 2014, 'n_words': 447, 'path': 'Prace_domowe/Praca_domowa5/Grupa1/Mikolaj_Jakubowski/PD5.ipynb', 'repo_name': 'niladrem/2020L-WUM', 'size': '4957'}