"""
K-Means (xoujez03)
Postup zvolen podle teorie:
http://mnemstudio.org/clustering-k-means-example-1.htm
Změna proti "Lloyd’s Algorithm" algoritmu v okamžitém přemístění člena při zjištění rozdílu s klasterem

Zajímavé porovnání implementace Euklidovské vzálenosti v Python:
http://www.codehamster.com/2015/03/09/different-ways-to-calculate-the-euclidean-distance-in-python/

"""
from DaviesBouldin import GetDaviesBouldin
import unittest
import pandas as pd
import numpy as np
import random
import math
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_circles

plt.rcParams['font.family'] = 'serif'
plt.rcParams['font.serif'] = ['Cambria Math'] + plt.rcParams['font.serif']


# výpočet Euklidovské vzdálenosti
def euclidianDistanceMean(adept, centroids):
    mean_center = np.mean(centroids, axis=0)
    distance = (math.sqrt(sum([(a - b) ** 2 for a, b in zip(adept, mean_center)])))
    return distance

def euclidianDistance(adept, centroids):
    distance = (math.sqrt(sum([(a - b) ** 2 for a, b in zip(adept, centroids)])))
    return distance

def euclidianCentroids(center1, center2):
    center1 = np.mean(center1, axis=0)
    center2 = np.mean(center2, axis=0)
    distance = (math.sqrt(sum([(a - b) ** 2 for a, b in zip(center1, center2)])))
    return distance


# TESTOVACÍ TŘÍDA
class KmeansCase(unittest.TestCase):
    def test(self):
        dataset = pd.read_csv("data_1024.csv",sep="\t")
        f1 = dataset['Distance_Feature'].values
        f2 = dataset['Speeding_Feature'].values
        X = np.array(list(zip(f1, f2)))
        plt.scatter(dataset['Distance_Feature'], dataset['Speeding_Feature'])
        plt.xlabel('$Vzdálenost$')
        plt.ylabel('$Rychlost$')
        #X, y = make_circles(n_samples=400, factor=.3, noise=.05)
        #plt.scatter(X[:, 0], X[:, 1])
        plt.show()
        self.computeKmeans(X, 4)

    @staticmethod
    def computeKmeans(X, k):
        # vytvoření a naplnění proměnných
        centers = [X[i] for i in random.sample(range(len(X)), k)]
        centroids = [[] for j in range(k)]
        clusters = [[] for l in range(k)]

        # vložení prvních členů (center) do klastru
        for i, member in enumerate(centroids[:]):
            centroids[i].append(centers[i])

        # výpočet Euklidovské vzdálenosti každého klasteru a vložení hodnoty do nejvyhovující skupiny
        # porovnává každého člena a průměrnou hodnotu klasteru z pohledu Euklidovské vzdálenosti
        for individum in X:
            for i, centroid in enumerate(centroids):
                # nejmenší hodnota vůči centroidům
                if (euclidianDistanceMean(individum, centroid)) <= min(euclidianDistanceMean(individum, centroid) \
                                                                     for centroid in centroids):
                    clusters[i].append(individum)
                    centroid.clear()
                    # přepočítání centroidů
                    centroid.append(np.mean(clusters[i], axis=0))


        # KONVERGENCE (dokud počet přesunů členů není 0)
        # porovnává každého člena s vlastním klastrem a klastrem sousedním, v případě nalezení vhodnějšího klasteru
        # vybraného člena přesune do nejvyhovujícího klasteru z pohledu průměrné hodnoty klasteru a Euklidovské vzdálenosti s vzbraným členem

        relocation = 1
        R = 0
        while relocation != 0:
            print("(Round R) {}\t".format(R))
            R += 1
            for cluster_index, cluster in enumerate(clusters):
                relocation = 0
                for index, get_value in enumerate(cluster):
                    if (euclidianDistanceMean(get_value, cluster)) > (min(euclidianDistanceMean(get_value, old_cluster) for old_cluster in clusters)):
                        a, value = min(enumerate(clusters), key=lambda x: euclidianDistanceMean(get_value, x[1]))
                        relocation += 1
                        for position, value in enumerate(cluster):
                            if (get_value == value).all():
                                clusters[cluster_index].pop(position)
                        clusters[a].append(get_value)

        # Daviesův-Bouldinův validační index (Davies-Bouldin validity index)
        print(GetDaviesBouldin(centroids, clusters, k))

        # ZOBRAZENÍ ŘEŠENÍ
        paired = plt.get_cmap('Paired')
        colors = iter(paired(np.linspace(0, 1, k)))
        for i, cluster in enumerate(clusters):
            x, y = zip(*cluster)
            plt.scatter(x,y, color=next(colors))

        for i, centroid in enumerate(centroids):
            centroid.clear()
            centroid.append(np.mean(clusters[i], axis=0))
            a, b = zip(*centroid)
            plt.scatter(a,b, color="black", marker="*", s=60)

        plt.xlabel('$Vzdálenost$')
        plt.ylabel('$Rychlost$')
        plt.show()

if __name__ == '__main__':
    unittest.main()




