REQ01: Identificar una fuente pública

Para la elaboración de este proyecto se seleccionó la base de datos “Diamonds Characteristics and Pricing Analysis”, una fuente pública ampliamente utilizada en estudios estadísticos, modelado predictivo y análisis exploratorio.

REQ02: Planteamiento de preguntas

Pregunta 1:

¿Cómo se agrupan los diamantes según cut, color y clarity, y cómo difieren estos grupos en el precio promedio? Variables: cut, color, clarity, price Objetivo: Identificar clústeres basados en calidad y evaluar cómo varía el precio entre ellos.

Pregunta 2:

¿Cuál es la relación entre carat, price y cut, y cómo interactúan para influir en el valor del diamante? Variables: carat, price, cut Objetivo: Analizar cómo el peso y la calidad del corte afectan conjuntamente el precio.

Pregunta 3:

¿Cómo influyen depth y table en el price y carat? Variables: depth, table, price, carat Objetivo: Evaluar si las proporciones físicas del diamante afectan su valor y su relación con el peso.

Librerias usadas

import pandas as pd # Manejo del dataframe
import matplotlib.pyplot as plt # Para plotear
import seaborn as sns # Para plotear
import plotly.express as px # Para gráficos interactivos
from umap import UMAP # Paquete para usar el UMAP
from sklearn.cluster import KMeans # Para usar KMeans
import numpy as np # Habilita el Python Numérico para análisis de datos. análisis de datos.

Funciones

#Funciones
def radar_plot(centros, labels):
    from math import pi
    centros = np.array([
        ((n - min(n)) / (max(n) - min(n)) * 100) if max(n) != min(n) else (n/n * 50)
        for n in centros.T
    ])
    angulos = [n / float(len(labels)) * 2 * pi for n in range(len(labels))]
    angulos += angulos[:1]

    ax = plt.subplot(111, polar=True)
    ax.set_theta_offset(pi / 2)
    ax.set_theta_direction(-1)

    plt.xticks(angulos[:-1], labels)
    ax.set_rlabel_position(0)
    plt.yticks(
        [10,20,30,40,50,60,70,80,90,100],
        ["10%","20%","30%","40%","50%","60%","70%","80%","90%","100%"],
        color="grey",
        size=8
    )
    plt.ylim(-10,100)

    for i in range(centros.shape[1]):
        valores = centros[:, i].tolist()
        valores += valores[:1]
        ax.plot(angulos, valores, linewidth=1, linestyle="solid", label=f"Cluster {i}")
        ax.fill(angulos, valores, alpha=0.3)

    plt.legend(loc="upper right", bbox_to_anchor=(0.1, 0.1))


def centroide(num_cluster, datos, clusters):
    ind = clusters == num_cluster
    return pd.DataFrame(datos[ind].mean()).T


def calcular_centros(cant_clusters, datos, clusters):
    centro = centroide(0, datos, clusters)
    for j in range(1, cant_clusters):
        centro = pd.concat([centro, centroide(j, datos, clusters)])
    return np.array(centro)


def scatter_2D(df, grupos, label="cluster", title=""):
    fig = px.scatter(
        df,
        x="dim1",
        y="dim2",
        color=grupos.astype(str),
        hover_data={"Individuo": df.index.values},
        labels={"color": label}
    )
    fig.update_layout(title_text=title, title_x=0.5)
    return fig.show()

REQ03: Preparación de los datos: clasificación de las variables cuantitativas.

Se inserta el data set

datos = pd.read_csv(r"C:\UIA\Data\diamonds.csv", delimiter=',', decimal=".", index_col=0)

datos_dum = pd.get_dummies(datos, drop_first=True)

datos_dum.shape
## (53940, 20)
print(datos_dum.columns.tolist())
## ['depth', 'table', 'price', 'cut_Good', 'cut_Ideal', 'cut_Premium', 'cut_Very Good', 'color_E', 'color_F', 'color_G', 'color_H', 'color_I', 'color_J', 'clarity_IF', 'clarity_SI1', 'clarity_SI2', 'clarity_VS1', 'clarity_VS2', 'clarity_VVS1', 'clarity_VVS2']
datos.describe()
##               depth         table         price
## count  53940.000000  53940.000000  53940.000000
## mean      61.749405     57.457184   3932.799722
## std        1.432621      2.234491   3989.439738
## min       43.000000     43.000000    326.000000
## 25%       61.000000     56.000000    950.000000
## 50%       61.800000     57.000000   2401.000000
## 75%       62.500000     59.000000   5324.250000
## max       79.000000     95.000000  18823.000000

Tablas de variables ordenadas

REQ04: Generación de al menos 3 clúster utilizando el modelado no supervisado, algoritmo UMAP.

vars_deseadas = [
"carat", "depth", "table", "price",
"cut_Good", "cut_Ideal", "cut_Premium", "cut_Very Good",
"color_E", "color_F", "color_G", "color_H",
"clarity_SI1", "clarity_VS1", "clarity_VVS1"
]

vars_cor = [c for c in vars_deseadas if c in datos_dum.columns]

sub = datos_dum[vars_cor]

corr = sub.corr()

import matplotlib.pyplot as plt
import seaborn as sns

fig, ax = plt.subplots(figsize=(10, 8), dpi=150)
paleta = sns.diverging_palette(220, 10, as_cmap=True).reversed()

sns.heatmap(
corr,
vmin=-1, vmax=1, cmap=paleta,
square=True, annot=True, fmt=".2f",
annot_kws={"size": 7},
cbar=True, ax=ax
)

plt.xticks(rotation=45, ha="right")
## (array([ 0.5,  1.5,  2.5,  3.5,  4.5,  5.5,  6.5,  7.5,  8.5,  9.5, 10.5,
##        11.5, 12.5, 13.5]), [Text(0.5, 0, 'depth'), Text(1.5, 0, 'table'), Text(2.5, 0, 'price'), Text(3.5, 0, 'cut_Good'), Text(4.5, 0, 'cut_Ideal'), Text(5.5, 0, 'cut_Premium'), Text(6.5, 0, 'cut_Very Good'), Text(7.5, 0, 'color_E'), Text(8.5, 0, 'color_F'), Text(9.5, 0, 'color_G'), Text(10.5, 0, 'color_H'), Text(11.5, 0, 'clarity_SI1'), Text(12.5, 0, 'clarity_VS1'), Text(13.5, 0, 'clarity_VVS1')])
plt.yticks(rotation=0)
## (array([ 0.5,  1.5,  2.5,  3.5,  4.5,  5.5,  6.5,  7.5,  8.5,  9.5, 10.5,
##        11.5, 12.5, 13.5]), [Text(0, 0.5, 'depth'), Text(0, 1.5, 'table'), Text(0, 2.5, 'price'), Text(0, 3.5, 'cut_Good'), Text(0, 4.5, 'cut_Ideal'), Text(0, 5.5, 'cut_Premium'), Text(0, 6.5, 'cut_Very Good'), Text(0, 7.5, 'color_E'), Text(0, 8.5, 'color_F'), Text(0, 9.5, 'color_G'), Text(0, 10.5, 'color_H'), Text(0, 11.5, 'clarity_SI1'), Text(0, 12.5, 'clarity_VS1'), Text(0, 13.5, 'clarity_VVS1')])
plt.tight_layout()
plt.show()

Mapa de calor, visualisacion de datos.

Se generó una matriz de correlación para identificar relaciones entre variables. Este análisis permite observar qué atributos tienen mayor influencia entre sí, y sirve como insumo para la interpretación de los clústeres generados por UMAP + K-Means.

UMAP

Se cargó la base original de diamonds y se transformaron todas las variables categóricas en variables dummy mediante get_dummies(). El dataset resultante contiene 53,940 registros y 20 variables numéricas, lo cual es necesario para aplicar UMAP y K-Means, ya que ambos algoritmos requieren datos completamente numéricos.

umap_data = UMAP(n_components=2,n_neighbors=10,min_dist=0.1,random_state=42).fit_transform(datos_dum)
## C:\Users\sebas\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\umap\umap_.py:1952: UserWarning:
## 
## n_jobs value 1 overridden to 1 by setting random_state. Use no seed for parallelism.
## 
## C:\Users\sebas\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\sklearn\manifold\_spectral_embedding.py:328: UserWarning:
## 
## Graph is not fully connected, spectral embedding may not work as expected.

Kmeans integrado con 4 clusters

Se configuró K-Means con 4 clústeres, max_iter=2000, n_init=150 y random state 42 para garantizar estabilidad en la asignación de grupos. Esta configuración mejora la calidad de los clústeres al reducir la variabilidad entre ejecuciones.

df_umap = pd.DataFrame(umap_data, columns = ["dim1","dim2"], index = datos.index.values)
kmedias = KMeans(n_clusters=4,max_iter=2000,n_init=150,random_state=42)

kmedias.fit(df_umap)
KMeans(max_iter=2000, n_clusters=4, n_init=150, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

Resumen del análisis de clusters

Se aplicó UMAP para reducir las 20 variables numéricas a dos dimensiones (dim1 y dim2), permitiendo visualizar patrones y evidenciando tres zonas bien diferenciadas en los datos. Con estas proyecciones se ejecutó K-Means con k = 4, utilizando 150 inicializaciones y un máximo de 2000 iteraciones y random state 42 para asegurar una agrupación estable.

Mapa

grupos = kmedias.predict(df_umap)
df_umap["cluster"] = grupos
df_umap
##            dim1      dim2  cluster
## 0.23  16.626076 -8.711778        1
## 0.21  16.614927 -8.702290        1
## 0.23  16.625465 -8.710221        1
## 0.29  16.614407 -8.700451        1
## 0.31  16.615726 -8.699868        1
## ...         ...       ...      ...
## 0.72   1.383233 -2.598651        2
## 0.72   1.412605 -2.589294        2
## 0.70   1.435360 -2.655919        2
## 0.86   1.384377 -2.606956        2
## 0.75   1.377521 -2.563674        2
## 
## [53940 rows x 3 columns]

Grafico radar

centros = calcular_centros(4, datos_dum, np.array(df_umap["cluster"]))

radar_plot(centros, datos_dum.columns)
plt.show()

Conclusión

Clúster 0 – Diamantes grandes pero de calidad baja/media

Clúster 1 – Diamantes equilibrados (calidad media, precio moderado)

Clúster 2 – Diamantes premium (alta claridad, buen color y cortes superiores)

Clúster 3 – Diamantes caros por tamaño y proporciones

REQ05: hacer una clusterización utilizando un modelo no supervidado con el algoritmo K-Means.

K-MEANS

Resultados del K-MEANS

Tableview