#!/usr/bin/env python
"""
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):
        # Reálný dataset
        dataset = pd.read_csv("data_1025.csv", sep=" ")
        print(dataset)
        f1 = dataset['a'].values
        f2 = dataset['b'].values
        f3 = dataset['c'].values
        f4 = dataset['f'].values
        f5 = dataset['g'].values
        f6 = dataset['h'].values
        X = np.array(list(zip(f1, f2, f3, f4, f5, f6)))
        print(X)
        self.computeKmeans(X, 3, dataset)

    @staticmethod
    def computeKmeans(X, k, dataset):
        # 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)

        print(GetDaviesBouldin(centroids, clusters, k))

        # Daviesův-Bouldinův validační index (Davies-Bouldin validity index)
        inter_lenght = [[] for l in range(k)]
        inter_lenght_summary = np.empty((k,k,))
        centroids_lenght = np.empty((k,k,))
        summury_metrix = np.empty((k,k,))

        """
        Průměrná vzdálenost uvnitř shluku
        [[0.0], [0.0], [0.0], [1.5871699711831522]]
        """
        for i, centroid in enumerate(centroids):
            centroid.clear()
            centroid.append(np.mean(clusters[i], axis=0))

        temp = []
        for i, cluster in enumerate(clusters):
            for index in cluster:
                temp.append(euclidianDistanceMean(index,centroids[i]))
            inter_lenght[i].append(np.mean(temp[:], axis=0))

        """
        součet průměrných mezishlukových vzdáleností pro všechny dvojice různých shluků
        [[ 0.         5.97167604  5.45181999  5.15686113]
        [ 5.97167604  0.          4.64928021  4.35432134]
        [ 5.45181999  4.64928021  0.          3.8344653 ]
        [ 5.15686113  4.35432134  3.8344653   0.        ]]
        """
        for i, lenght_i in enumerate(inter_lenght):
            for j, lenght_j in enumerate(inter_lenght):
                inter_lenght_summary[i][j] = lenght_i[0] + lenght_j[0]
                inter_lenght_summary[i][i] = 0


        """
        Euklidovská vzdálenost mezi shluky (vdálenost centroidů)
        [[ 0.          27.73821191  13.70490423  13.4329483 ]
        [ 27.73821191   0.          14.92514657  28.39409974]
        [ 13.70490423  14.92514657   0.          14.36484946]
        [ 13.4329483   28.39409974  14.36484946   0.        ]]
        """
        for i, center1 in enumerate(centroids):
            for j, center2 in enumerate(centroids):
                centroids_lenght[i][j] = euclidianCentroids(center1, center2)
                centroids_lenght[i][i] = 0

        """
        Podíl součtu vnitroshlukových vzdáleností a mezishlukové vzdálenosti
        [[ 0.         0.42268431  0.5460712   1.40405177]
        [ 0.42268431  0.          0.29445698  0.57782938]
        [ 0.5460712   0.29445698  0.          0.76823865]
        [ 1.40405177  0.57782938  0.76823865  0.        ]]
        """
        for i, lenght_i in enumerate(inter_lenght):
            for j, lenght_j in enumerate(inter_lenght):
                summury_metrix[i][j] = inter_lenght_summary[i][j] / centroids_lenght[i][j]
                summury_metrix[i][i] = 0

        """
        Daviesův-Bouldinův validační index
        Maximum každého řádku a poté průměr maximálních hodnot
        Nízká hodnota tohoto indexu indikuje optimální počet shluků
        """
        DB_index = []
        for summury in summury_metrix:
            DB_index.append(max(summury))
        print("DB Index {}\t".format(np.mean(DB_index)))
        print("Průměrná vzdálenost uvnitř shluku: {}\t ".format(inter_lenght))
        datas = pd.DataFrame(dataset)
        print(datas)
        # ZOBRAZENÍ ŘEŠENÍ
        for i, cluster in enumerate(clusters):
            print("Blízká hodnota hazardu {}:".format(i))
            a, b, c, d, e, ff  = zip(*cluster)
            for i, cislo in enumerate(ff):
                for j, jmeno in enumerate(datas.h):
                    if datas.h.loc[j] == cislo:
                        print(datas.iloc[j][0])

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




