# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# # Clustering  con HDBSCAN - Generación de energía por Región
# En la ingeniería eléctrica, específicamente el énfasis de sistemas de potencia, la generación de energía es una de las ramas principales de aplicación. En muchos artículos académicos e investigaciones se analiza la generación y consumo de energía en diferentes regiones y con multiples variables con el fin de optimizar este preciado recurso.
#
# ![Imagen](https://images.theconversation.com/files/324618/original/file-20200401-23100-rtsiwl.jpg?ixlib=rb-1.1.0&rect=7%2C0%2C4985%2C2133&q=45&auto=format&w=496&fit=clip)
#
# En este caso se tiene una base de datos con los resultados de diferentes mediciones de generación de energía por tipo y por región, a su vez que la cantidad de energía a producir estimada en unidades de Medición fasorial. 
#
# Las unidades de Medición fasorial como su nombre lo indica realizan una medida de los fasores de corriente y tensión de la red eléctrica, garantizando sincronización en las medidas y una alta tasa de muestreo que permite una visualización en tiempo real del sistema, siendo un factor clave para determinar flujos de potencia y en general el estado del sistema. El conjunto de dos o más PMU’s instaladas en el sistema y los software de análisis de datos provenientes de estas es llamado un WAMS de PMU’s. Todos los valores tomados, que se muestran en la base de datos estan en MU.
#
# ![Imagen](https://www.researchgate.net/profile/Abel-Gawel/publication/320747288/figure/fig5/AS:642803856056320@1530267790130/Clustering-results-using-HDBSCAN-algorithm-Points-with-the-same-color-belong-to-the-same.png)
# Se utilizara el método HDBSCAN para ordenar los datos en clusteres relacionados, con el fin de analizar características geográficas y técnicas de cada región.
# Este algorítmo realiza DBSCAN sobre diferentes valores de épsilon e integra el resultado para encontrar un agrupamiento que brinde la mejor estabilidad sobre épsilon. 
# Esto permite que HDBSCAN encuentre grupos de diferentes densidades (a diferencia de DBSCAN) y sea más robusto para la selección de parámetros.
#

# +
# Imports

import numpy as np
import matplotlib.pyplot as plt
import hdbscan
import seaborn as sns
import pandas as pd
import sklearn.cluster as cluster

# %matplotlib inline
# -

# ## Importando Dataset y visualizando sus características

data = pd.read_csv('../datasets/generación energía por tipo region.csv',engine='python')
data.info()

# ## Mostrar las primeras filas para una previsualización del orden de los datos

data.head()

# ## Se puede ver que la columna "indice" es el índice numérico de la muestra, pero que ya se ha enumerado con el método de lectura, por ello, se procede a eliminarla

data = data.drop(['indice'],axis=1)
data.head()


# ## Se obtienen las variables estadísticas de los datos por columna, para los valores numéricos.

data_std = data.describe()
data_std
# En este punto se puede optar por un análisis de estos elementos, que caracterizan los datos procesados,teniendo hasta N componentes
# a los cuales aplicar métodos de agrupamiento y visualizarlos. 
# En este caso, se hara un enfoque directo con los datos y no sus variables estadísticas.

# ## Estos datos son muy utiles para diferentes análisis de producción y estimación. Otra buena forma de tratar los datos es normalizandolos. 
#
# Nótese que este dataset mantiene datos numéricos y no-numéricos(texto) por lo que su procesamiento no se puede realizar de forma directa. En este caso existen varias formas de procesamiento y ordenamiento: Por fecha, por región o por valores numéricos. Una posibilidad es tratar estos datos texto como números o etiquetas numéricas, o simplemente omitiendolos del procesamiento numérico.
#
# Para esto, es posible crear un datase o un dataframe que contenga los valores a procesar, mientras se que guardan las etiquetas de texto en el vector original de datos o en otro lugar conocido para su procesamiento posterior.

# Se quitan las columnas fecha y región, que se dejarán como etiquetas posteriores.
data = data.drop(['fecha'],axis=1)
data = data.drop(['Region'],axis=1)
data.info()

# Ahora que se tienen todos los valores numéricos, se normalizan los datos en un rango adecuado y se vuelven a obtener sus métricas:
data_norm = (data - data.min())/(data.max()-data.min())
data_norm.describe()

# ## Se puede observar que ahora los datos tienen valores entre cero y uno, max y min.
# ## Ahora con este preprocesamiento se tienen datos ordenados, numéricos y normalizados, listos para un agrupamiento óptimo.
#

# +
# Configuración del modelo HDBSCAN (una forma es aplicar fit_predict para evitar setear algunos parámetros)
# un ejemplo de configuración manual es: hdbscan_model = hdbscan.HDBSCAN(algorithm='best', alpha=1.0,approx_min_span_tree=True,gen_min_span_tree=True,leaf_size=40,metric='euclidean',min_cluster_size=650, min_samples=3,p=None)

# Obtenemos el modelo
hdbscan_auto = hdbscan.HDBSCAN(min_cluster_size=700)    # Selección empírica de un único parámetro, que es intuitivo y fácil de probar.

# Aplicar el modelo a los datos normalizados
cluster_labels = hdbscan_auto.fit_predict(data_norm)

# Podemos llamar a visualizar a las etiquetas de los grupos creados y analizar el conteo de estas etiquetas y su asignación con una tabla cruzada
pd.crosstab(cluster_labels, columns='Cantidad Elementos')
# -

# Se puede observar que con esta configuración de parámetros, HDBSCAN encuentra varios clusters con diferentes cantidades de elementos. HDBSCAN no tiene dificultades reconociendo los datos atípicos o outliers del dataset, como se puede ver en la agrupacion -1. Esta es una de las principales fortalezas del método.

## Ahora se grafican estos resultados:
hdbscan_auto.condensed_tree_.plot()

# Este método crea un atributo label_ dentro del modelo clustering generado. Se agrega esta calificación al archivo original del Dataset:

data['HDBSCAN clusters'] = cluster_labels
data.head()

# ## Visualización de los clusters Generados
# Ahora se pueden colorear los clusters generados en la imagen.

hdbscan_auto.condensed_tree_.plot(select_clusters=True, selection_palette=sns.color_palette('deep',10))

# +
# El otro enfoque mencionado, es a partir de los datos estadísticos que mejor representen las relaciones del dataset, para ello se puede hacer uso del Análisis de Componentes Principales (PCA)
from sklearn.decomposition import PCA

# Dos componentes principales
pca = PCA(n_components=2)                                  

# Aplicar transformacion
pca_data = pca.fit_transform(data_norm)

# Obtener DataFrame ordenado
pca_df = pd.DataFrame(data= pca_data, columns=['Componente_1', 'Componente_2'])

# Aplicar el modelo clustering HDBSCAN y obtener etiquetas ordenadas
hdbscan_auto_PCA = hdbscan.HDBSCAN(min_cluster_size=60)

labels = pd.DataFrame(data= hdbscan_auto_PCA.fit_predict(pca_df), columns=['HDBSCANS_Labels'])

# Agregar etiquetas de agrupaciones y dataframe en un solo arreglo
pca_df  = pd.concat([pca_df, labels], axis=1)
# -

# ## Graficar el dataframe procesado
# Ahora se configura la figura plot a mostrar con estos datos obtenidos

fig = plt.figure(figsize= (7,7))
grafico = fig.add_subplot(1,1,1)
grafico.set_xlabel('Componente 1',fontsize = 12 )
grafico.set_ylabel('Componente 2',fontsize = 12 )
grafico.set_title('Componentes principales - Clustering HDBSCAN',fontsize = 20 )
Colores = np.array(["blue", "orange", "green","yellow","red","brown","purple","pink","grey"])
grafico.scatter(x=pca_df.Componente_1 , y=pca_df.Componente_2, c=Colores[pca_df.HDBSCANS_Labels], s=40)
plt.show()

# ## Guardar los datos generados
# Se procede a guardar el dataframe en formato csv. COmo se modifico el archido inicial, ahora se carga el original, se agregan las etiquetas generadas y se vuelve a guardar como csv ya con el clustering realizado.

csv = pd.read_csv('../datasets/generación energía por tipo region.csv',engine='python')
csv['HDBSCAN_clusters'] = hdbscan_auto.labels_
csv.to_csv('../Results/energy-HDBSCAN.csv')   # Se crea un archivo csv en la carpeta Results

# # Ejercicios/ Experimentos propuestos
#
# ## 1. Elija diferentes valores aleatorios para el número mínimo de clusters a implementar en el modelo HDBSCAN ¿Cómo cambia el resultado? ¿Qué se nota?
# ## 2. Investigue cuales son los parámetros configurables del método HDBSCAN (los mostrados en el comentario del codigo) y qué determinan cada uno de ellos.
# ## 3. Implemente el modelo HDBSCAN con estos parámetros seteados a su parecer. ¿Qué cambia?
# ## 4. Explique las ventajas y desventajas que tiene el algorítmo HDBSCAN. Puede investigar diferentes fuentes.
# ## 5. Aumente el número de PCA a 3 componentes y grafiquelo en 3 Dimensiones. ¿Qué es lo que cambio y que se esta añadiendo?

StarcoderdataJupyterScriptsDedupFiltered