<jupyter_start><jupyter_text>Practica sobre K-Means y GMM<jupyter_code>import numpy as np
import pandas as pd
import random
import seaborn as sns; sns.set()
import matplotlib.pyplot as plt
from  sklearn.mixture import GaussianMixture

base_data_or = pd.read_csv('p1_estaturas.csv')
base_data = base_data_or.to_numpy()
#base_data
sns.scatterplot(x="Edad", y="Estatura",  palette="Set1",s=75, data=base_data_or)
def k_predict(k, valores):
    # (x-x*)  (y - y*)
    diferencias = valores[:,None]- k

    # (x-x*)^2 + (y - y*)^2
    suma_cuadrados = np.sum(np.square(diferencias),axis=2)

    # distancia euclidiana sqrt( suma_cuadrados )
    distancia_euc = np.sqrt(suma_cuadrados)

    # seleccion del k con la distancia mas cercana
    assigned = np.argmin(distancia_euc,axis=1)
    return assigned

def kmeans(data,epochs = 5,k = 5,status=False, seed=False):
    if seed:
        random.seed(101)
    j = np.array([])
    total = len(data)
    # seleccion aleatoria de elementos como k iniciales
    randomlist = random.sample(range(0, total-1), k)
    listk = np.array([data[i] for i in randomlist])
    # registro para saber donde iniciaron los k
    init_k =  np.copy(listk)
    
    for i in range(epochs):
        # (x-x*)  (y - y*)
        diferencias = data[:,None]- listk

        # (x-x*)^2 + (y - y*)^2
        suma_cuadrados = np.sum(np.square(diferencias),axis=2)

        # distancia euclidiana sqrt( suma_cuadrados )
        distancia_euc = np.sqrt(suma_cuadrados)

        # seleccion del k con la distancia mas cercana
        assigned = np.argmin(distancia_euc,axis=1)
        #print(assigned)

        # sumatoria de las distancias minimas de cada elemento 
        #   :distancia_euc tiene todas las k-esimas distancias, 
        #   por eso se toma el minimo. en este caso
        j = np.append(j,sum(np.min(distancia_euc,axis=1))/total)
        if status:
            print(j[i])
        # reasignacion de k
        for i in range(k):
            # filtro del los datos para cada k
            subgrupo = data[assigned==i]
            # nuevo centroide de k
            listk[i] = np.average(subgrupo,axis=0)
    #print(j)
    
    return (listk,init_k,j, assigned)<jupyter_output><empty_output><jupyter_text>Con los datos crudos<jupyter_code>ks,k_init,j,assigned = kmeans(base_data, epochs = 5,k = 5,status=True, seed=False)
print(ks)
print('J: ',j)<jupyter_output>2.428022583283438
1.5089217747476418
1.080303162383244
1.046552656723866
1.0416659277670361
[[ 1.75625    34.75      ]
 [ 1.68285714 28.28571429]
 [ 1.73666667 25.44444444]
 [ 1.74       22.        ]
 [ 1.78666667 24.        ]]
J:  [2.42802258 1.50892177 1.08030316 1.04655266 1.04166593]<jupyter_text>Grafica J<jupyter_code>plt.plot(j)<jupyter_output><empty_output><jupyter_text>Grafica de clustersSe prepresenta el universo de elementos agrupados por colores y por medio de X los centroides definidos<jupyter_code>base_data_or['k'] = assigned
sns.scatterplot(x="Edad", y="Estatura",  palette="Set1",s=75,
                     hue="k", data=base_data_or)
# definicion de k
sns.scatterplot(x=ks[:,1], y=ks[:,0],  marker="x",s=100)
# puntos iniciales
#sns.scatterplot(x=init_k[:,1], y=init_k[:,0],  marker="s",s=75,color=".2" )<jupyter_output><empty_output><jupyter_text>Escalado de datos<jupyter_code>esc_data = (base_data - np.min(base_data, axis=0))/ ( np.max(base_data, axis=0) - np.min(base_data, axis=0) )
esc_data[0:3]<jupyter_output><empty_output><jupyter_text>k=5<jupyter_code>ks5,k_init5,j5,assigned5 = kmeans(esc_data, epochs = 5,k = 5,status=True,seed=False)
print(ks5)
print('J: ',j5)<jupyter_output>0.2055910825943832
0.15304699796436652
0.14789249814173838
0.14159402125307602
0.13979030197941986
[[0.23888889 0.30701754]
 [0.81666667 0.81578947]
 [0.57777778 0.25438596]
 [0.57333333 0.61052632]
 [0.85       0.18421053]]
J:  [0.20559108 0.153047   0.1478925  0.14159402 0.1397903 ]<jupyter_text>Grafica J<jupyter_code>plt.plot(j5)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,
                     hue=assigned5)
# definicion de k
sns.scatterplot(x=ks5[:,1], y=ks5[:,0],  marker="x",s=100)
# puntos iniciales
#sns.scatterplot(x=init_k[:,1], y=init_k[:,0],  marker="s",s=75,color=".2" )<jupyter_output><empty_output><jupyter_text>En base diversas ejecuciones y por el medio elbow se estima que un buen k seria de 2, considerando que el algoritmo converve en menos de 5 epochs.Aunque en varias ocuaciones elbow sugiere hacer un solo grupo; pero seria lo mismo que no hacer nada por lo que se deja la opcion de crear almenos 2 grupos  k=2<jupyter_code>ks2,k_init2,j2,assigned2 = kmeans(esc_data, epochs = 5,k = 2,status=True,seed=False)
print('J: ',j2)
plt.plot(j2)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,
                     hue=assigned2)
# definicion de k
sns.scatterplot(x=ks2[:,1], y=ks2[:,0],  marker="x",s=100)<jupyter_output><empty_output><jupyter_text>k=3<jupyter_code>ks3,k_init3,j3,assigned3 = kmeans(esc_data, epochs = 5,k = 3,status=False,seed=False)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,hue=assigned3)
print('J: ',j3)<jupyter_output>J:  [0.21740756 0.17329056 0.16589268 0.16589268 0.16589268]<jupyter_text>k=6<jupyter_code>ks6,_,_,assigned6 = kmeans(esc_data, epochs = 5,k = 6,status=False,seed=False)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,hue=assigned6)<jupyter_output><empty_output><jupyter_text>k=4<jupyter_code>ks4,_,_,assigned4 = kmeans(esc_data, epochs = 5,k = 4,status=False,seed=False)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,hue=assigned4)<jupyter_output><empty_output><jupyter_text>GMM<jupyter_code>gmm2 = GaussianMixture(n_components=2).fit(esc_data)
k_gmm2 = gmm2.predict(esc_data)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0], hue=k_gmm2,  palette="Set1",s=75)<jupyter_output><empty_output><jupyter_text>k=5<jupyter_code>gmm5 = GaussianMixture(n_components=5).fit(esc_data)
k_gmm5 = gmm5.predict(esc_data)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0], hue=k_gmm5,  palette="Set1",s=75)<jupyter_output><empty_output><jupyter_text>k=3<jupyter_code>gmm3 = GaussianMixture(n_components=3).fit(esc_data)
k_gmm3 = gmm3.predict(esc_data)
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0], hue=k_gmm3,  palette="Set1",s=75)<jupyter_output><empty_output><jupyter_text>Conclusiones Para la realizacion de los clustes fue importante realizar el escalado de los datos ya que la mayoria de los valores de estatura no superan el valor de 2 y los de edad estan alrededor de 30.  Antes de esto los grupos se separaban de forma vertical; depues del escalado de los datos el patron cambio. Segun el k que se defina se logran diferentes resultados; para el caso de k=3, los grupos se pueden interpretar como:1. Los de edad mas joven con alturas altas, 2. Los de edad media con altura baja3. Los de edad mayor<jupyter_code>sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,hue=assigned3)<jupyter_output><empty_output><jupyter_text>Debido a la naturaleza de los datos los clusters no son tan claros debido a que sabemos que la edad no esta relacionada directamente con la edad, independientemente de la altura estos llegaran a una dada mayor.  Por esta misma razon los cluster convergen de forma diferente en distintas ejecuciones.  A la vez que los datos convergen en pocas iteraciones por esta misma razon y la cantidad de datos.  Con respecto al metodo del codo Debido a que los resultados no son consistentes en cada ejecucion la representacion de J varia para estos datos entre 1 y 3 indicando que k esta funciona para estos datos.La seleccion de k debe de ser 1, 2 o 3; pero no mas de eso.  En base a estos datos no se logra tener mas variaciones.  A pesar de que es posible calcular grupos para k>3 la cohesion de los grupos no es tanta como para que sean significativos.    Estimacion Datos<jupyter_code>pruebas = pd.read_csv('p1_pruebas.csv')
pruebas = pruebas.to_numpy()

# se escalan los datos dentro de la misma escala que los datos de entrenamiento
pruebas = (pruebas - np.min(base_data, axis=0))/ ( np.max(base_data, axis=0) - np.min(base_data, axis=0) )
pruebas[0:3]<jupyter_output><empty_output><jupyter_text>Predicciones de ***GMM*** para un modelos de k = 3<jupyter_code>gmm3.predict(pruebas)<jupyter_output><empty_output><jupyter_text>Preciccion de probabilidades de pertenecia a cada grupo segun *GMM*<jupyter_code>gmm3.predict_proba(pruebas)<jupyter_output><empty_output><jupyter_text>Prediccion de **k-means** manual de k=3<jupyter_code>k_predict(ks3, pruebas)<jupyter_output><empty_output><jupyter_text>Debido a que el proceso de inicializacion es aleatorio los grupos de cada uno no necesariamente corresponden a otra ejecucion.  En otras palabras a pesar que de se creen los mismo *clusters* no necesariamente seran en el mismo orden; es posible que k1 de una ejecucion sea diferente a otro k1.En este caso lo que vemos es que `gmm3.predict(pruebas)` genera `[0, 1, 2, 2, 2, 2, 2, 2]``k_predict(ks3, pruebas)` genera `[1, 1, 2, 2, 1, 0, 0, 2]`Se corrio el modelo a manera de crear en el mismo orden los clusters para tener un mejor parametro de comparacion.  Como se ve en la siguientes graficas, los clustres estan creados de forma diferente<jupyter_code># GMM con datos de prueba
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0], hue=k_gmm3,  palette="Set1",s=75)
sns.scatterplot(x=pruebas[:,1], y=pruebas[:,0],  marker="x",s=100)
# knn con datos de prueba
sns.scatterplot(x=esc_data[:,1], y=esc_data[:,0],  palette="Set1",s=75,hue=assigned3)
sns.scatterplot(x=pruebas[:,1], y=pruebas[:,0],  marker="x",s=100)<jupyter_output><empty_output><jupyter_text>Prediccion con k=5En este caso si se logran grupos diferentes, en la grafica anterior de GMM con k=5 se persive que los grupos no son uniformes<jupyter_code>print(gmm5.predict(pruebas))
print(k_predict(ks5, pruebas))
gmm5.predict_proba(pruebas)<jupyter_output><empty_output><jupyter_text>Parte 2  Deteccion de Anomalias<jupyter_code>from scipy.stats import multivariate_normal
from sklearn.metrics import f1_score
# para quitar la notacion cientifica
np.set_printoptions(suppress=True)
base_data = pd.read_csv('p1_estaturas.csv').to_numpy()
pruebas = pd.read_csv('p1_pruebas.csv').to_numpy()
anomalias = pd.read_csv('p1_anomalias.csv').to_numpy()
prue_y = np.zeros(len(pruebas))
anom_y = np.ones(len(anomalias))
data_anomalias = np.append(base_data,anomalias, axis=0)
data_pruebas = np.append(base_data,pruebas, axis=0)
#data_pruebas.shape
def estimateGaussian(dataset):
    mu = np.mean(dataset, axis=0)
    sigma = np.cov(dataset.T)
    return mu, sigma

def multivariateGaussian(dataset,mu,sigma):
    p = multivariate_normal(mean=mu, cov=sigma)
    return p.pdf(dataset)

def selectThresholdByCV(probs,gt):
    best_epsilon = 0
    best_f1 = 0
    f = 0
    stepsize = (max(probs) - min(probs)) / 2000
    #print(stepsize)
    epsilons = np.arange(min(probs),max(probs),stepsize)
    for epsilon in np.nditer(epsilons):
        #print(epsilon)
        predictions = (probs < epsilon) 
        #print(predictions)
        f = f1_score(gt, predictions,average='binary')
        #print(f,epsilon)
        #print('------')
        if f > best_f1:
            #print('----mejor')
            best_f1 = f
            best_epsilon = epsilon
    
    return best_f1, best_epsilon<jupyter_output><empty_output><jupyter_text>Pruebas con datos **normales + valtest**<jupyter_code>mu, sigma = estimateGaussian(data_pruebas)
p = multivariateGaussian(data_pruebas,mu,sigma)
print(mu, sigma)
#print(p)
p_cv = multivariateGaussian(pruebas,mu,sigma)
fscore, ep = selectThresholdByCV(p_cv,prue_y)
#print(fscore,ep)
# seleccion de anomalias
outliers = np.asarray(np.where(p < ep))
outliers<jupyter_output><empty_output><jupyter_text>Para este caso los datos no presentan anomalias<jupyter_code>plt.figure()
plt.plot(data_pruebas[:,1],data_pruebas[:,0],'bx')
plt.plot(data_pruebas[outliers,1],data_pruebas[outliers,0],'ro')
plt.show()<jupyter_output><empty_output><jupyter_text>Pruebas con datos **normales + anomalias**<jupyter_code>mu, sigma = estimateGaussian(data_anomalias)
p = multivariateGaussian(data_anomalias,mu,sigma)
print(mu, sigma)
#print(p)
p_cv = multivariateGaussian(anomalias,mu,sigma)
fscore, ep = selectThresholdByCV(p_cv,anom_y)
#print(fscore,ep)
print(ep)
# seleccion de anomalias
outliers = np.asarray(np.where(p < ep))
outliers
plt.figure()
plt.plot(data_anomalias[:,1],data_anomalias[:,0],'bx')
plt.plot(data_anomalias[outliers,1],data_anomalias[outliers,0],'ro')
plt.show()
En este caso se encuentran las anomalias con los datos
data_anomalias[outliers]<jupyter_output><empty_output>