CLUSTER JERARQUICO Y KMEANS EJEMPLO 1 en python

# Análisis de clustering jerárquico en Python

import pandas as pd

# Configurar opciones de visualización
pd.set_option('display.max_rows', None)         # Mostrar todas las filas
pd.set_option('display.max_columns', None)      # Mostrar todas las columnas
pd.set_option('display.width', None)            # Ajustar el ancho automáticamente
pd.set_option('display.max_colwidth', None)     # Mostrar el contenido completo de cada celda

# Ejemplo con tu base de datos (o cualquier DataFrame)

import pandas as pd
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.preprocessing import StandardScaler

# Lectura del archivo
df = pd.read_csv('Mall_Customers.csv', encoding='ascii')

# Mostrar las primeras filas para confirmar la lectura
print('Primeras filas del dataframe:')
print(df.head())
Primeras filas del dataframe:
   CustomerID   Genre  Age  Annual Income (k$)  Spending Score (1-100)
0           1    Male   19                  15                      39
1           2    Male   21                  15                      81
2           3  Female   20                  16                       6
3           4  Female   23                  16                      77
4           5  Female   31                  17                      40
# Ahora realizaremos el análisis de clustering jerárquico con tres variables: Age, Annual Income y Spending Score
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.spatial.distance import pdist, squareform


# Cargar el archivo
df = pd.read_csv('Mall_Customers.csv')

# Extraer las variables para el análisis
features_ext = df[['Age', 'Annual Income (k$)', 'Spending Score (1-100)']]

# Estandarizar las variables
scaler = StandardScaler()
features_ext_scaled = scaler.fit_transform(features_ext)

print(features_ext.head())
   Age  Annual Income (k$)  Spending Score (1-100)
0   19                  15                      39
1   21                  15                      81
2   20                  16                       6
3   23                  16                      77
4   31                  17                      40
# Calcular la matriz de distancias euclídeas
dist_matrix = squareform(pdist(features_ext_scaled , metric='euclidean'))

# Mostrar la forma de la matriz
print('Forma de la matriz de distancias:', dist_matrix.shape)

# Visualizar la matriz de distancias utilizando un heatmap (solo una parte para evitar visualizaciones muy densas)
plt.figure(figsize=(10, 8))
sns.heatmap(dist_matrix[:50, :50], cmap='viridis')
plt.title('Heatmap de la Matriz de Distancias Euclídeas (Primeros 50 elementos)')
plt.xlabel('Índice de muestra')
plt.ylabel('Índice de muestra')
plt.tight_layout()
plt.show()

print('Matriz de distancias calculada y visualizada (primeros 50 elementos).')

print('done')
Forma de la matriz de distancias: (200, 200)

Matriz de distancias calculada y visualizada (primeros 50 elementos).
done
# Crear un dataframe con las distancias euclídeas entre algunos clientes
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform
from sklearn.preprocessing import StandardScaler

# Cargar los datos
df = pd.read_csv('Mall_Customers.csv')

# Extraer las variables para el análisis
features = df[['Annual Income (k$)', 'Spending Score (1-100)']]

# Estandarizar las variables
scaler = StandardScaler()
features_scaled = scaler.fit_transform(features)

# Calcular la matriz de distancias euclídeas
dist_matrix = squareform(pdist(features_scaled, metric='euclidean'))

# Crear un dataframe con las distancias
# Seleccionamos los primeros 10 clientes para una visualización más clara
n_samples = 10
dist_df = pd.DataFrame(dist_matrix[:n_samples, :n_samples])

# Añadir etiquetas para identificar a los clientes
customer_labels = [f"Cliente {i+1}" for i in range(n_samples)]
dist_df.index = customer_labels
dist_df.columns = customer_labels

# Mostrar el dataframe de distancias
print("Matriz de distancias euclídeas entre los primeros 10 clientes:")
print(dist_df)

# Mostrar también los datos originales de estos clientes para contexto
print("\
Datos originales de los primeros 10 clientes:")
customer_data = df[['CustomerID', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)']].head(n_samples)
print(customer_data)

# Visualizar la matriz de distancias como un heatmap
import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(10, 8))
sns.heatmap(dist_df, annot=True, cmap='viridis', fmt='.2f')
plt.title('Matriz de Distancias Euclídeas entre los primeros 10 clientes')
plt.tight_layout()
plt.show()

# Calcular algunas estadísticas de las distancias
print("\
Estadísticas de las distancias en toda la matriz:")
all_distances = dist_matrix[np.triu_indices(dist_matrix.shape[0], k=1)]  # Solo el triángulo superior sin la diagonal
print(f"Distancia mínima: {all_distances.min():.4f}")
print(f"Distancia máxima: {all_distances.max():.4f}")
print(f"Distancia media: {all_distances.mean():.4f}")
print(f"Desviación estándar: {all_distances.std():.4f}")

# Identificar los pares de clientes con la distancia mínima y máxima
min_dist_idx = np.unravel_index(np.argmin(dist_matrix + np.eye(dist_matrix.shape[0]) * np.inf), dist_matrix.shape)
max_dist_idx = np.unravel_index(np.argmax(dist_matrix), dist_matrix.shape)

print("\
Par de clientes con la distancia mínima:")
print(f"Cliente {min_dist_idx[0]+1} y Cliente {min_dist_idx[1]+1}: {dist_matrix[min_dist_idx]:.4f}")
print(df.iloc[[min_dist_idx[0], min_dist_idx[1]]][['CustomerID', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)']])

print("\
Par de clientes con la distancia máxima:")
print(f"Cliente {max_dist_idx[0]+1} y Cliente {max_dist_idx[1]+1}: {dist_matrix[max_dist_idx]:.4f}")
print(df.iloc[[max_dist_idx[0], max_dist_idx[1]]][['CustomerID', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)']])
Matriz de distancias euclídeas entre los primeros 10 clientes:
            Cliente 1  Cliente 2  Cliente 3  Cliente 4  Cliente 5  Cliente 6  \
Cliente 1    0.000000   1.630506   1.281680   1.475713   0.085643   1.438425   
Cliente 2    1.630506   0.000000   2.911867   0.159908   1.593514   0.208580   
Cliente 3    1.281680   2.911867   0.000000   2.756331   1.320485   2.717777   
Cliente 4    1.475713   0.159908   2.756331   0.000000   1.436905   0.054443   
Cliente 5    0.085643   1.593514   1.320485   1.436905   0.000000   1.397576   
Cliente 6    1.438425   0.208580   2.717777   0.054443   1.397576   0.000000   
Cliente 7    1.286219   2.913868   0.076339   2.757388   1.320485   2.717777   
Cliente 8    2.138254   0.517508   3.417150   0.664367   2.096712   0.699830   
Cliente 9    1.405891   3.031928   0.163328   2.875077   1.438425   2.835002   
Cliente 10   1.290177   0.381296   2.564780   0.225366   1.244633   0.173036   

            Cliente 7  Cliente 8  Cliente 9  Cliente 10  
Cliente 1    1.286219   2.138254   1.405891    1.290177  
Cliente 2    2.913868   0.517508   3.031928    0.381296  
Cliente 3    0.076339   3.417150   0.163328    2.564780  
Cliente 4    2.757388   0.664367   2.875077    0.225366  
Cliente 5    1.320485   2.096712   1.438425    1.244633  
Cliente 6    2.717777   0.699830   2.835002    0.173036  
Cliente 7    0.000000   3.416297   0.122560    2.562507  
Cliente 8    3.416297   0.000000   3.532968    0.854927  
Cliente 9    0.122560   3.532968   0.000000    2.678688  
Cliente 10   2.562507   0.854927   2.678688    0.000000  
Datos originales de los primeros 10 clientes:
   CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
0           1   19                  15                      39
1           2   21                  15                      81
2           3   20                  16                       6
3           4   23                  16                      77
4           5   31                  17                      40
5           6   22                  17                      76
6           7   35                  18                       6
7           8   23                  18                      94
8           9   64                  19                       3
9          10   30                  19                      72

Estadísticas de las distancias en toda la matriz:
Distancia mínima: 0.0000
Distancia máxima: 5.5015
Distancia media: 1.7554
Desviación estándar: 0.9688
Par de clientes con la distancia mínima:
Cliente 1 y Cliente 2: 1.6305
   CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
0           1   19                  15                      39
1           2   21                  15                      81
Par de clientes con la distancia máxima:
Cliente 3 y Cliente 200: 5.5015
     CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
2             3   20                  16                       6
199         200   30                 137                      83
C:\Users\MINEDUCYT\AppData\Local\Temp\ipykernel_10384\586378680.py:60: RuntimeWarning: invalid value encountered in multiply
  min_dist_idx = np.unravel_index(np.argmin(dist_matrix + np.eye(dist_matrix.shape[0]) * np.inf), dist_matrix.shape)
# Aplicar clustering jerárquico usando linkage
linked_ext = linkage(features_ext_scaled, method='ward')

# Graficar el dendrograma
plt.figure(figsize=(10, 7))
plt.title('Dendrograma - Clustering jerárquico (Variables extendidas)')
dendrogram(linked_ext,
           orientation='top',
           distance_sort='descending',
           show_leaf_counts=True)
plt.xlabel('Índices de muestra')
plt.ylabel('Distancia')
plt.tight_layout()
plt.show()

# Determinar número óptimo de clusters
plt.figure(figsize=(10, 6))
last = linked_ext[-10:, 2]
last_rev = last[::-1]
idxs = np.arange(1, len(last) + 1)
plt.plot(idxs, last_rev)
plt.title('Método del codo para determinar número óptimo de clusters (Variables extendidas)')
plt.xlabel('Número de clusters')
plt.ylabel('Distancia de enlace')
plt.tight_layout()
plt.show()

# Basado en el método del codo, elegimos 5 clusters
num_clusters = 5
clusters_ext = fcluster(linked_ext, num_clusters, criterion='maxclust')
# Agregar los clusters al dataframe original
df['Cluster_ext'] = clusters_ext

# Visualizar los clusters en 3D
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
scatter = ax.scatter(df['Age'], df['Annual Income (k$)'], df['Spending Score (1-100)'],
                    c=df['Cluster_ext'], cmap='viridis', s=50, alpha=0.8)
plt.colorbar(scatter, label='Cluster')
ax.set_title(f'Clustering Jerárquico 3D con {num_clusters} clusters')
ax.set_xlabel('Age')
ax.set_ylabel('Annual Income (k$)')
ax.set_zlabel('Spending Score (1-100)')
plt.tight_layout()
plt.show()

print(df.head(20))
    CustomerID   Genre  Age  Annual Income (k$)  Spending Score (1-100)  \
0            1    Male   19                  15                      39   
1            2    Male   21                  15                      81   
2            3  Female   20                  16                       6   
3            4  Female   23                  16                      77   
4            5  Female   31                  17                      40   
5            6  Female   22                  17                      76   
6            7  Female   35                  18                       6   
7            8  Female   23                  18                      94   
8            9    Male   64                  19                       3   
9           10  Female   30                  19                      72   
10          11    Male   67                  19                      14   
11          12  Female   35                  19                      99   
12          13  Female   58                  20                      15   
13          14  Female   24                  20                      77   
14          15    Male   37                  20                      13   
15          16    Male   22                  20                      79   
16          17  Female   35                  21                      35   
17          18    Male   20                  21                      66   
18          19    Male   52                  23                      29   
19          20  Female   35                  23                      98   

    Cluster_ext  
0             2  
1             5  
2             2  
3             5  
4             2  
5             5  
6             2  
7             5  
8             2  
9             5  
10            2  
11            5  
12            2  
13            5  
14            2  
15            5  
16            2  
17            5  
18            2  
19            5  
# Estadísticas por cluster
cluster_ext_stats = df.groupby('Cluster_ext')[['Age', 'Annual Income (k$)', 'Spending Score (1-100)']].agg(['mean', 'std', 'min', 'max'])
print("Estadísticas por cluster (Variables extendidas):")
print(cluster_ext_stats)
Estadísticas por cluster (Variables extendidas):
                   Age                    Annual Income (k$)                 \
                  mean        std min max               mean        std min   
Cluster_ext                                                                   
1            43.892857   8.337063  32  59          91.285714  16.915601  71   
2            44.318182  12.800247  19  67          25.772727   7.646548  15   
3            56.400000   8.373117  43  70          55.288889   9.834129  38   
4            32.692308   3.728650  27  40          86.538462  16.312485  69   
5            26.560606   7.043008  18  45          47.363636  18.328319  15   

                 Spending Score (1-100)                     
             max                   mean        std min max  
Cluster_ext                                                 
1            137              16.678571   8.973432   1  39  
2             39              20.272727  12.947446   3  40  
3             79              48.355556   6.869417  35  60  
4            137              82.128205   9.364489  63  97  
5             81              56.787879  20.628222   5  99  
# Mostrar las primeras filas con la asignación de clusters
print("Asignación de clusters (Variables extendidas):")
print(df[['Age', 'Annual Income (k$)', 'Spending Score (1-100)', 'Cluster_ext']].head())

print("Análisis de clustering jerárquico con variables extendidas completado.")
Asignación de clusters (Variables extendidas):
   Age  Annual Income (k$)  Spending Score (1-100)  Cluster_ext
0   19                  15                      39            2
1   21                  15                      81            5
2   20                  16                       6            2
3   23                  16                      77            5
4   31                  17                      40            2
Análisis de clustering jerárquico con variables extendidas completado.
# Cargar los datos
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import seaborn as sns

# Cargar los datos
df = pd.read_csv('Mall_Customers.csv')

# Extraer las variables para el análisis
features = df[['Annual Income (k$)', 'Spending Score (1-100)']]

# Estandarizar las variables
scaler = StandardScaler()
features_scaled = scaler.fit_transform(features)

# Aplicar K-means con 5 clusters
kmeans = KMeans(n_clusters=5, random_state=42)
df['Cluster'] = kmeans.fit_predict(features_scaled)

# Contar cuántos individuos hay en cada cluster
cluster_counts = df['Cluster'].value_counts().sort_index()
print("Número de individuos por cluster:")
print(cluster_counts)

# Crear una tabla con información detallada de cada cluster
cluster_info = pd.DataFrame()

# Para cada cluster, mostrar estadísticas
for cluster in sorted(df['Cluster'].unique()):
    cluster_data = df[df['Cluster'] == cluster]
    
    # Calcular estadísticas
    stats = {
        'Cluster': cluster,
        'Número de clientes': len(cluster_data),
        'Edad promedio': round(cluster_data['Age'].mean(), 2),
        'Ingreso anual promedio (k$)': round(cluster_data['Annual Income (k$)'].mean(), 2),
        'Puntuación de gasto promedio': round(cluster_data['Spending Score (1-100)'].mean(), 2),
        'Edad mínima': cluster_data['Age'].min(),
        'Edad máxima': cluster_data['Age'].max(),
        'Ingreso mínimo (k$)': cluster_data['Annual Income (k$)'].min(),
        'Ingreso máximo (k$)': cluster_data['Annual Income (k$)'].max(),
        'Gasto mínimo': cluster_data['Spending Score (1-100)'].min(),
        'Gasto máximo': cluster_data['Spending Score (1-100)'].max()
    }
    
    # Añadir a la tabla de información
    cluster_info = pd.concat([cluster_info, pd.DataFrame([stats])], ignore_index=True)

# Mostrar la tabla de información de clusters
print("\
Información detallada de cada cluster:")
print(cluster_info[['Cluster', 'Número de clientes', 'Edad promedio', 'Ingreso anual promedio (k$)', 'Puntuación de gasto promedio']])
print("\
Rangos de valores por cluster:")
print(cluster_info[['Cluster', 'Edad mínima', 'Edad máxima', 'Ingreso mínimo (k$)', 'Ingreso máximo (k$)', 'Gasto mínimo', 'Gasto máximo']])

# Visualizar los clusters
plt.figure(figsize=(10, 8))
scatter = plt.scatter(df['Annual Income (k$)'], df['Spending Score (1-100)'], 
                     c=df['Cluster'], cmap='viridis', s=100, alpha=0.7)
plt.xlabel('Ingreso Anual (k$)')
plt.ylabel('Puntuación de Gasto (1-100)')
plt.title('Segmentación de Clientes con K-means')
plt.colorbar(scatter, label='Cluster')
plt.grid(True, linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()

# Mostrar algunos ejemplos de clientes de cada cluster
print("\
Ejemplos de clientes por cluster:")
for cluster in sorted(df['Cluster'].unique()):
    print(f"\
Cluster {cluster}:")
    sample_customers = df[df['Cluster'] == cluster].sample(min(5, len(df[df['Cluster'] == cluster])), random_state=42)
    print(sample_customers[['CustomerID', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)']])
Número de individuos por cluster:
Cluster
0    81
1    39
2    22
3    35
4    23
Name: count, dtype: int64
Información detallada de cada cluster:
   Cluster  Número de clientes  Edad promedio  Ingreso anual promedio (k$)  \
0        0                  81          42.72                        55.30   
1        1                  39          32.69                        86.54   
2        2                  22          25.27                        25.73   
3        3                  35          41.11                        88.20   
4        4                  23          45.22                        26.30   

   Puntuación de gasto promedio  
0                         49.52  
1                         82.13  
2                         79.36  
3                         17.11  
4                         20.91  
Rangos de valores por cluster:
   Cluster  Edad mínima  Edad máxima  Ingreso mínimo (k$)  \
0        0           18           70                   39   
1        1           27           40                   69   
2        2           18           35                   15   
3        3           19           59                   70   
4        4           19           67                   15   

   Ingreso máximo (k$)  Gasto mínimo  Gasto máximo  
0                   76            34            61  
1                  137            63            97  
2                   39            61            99  
3                  137             1            39  
4                   39             3            40  

Ejemplos de clientes por cluster:
Cluster 0:
    CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
75          76   26                  54                      54
43          44   31                  39                      61
67          68   68                  48                      48
76          77   45                  54                      53
63          64   54                  47                      59
Cluster 1:
     CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
189         190   36                 103                      85
195         196   35                 120                      79
131         132   39                  71                      75
149         150   34                  78                      90
183         184   29                  98                      88
Cluster 2:
    CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
1            2   21                  15                      81
27          28   35                  28                      61
17          18   20                  21                      66
3            4   23                  16                      77
31          32   21                  30                      73
Cluster 3:
     CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
182         183   46                  98                      15
156         157   37                  78                       1
178         179   59                  93                      14
172         173   36                  87                      10
160         161   56                  79                      35
Cluster 4:
    CustomerID  Age  Annual Income (k$)  Spending Score (1-100)
30          31   60                  30                       4
18          19   52                  23                      29
0            1   19                  15                      39
16          17   35                  21                      35
34          35   49                  33                      14
# Supongamos que el nuevo individuo tiene:  
nuevo_individuo = [[50, 60]]  
  
# Estandarizamos usando el mismo objeto scaler entrenado anteriormente:  
nuevo_individuo_escalado = scaler.transform(nuevo_individuo)  
  
# Prediciendo el cl\u00faster  
cluster_nuevo = kmeans.predict(nuevo_individuo_escalado)  
print("El nuevo individuo pertenece al cl\u00faster:", cluster_nuevo[0])  
El nuevo individuo pertenece al clúster: 0
C:\Users\MINEDUCYT\AppData\Local\Programs\Python\Python312\Lib\site-packages\sklearn\utils\validation.py:2739: UserWarning: X does not have valid feature names, but StandardScaler was fitted with feature names
  warnings.warn(
# Cargar los datos y preparar el modelo
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import seaborn as sns

# Cargar los datos
df = pd.read_csv('Mall_Customers.csv')

# Extraer las variables para el análisis
features = df[['Annual Income (k$)', 'Spending Score (1-100)']]

# Estandarizar las variables
scaler = StandardScaler()
features_scaled = scaler.fit_transform(features)

# Aplicar K-means con 5 clusters
kmeans = KMeans(n_clusters=5, random_state=42)
kmeans.fit(features_scaled)
df['Cluster'] = kmeans.labels_

# Función para predecir el cluster de nuevos individuos
def predecir_cluster(nuevos_individuos, scaler, modelo):
    """
    Predice el cluster al que pertenecen nuevos individuos.
    
    Parámetros:
    nuevos_individuos -- DataFrame con las columnas 'Annual Income (k$)' y 'Spending Score (1-100)'
    scaler -- objeto StandardScaler ya entrenado
    modelo -- modelo KMeans ya entrenado
    
    Retorna:
    DataFrame con los datos originales y la columna 'Cluster_Predicho'
    """
    # Extraer características
    features_nuevos = nuevos_individuos[['Annual Income (k$)', 'Spending Score (1-100)']]
    
    # Estandarizar
    features_nuevos_scaled = scaler.transform(features_nuevos)
    
    # Predecir
    clusters_predichos = modelo.predict(features_nuevos_scaled)
    
    # Añadir predicciones al DataFrame
    nuevos_individuos_con_cluster = nuevos_individuos.copy()
    nuevos_individuos_con_cluster['Cluster_Predicho'] = clusters_predichos
    
    return nuevos_individuos_con_cluster

# Ejemplo: Crear un conjunto de nuevos individuos
nuevos_individuos = pd.DataFrame({
    'ID': range(1, 11),
    'Age': [25, 30, 45, 22, 50, 35, 28, 40, 55, 33],
    'Annual Income (k$)': [30, 70, 50, 20, 90, 40, 25, 85, 35, 75],
    'Spending Score (1-100)': [70, 80, 50, 90, 20, 60, 75, 30, 10, 85]
})

# Predecir clusters para los nuevos individuos
nuevos_con_cluster = predecir_cluster(nuevos_individuos, scaler, kmeans)

# Mostrar resultados
print("Nuevos individuos con sus clusters predichos:")
print(nuevos_con_cluster)

# Visualizar los nuevos individuos en el espacio de características junto con los clusters existentes
plt.figure(figsize=(12, 8))

# Graficar los datos originales
scatter_orig = plt.scatter(df['Annual Income (k$)'], df['Spending Score (1-100)'], 
                          c=df['Cluster'], cmap='viridis', s=80, alpha=0.5, label='Datos originales')

# Graficar los nuevos individuos
scatter_new = plt.scatter(nuevos_con_cluster['Annual Income (k$)'], nuevos_con_cluster['Spending Score (1-100)'],
                         c=nuevos_con_cluster['Cluster_Predicho'], cmap='viridis', 
                         s=150, alpha=1.0, edgecolors='black', linewidth=1.5, marker='*', label='Nuevos individuos')

# Añadir etiquetas de ID a los nuevos individuos
for i, txt in enumerate(nuevos_con_cluster['ID']):
    plt.annotate(txt, 
                (nuevos_con_cluster['Annual Income (k$)'].iloc[i], 
                 nuevos_con_cluster['Spending Score (1-100)'].iloc[i]),
                xytext=(5, 5), textcoords='offset points')

plt.xlabel('Ingreso Anual (k$)')
plt.ylabel('Puntuación de Gasto (1-100)')
plt.title('Predicción de Clusters para Nuevos Individuos')
plt.colorbar(scatter_orig, label='Cluster')
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
plt.tight_layout()
plt.show()

# Crear una función para predecir clusters de forma interactiva
def predecir_cluster_interactivo(ingresos, gastos, scaler, modelo):
    """
    Predice el cluster para un conjunto de ingresos y gastos.
    
    Parámetros:
    ingresos -- lista de ingresos anuales en k$
    gastos -- lista de puntuaciones de gasto (1-100)
    scaler -- objeto StandardScaler ya entrenado
    modelo -- modelo KMeans ya entrenado
    
    Retorna:
    DataFrame con los datos y sus clusters predichos
    """
    # Crear DataFrame
    datos = pd.DataFrame({
        'ID': range(1, len(ingresos) + 1),
        'Annual Income (k$)': ingresos,
        'Spending Score (1-100)': gastos
    })
    
    # Extraer características
    features = datos[['Annual Income (k$)', 'Spending Score (1-100)']]
    
    # Estandarizar
    features_scaled = scaler.transform(features)
    
    # Predecir
    clusters = modelo.predict(features_scaled)
    
    # Añadir predicciones
    datos['Cluster_Predicho'] = clusters
    
    return datos

# Ejemplo de uso de la función interactiva
ingresos_ejemplo = [45, 65, 30, 80, 25]
gastos_ejemplo = [55, 75, 85, 25, 35]

resultado_interactivo = predecir_cluster_interactivo(ingresos_ejemplo, gastos_ejemplo, scaler, kmeans)
print("\
Ejemplo de predicción interactiva:")
print(resultado_interactivo)

# Mostrar características de cada cluster para interpretación
print("\
Características de cada cluster:")
for cluster in sorted(df['Cluster'].unique()):
    cluster_data = df[df['Cluster'] == cluster]
    print(f"\
Cluster {cluster}:")
    print(f"  Ingreso anual promedio: {cluster_data['Annual Income (k$)'].mean():.2f} k$")
    print(f"  Puntuación de gasto promedio: {cluster_data['Spending Score (1-100)'].mean():.2f}")
    print(f"  Número de clientes: {len(cluster_data)}")

# Crear una función para guardar el modelo y el scaler
def guardar_modelo_clustering(modelo, scaler, nombre_archivo="modelo_clustering.pkl"):
    """
    Guarda el modelo de clustering y el scaler para uso futuro.
    
    Parámetros:
    modelo -- modelo KMeans entrenado
    scaler -- objeto StandardScaler entrenado
    nombre_archivo -- nombre del archivo para guardar
    """
    import pickle
    
    # Crear un diccionario con el modelo y el scaler
    modelo_completo = {
        'modelo': modelo,
        'scaler': scaler
    }
    
    # Guardar en un archivo
    with open(nombre_archivo, 'wb') as archivo:
        pickle.dump(modelo_completo, archivo)
    
    print(f"Modelo guardado como '{nombre_archivo}'")
    
    # Instrucciones para cargar el modelo
    print("\
Para cargar y usar el modelo en el futuro:")
    print("import pickle")
    print(f"with open('{nombre_archivo}', 'rb') as archivo:")
    print("    modelo_cargado = pickle.load(archivo)")
    print("scaler_cargado = modelo_cargado['scaler']")
    print("kmeans_cargado = modelo_cargado['modelo']")
    print("# Luego puedes usar scaler_cargado.transform() y kmeans_cargado.predict()")

# Guardar el modelo
guardar_modelo_clustering(kmeans, scaler)
Nuevos individuos con sus clusters predichos:
   ID  Age  Annual Income (k$)  Spending Score (1-100)  Cluster_Predicho
0   1   25                  30                      70                 2
1   2   30                  70                      80                 1
2   3   45                  50                      50                 0
3   4   22                  20                      90                 2
4   5   50                  90                      20                 3
5   6   35                  40                      60                 0
6   7   28                  25                      75                 2
7   8   40                  85                      30                 3
8   9   55                  35                      10                 4
9  10   33                  75                      85                 1

Ejemplo de predicción interactiva:
   ID  Annual Income (k$)  Spending Score (1-100)  Cluster_Predicho
0   1                  45                      55                 0
1   2                  65                      75                 1
2   3                  30                      85                 2
3   4                  80                      25                 3
4   5                  25                      35                 4
Características de cada cluster:
Cluster 0:
  Ingreso anual promedio: 55.30 k$
  Puntuación de gasto promedio: 49.52
  Número de clientes: 81
Cluster 1:
  Ingreso anual promedio: 86.54 k$
  Puntuación de gasto promedio: 82.13
  Número de clientes: 39
Cluster 2:
  Ingreso anual promedio: 25.73 k$
  Puntuación de gasto promedio: 79.36
  Número de clientes: 22
Cluster 3:
  Ingreso anual promedio: 88.20 k$
  Puntuación de gasto promedio: 17.11
  Número de clientes: 35
Cluster 4:
  Ingreso anual promedio: 26.30 k$
  Puntuación de gasto promedio: 20.91
  Número de clientes: 23
Modelo guardado como 'modelo_clustering.pkl'
Para cargar y usar el modelo en el futuro:
import pickle
with open('modelo_clustering.pkl', 'rb') as archivo:
    modelo_cargado = pickle.load(archivo)
scaler_cargado = modelo_cargado['scaler']
kmeans_cargado = modelo_cargado['modelo']
# Luego puedes usar scaler_cargado.transform() y kmeans_cargado.predict()
# Cargar datos con header inferido del archivo
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from scipy.cluster.hierarchy import dendrogram, linkage

# Verificar la versión de scikit-learn
import sklearn
print(f"Versión de scikit-learn: {sklearn.__version__}")

# Leer CSV asumiendo que el archivo tiene header
mall_df = pd.read_csv('Mall_Customers.csv')

# Mostrar primeros registros
print('Dataframe original:')
print(mall_df.head())

# Revisar info
print('\
Información del DataFrame:')
print(mall_df.info())

# Convertir columnas numéricas si es necesario
numeric_cols = ['Age', 'Annual Income (k$)', 'Spending Score (1-100)']
for col in numeric_cols:
    mall_df[col] = pd.to_numeric(mall_df[col], errors='coerce')

# Seleccionar características para clustering
features = numeric_cols
x = mall_df[features].values

# Estandarizar
scaler = StandardScaler()
x_scaled = scaler.fit_transform(x)

# Dendrograma para inspección del número óptimo de clusters
linked = linkage(x_scaled, method='ward')
plt.figure(figsize=(10,7))
dendrogram(linked, truncate_mode='lastp', p=20)
plt.title('Dendrograma - Método Ward')
plt.xlabel('Observaciones')
plt.ylabel('Distancia')
plt.show()

# Corregir los parámetros de AgglomerativeClustering
# Verificar los parámetros disponibles
print("\
Parámetros disponibles para AgglomerativeClustering:")
print(AgglomerativeClustering.__init__.__code__.co_varnames)

# Seleccionar número de clusters basado en observación, por ejemplo 5
n_clusters = 5
# Usar los parámetros correctos según la versión
try:
    # Intentar con los parámetros corregidos
    cluster_model = AgglomerativeClustering(n_clusters=n_clusters, metric='euclidean', linkage='ward')
except TypeError:
    # Si falla, intentar con menos parámetros
    cluster_model = AgglomerativeClustering(n_clusters=n_clusters)

cluster_labels = cluster_model.fit_predict(x_scaled)

mall_df['Cluster'] = cluster_labels

# Mostrar la cantidad de individuos por cluster
print('Cantidad de individuos por cluster:')
print(mall_df['Cluster'].value_counts().sort_index())

# Mostrar tabla de individuos y sus clusters
print('\
Ejemplo de tabla con clusters:')
print(mall_df[['CustomerID','Age','Annual Income (k$)', 'Spending Score (1-100)', 'Cluster']].head(10))

# Graficar los clusters: Ingreso vs Puntuación de Gasto
plt.figure(figsize=(8,6))
sns.scatterplot(data=mall_df, x='Annual Income (k$)', y='Spending Score (1-100)', hue='Cluster', palette='Set1', s=80)
plt.title('Clusters: Ingreso vs Puntuación de Gasto')
plt.show()

# Graficar: Edad vs Puntuación de Gasto
plt.figure(figsize=(8,6))
sns.scatterplot(data=mall_df, x='Age', y='Spending Score (1-100)', hue='Cluster', palette='Set2', s=80)
plt.title('Clusters: Edad vs Puntuación de Gasto')
plt.show()

# Función para predecir el cluster de nuevos individuos
# Nota: AgglomerativeClustering no tiene método 'predict'. Calculamos centroides a partir de los datos escalados.
centroids = []
for i in range(n_clusters):
    cent = x_scaled[mall_df['Cluster'] == i].mean(axis=0)
    centroids.append(cent)
centroids = np.array(centroids)

from scipy.spatial.distance import cdist

def predecir_cluster(nuevos_datos):
    '''
    nuevos_datos: array-like de forma (n_ejemplos, 3) en el orden: Age, Annual Income (k$), Spending Score (1-100)
    '''
    # Estandarizar nuevos datos con el scaler previamente calculado
    nuevos_scaled = scaler.transform(nuevos_datos)
    # Calcular la distancia a cada centroide
    dists = cdist(nuevos_scaled, centroids, metric='euclidean')
    # El índice del centroide más cercano es el cluster asignado
    return np.argmin(dists, axis=1)

# Ejemplo de predicción para dos nuevos individuos
nuevos = np.array([[25, 40, 60], [40, 70, 20]])
predicciones = predecir_cluster(nuevos)
print('\
Predicción de clusters para nuevos individuos:')
print(predicciones)

print('done')
Versión de scikit-learn: 1.6.1
Dataframe original:
   CustomerID   Genre  Age  Annual Income (k$)  Spending Score (1-100)
0           1    Male   19                  15                      39
1           2    Male   21                  15                      81
2           3  Female   20                  16                       6
3           4  Female   23                  16                      77
4           5  Female   31                  17                      40
Información del DataFrame:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 5 columns):
 #   Column                  Non-Null Count  Dtype 
---  ------                  --------------  ----- 
 0   CustomerID              200 non-null    int64 
 1   Genre                   200 non-null    object
 2   Age                     200 non-null    int64 
 3   Annual Income (k$)      200 non-null    int64 
 4   Spending Score (1-100)  200 non-null    int64 
dtypes: int64(4), object(1)
memory usage: 7.9+ KB
None

Parámetros disponibles para AgglomerativeClustering:
('self', 'n_clusters', 'metric', 'memory', 'connectivity', 'compute_full_tree', 'linkage', 'distance_threshold', 'compute_distances')
Cantidad de individuos por cluster:
Cluster
0    66
1    45
2    39
3    28
4    22
Name: count, dtype: int64
Ejemplo de tabla con clusters:
   CustomerID  Age  Annual Income (k$)  Spending Score (1-100)  Cluster
0           1   19                  15                      39        4
1           2   21                  15                      81        0
2           3   20                  16                       6        4
3           4   23                  16                      77        0
4           5   31                  17                      40        4
5           6   22                  17                      76        0
6           7   35                  18                       6        4
7           8   23                  18                      94        0
8           9   64                  19                       3        4
9          10   30                  19                      72        0

Predicción de clusters para nuevos individuos:
[0 3]
done
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# Creamos el scatter plot base para 'Annual Income (k$)' vs 'Spending Score (1-100)'
plt.figure(figsize=(8,6))
sns.scatterplot(data=mall_df, x='Annual Income (k$)', y='Spending Score (1-100)', hue='Cluster', palette='Set1', s=80, legend='brief')

# Nuevos individuos
nuevos = np.array([[25, 40, 60], [40, 70, 20]])
predicciones = predecir_cluster(nuevos)

# Para asignar colores, obtendremos las paletas de colores para clusters
palette = sns.color_palette('Set1', n_colors=5)

# Graficar los nuevos individuos usando marcas de estrella y el color correspondiente
for i, (p, cluster) in enumerate(zip(nuevos, predicciones)):
    # p tiene el formato: [Age, Annual Income (k$), Spending Score (1-100)]
    # Queremos graficar Ingreso vs Score, que son las columnas 1 y 2 respectivamente
    plt.scatter(p[1], p[2], s=200, marker='*', color=palette[cluster], edgecolor='black', label='Nuevo individuo ' + str(i+1) if i==0 else "")

plt.title('Clusters: Ingreso vs Puntuación de Gasto(incluyendo nuevosindividuos)')
plt.xlabel('Annual Income (k$)')
plt.ylabel('Spending Score (1-100)')
plt.legend()
plt.show()

print('done')

done

UNA FORMA MAS ORDENADA

# Import necessary libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.cluster import KMeans, AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.metrics import silhouette_score
# Cargar el dataset
file_path = 'Mall_Customers.csv'
df = pd.read_csv(file_path, encoding='ascii')

# Revisar head del dataset
print('Primeras filas del dataset:')
print(df.head())
Primeras filas del dataset:
   CustomerID   Genre  Age  Annual Income (k$)  Spending Score (1-100)
0           1    Male   19                  15                      39
1           2    Male   21                  15                      81
2           3  Female   20                  16                       6
3           4  Female   23                  16                      77
4           5  Female   31                  17                      40
# Voy a usar las siguientes columnas para clustering: 'Annual Income (k$)' y 'Spending Score (1-100)'.
# Estos dos son populares para analizar clusters en el dataset Mall Customers

X = df[['Annual Income (k$)', 'Spending Score (1-100)']].values

# Escalado (opcional, pero aquí las variables tienen rangos similares, así que lo omitiremos)

# ------------------------------
# Clustering Jerárquico
# ------------------------------

# Cálculo del linkage matrix usando Ward
Z = linkage(X, method='ward')
# Dendrograma
plt.figure(figsize=(10, 7))
dendrogram(Z, truncate_mode='lastp', p=20, leaf_rotation=45., leaf_font_size=12., show_contracted=True)
plt.title('Dendrograma - Clustering Jerárquico')
plt.xlabel('Muestras o Cluster')
plt.ylabel('Distancia')
plt.show()

# Usando AgglomerativeClustering para asignar clusters (corregido)
# Usamos 5 clusters como ejemplo, que es usual en Mall Customers
agg_model = AgglomerativeClustering(n_clusters=5, metric='euclidean', linkage='ward')
clusters_hier = agg_model.fit_predict(X)

# Añadir la asignación al DF
df['Cluster_Hier'] = clusters_hier

# Tabla de conteo por cluster
table_hier = df['Cluster_Hier'].value_counts().sort_index()
print('Conteo de individuos por cluster - Clustering Jerárquico:')
print(table_hier)
Conteo de individuos por cluster - Clustering Jerárquico:
Cluster_Hier
0    32
1    85
2    39
3    21
4    23
Name: count, dtype: int64
# ------------------------------
# Clustering K-means
# ------------------------------

# El método del codo para seleccionar k óptimo
inertias = []
k_vals = range(1, 11)
for k in k_vals:
    kmeans_temp = KMeans(n_clusters=k, random_state=42)
    kmeans_temp.fit(X)
    inertias.append(kmeans_temp.inertia_)

plt.figure(figsize=(8,5))
plt.plot(k_vals, inertias, 'bo-')
plt.xlabel('Número de clusters (k)')
plt.ylabel('Inercia')
plt.title('Método del Codo para K-means')
plt.show()

# Seleccionar k=5 basado en el dendrograma y el método del codo
k_opt = 5
kmeans = KMeans(n_clusters=k_opt, random_state=42)
clusters_km = kmeans.fit_predict(X)
# Agregar clusters K-means al DF
df['Cluster_Kmeans'] = clusters_km

# Tabla de conteo por cluster
table_km = df['Cluster_Kmeans'].value_counts().sort_index()
print('Conteo de individuos por cluster - K-means:')
print(table_km)
Conteo de individuos por cluster - K-means:
Cluster_Kmeans
0    81
1    39
2    22
3    35
4    23
Name: count, dtype: int64
# Calcular la "precisión" del modelo en términos de la puntuación de silhouette
silhouette_km = silhouette_score(X, clusters_km)
silhouette_hier = silhouette_score(X, clusters_hier)

print('Silhouette Score para K-means: ' + str(round(silhouette_km,4)))
print('Silhouette Score para Clustering Jerárquico: ' + str(round(silhouette_hier,4)))
Silhouette Score para K-means: 0.5539
Silhouette Score para Clustering Jerárquico: 0.553
# Visualización de clusters - K-means
plt.figure(figsize=(8,6))
sns.scatterplot(x=df['Annual Income (k$)'], y=df['Spending Score (1-100)'], hue=df['Cluster_Kmeans'], palette='Set1', s=100, legend='full')
plt.scatter(kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], s=300, c='black', marker='X', label='Centroides')
plt.title('Clusters con K-means')
plt.xlabel('Annual Income (k$)')
plt.ylabel('Spending Score (1-100)')
plt.legend()
plt.show()

# Visualización de clusters - Jerárquico
plt.figure(figsize=(8,6))
sns.scatterplot(x=df['Annual Income (k$)'], y=df['Spending Score (1-100)'], hue=df['Cluster_Hier'], palette='Set2', s=100, legend='full')
plt.title('Clusters con Clustering Jerárquico')
plt.xlabel('Annual Income (k$)')
plt.ylabel('Spending Score (1-100)')
plt.legend()
plt.show()

# Mostrar la tabla con la asignación de clusters para K-means (individuales)
cluster_assignments = df[['CustomerID', 'Cluster_Kmeans']]
print('Asignación de cada individuo a clusters (K-means):')
print(cluster_assignments.head(10))
Asignación de cada individuo a clusters (K-means):
   CustomerID  Cluster_Kmeans
0           1               4
1           2               2
2           3               4
3           4               2
4           5               4
5           6               2
6           7               4
7           8               2
8           9               4
9          10               2
# Función de predicción para nuevos individuos usando el modelo kmeans
def predecir_cluster(annual_income, spending_score):
    datos = np.array([[annual_income, spending_score]])
    cluster_pred = kmeans.predict(datos)
    return cluster_pred[0]

# Ejemplo de predicción para un nuevo individuo
nuevo_individuo = {'Annual Income (k$)': 70, 'Spending Score (1-100)': 40}
pred_cluster = predecir_cluster(nuevo_individuo['Annual Income (k$)'], nuevo_individuo['Spending Score (1-100)'])
print('El nuevo individuo se asigna al cluster (K-means): ' + str(pred_cluster))
El nuevo individuo se asigna al cluster (K-means): 0
# Visualización mejorada con factoextra style
plt.figure(figsize=(10,8))
# Crear un gráfico de dispersión con colores por cluster
ax = sns.scatterplot(x='Annual Income (k$)', y='Spending Score (1-100)', 
                    hue='Cluster_Kmeans', data=df, 
                    palette='viridis', s=100, alpha=0.8)

# Añadir centroides
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], 
           s=200, c='red', marker='X', edgecolor='black', linewidth=1.5,
           label='Centroides')

# Añadir etiquetas para cada centroide
for i, centroid in enumerate(kmeans.cluster_centers_):
    plt.annotate(f'Cluster {i}', 
                xy=(centroid[0], centroid[1]),
                xytext=(centroid[0]+2, centroid[1]+2),
                fontsize=12, fontweight='bold')

# Personalizar el gráfico
plt.title('Análisis de Clusters K-means con Estilo Factoextra', fontsize=16, fontweight='bold')
plt.xlabel('Ingreso Anual (k$)', fontsize=14)
plt.ylabel('Puntuación de Gasto (1-100)', fontsize=14)
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(title='Clusters', fontsize=12, title_fontsize=13)

# Añadir un recuadro alrededor del gráfico
ax.spines['top'].set_visible(True)
ax.spines['right'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
for spine in ax.spines.values():
    spine.set_linewidth(1.5)
    
plt.tight_layout()
plt.show()

# Crear una tabla más detallada con información de los clusters
cluster_summary = df.groupby('Cluster_Kmeans').agg({
    'CustomerID': 'count',
    'Annual Income (k$)': ['mean', 'min', 'max'],
    'Spending Score (1-100)': ['mean', 'min', 'max']
}).round(2)

cluster_summary.columns = ['Cantidad', 'Ingreso Promedio', 'Ingreso Mínimo', 'Ingreso Máximo', 
                          'Gasto Promedio', 'Gasto Mínimo', 'Gasto Máximo']

print("Resumen estadístico por cluster (K-means):")
print(cluster_summary)
Resumen estadístico por cluster (K-means):
                Cantidad  Ingreso Promedio  Ingreso Mínimo  Ingreso Máximo  \
Cluster_Kmeans                                                               
0                     81             55.30              39              76   
1                     39             86.54              69             137   
2                     22             25.73              15              39   
3                     35             88.20              70             137   
4                     23             26.30              15              39   

                Gasto Promedio  Gasto Mínimo  Gasto Máximo  
Cluster_Kmeans                                              
0                        49.52            34            61  
1                        82.13            63            97  
2                        79.36            61            99  
3                        17.11             1            39  
4                        20.91             3            40  
# Crear una visualización de la distribución de clusters
plt.figure(figsize=(12, 5))

# Gráfico de barras para el conteo de individuos por cluster
plt.subplot(1, 2, 1)
sns.countplot(x='Cluster_Kmeans', data=df, palette='viridis')
plt.title('Número de Clientes por Cluster')
plt.xlabel('Cluster')
plt.ylabel('Cantidad de Clientes')
C:\Users\MINEDUCYT\AppData\Local\Temp\ipykernel_10384\1395649029.py:6: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.countplot(x='Cluster_Kmeans', data=df, palette='viridis')
Text(0, 0.5, 'Cantidad de Clientes')

# Gráfico de pastel para la proporción de clusters
plt.subplot(1, 2, 2)
df['Cluster_Kmeans'].value_counts().plot.pie(autopct='%1.1f%%', colors=sns.color_palette('viridis', n_colors=5),
                                           wedgeprops={'linewidth': 1, 'edgecolor': 'white'})
plt.title('Proporción de Clientes por Cluster')
plt.ylabel('')

plt.tight_layout()
plt.show()