__author__ = "Vaclav Oujezsky, xoujez03"

# Daviesův-Bouldinův validační index (Davies-Bouldin validity index)
#
# Daviesův-Bouldinův validační index je podíl sumy vnitro-shlukového rozložení
# a mezi-shlukového rozložení. Hodnoty tohoto indexu získáme ze vzorce:
# DB = 1/n = sum_{i = 1}^{n} max_{i != j} {Sn(Qi)+ Sn(Qj) / Sn(Qi, Qj)}
# kde "n" je počet shluků,  "Sn(Qi)" je průměrná vzdálenost objektů ve shluku od středu shluku
# a "Sn(Qi, Qj)"je vzdálenost mezi středy shluků, které mohou být reprezentovány centroidy nebo medoidy.
# Optimální počet shluků je indikován nízkými hodnotami tohoto indexu, které získáme, když jsou
# shluky kompaktní a daleko od sebe.
#
#
#  k = int : počet klasterů
#  centers = [X[i] for i in random.sample(range(len(X)), k)]
#  centroids = [[] for j in range(k)]
#  clusters = [[] for l in range(k)]
#  X = np.array(list(zip(f1, f2)))
#  f1, f2 = data

import numpy as np
import math

class GetDaviesBouldin(object):
    def __init__(self, centroids, clusters, k):
        self.dbIndex = []
        self.interLenSum = []
        self.Centroids = centroids
        self.Clusters = clusters
        self.K = k
        self._solveDB()


    def _solveDB(self):
        inter_lenght = [[] for l in range(self.K)]
        inter_lenght_summary = np.empty((self.K, self.K,))
        centroids_lenght = np.empty((self.K, self.K,))
        summury_metrix = np.empty((self.K, self.K,))

        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

        def _euclidianDistanceMean(adept, cen):
            mean_center = np.mean(cen, axis=0)
            distance = (math.sqrt(sum([(a-b) ** 2 for a, b in zip(adept, mean_center)])))
            return distance


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

        temp = []
        for i, cluster in enumerate(self.Clusters):
            for index in cluster:
                temp.append(_euclidianDistanceMean(index, self.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(self.Centroids):
            for j, center2 in enumerate(self.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))

        self.dbIndex = np.mean(DB_index)
        self.interLenSum = sum(sum(inter_lenght, []))

    def __str__(self):
        return "DB Index {} ".format(self.dbIndex)

