Agrupamiento KMeans – Práctica con Python

En este proyecto implementaremos el algoritmo de Agrupamiento Kmeans en Python, analizaremos un conjunto de datos que contiene información sobre los montos de gasto anual de varios clientes de diversas categorías de productos para la estructura interna.

Nuestro objetivo es agrupar los datos para que podamos ver los productos que son comprados juntos por los clientes. Por ejemplo, si una persona va a la tienda a comprar algún comestible, es muy probable que también compre “leche”, por lo tanto podemos colocar la “leche” cerca de los comestibles de la tienda; de la misma manera es muy poco probable que la misma persona compre las verduras frescas al mismo tiempo.

Si podemos predecir este comportamiento del cliente, podemos organizar la tienda, en consecuencia aumentará las ventas de artículos.

El conjunto de datos de este proyecto se puede encontrar en Kaggle bajo el nombre de “Wholesale Customers data set”, el link es el siguiente: https://bit.ly/2KECiHq

Como todo proyecto de Machine Learning, lo primero que vamos a hacer será importar las librerías que vamos a utilizar. Por los momentos solo importaremos las de Pandas, para manipular los datos y la de Matplotlib, para analizar visualmente los datos. Posteriormente importaremos las librerías correspondientes al algoritmo de agrupamiento, por los momentos nos enfocaremos a entender los datos.

#### IMPORTAR LAS LIBRERÍAS ####
import pandas as pd
import matplotlib.pyplot as plt

El siguiente paso es cargar los datos que vamos a utilizar. En este punto ya debiste haber descargado los datos de Kaggle y haberlos guardado en tu computador, como siempre recomiendo es preferible guardar estos datos en la misma carpeta en donde estamos guardando el programa, de esta forma será más fácil importarlos y trabajar con ellos.

Para importar los datos usamos la instrucción read_csv junto con el nombre del archivo en donde se encuentran los datos o, si es necesario, se debe incluir la ruta en donde se encuentran guardados.

Los datos se encuentran guardados dentro de la variable “data” por lo que el siguiente paso es analizarlos.

#### CARGAR LOS DATOS ####
data = pd.read_csv('Wholesale customers data.csv')

Antes de realizar el análisis veamos de que se trata cada una de las columnas con las que cuenta el conjunto de datos.

La primera columna corresponde a “Channel” o Canal y se refiere al canal de clientes que vendría siendo hoteles, restaurantes o cafetería o canal de venta al por menor.

La segunda columna se refiere a la de “Region” o Región de venta, que sería Lisboa, Oporto u otra.

A partir de la tercera columna podemos encontrar la información que realmente nos interesa, que serían los productos que se venden. Recuerda que en cada columna nos mostrará los datos correspondientes al gasto anual de los productos correspondientes a cada categoría.

Comenzamos con “Fresh” o Fresco que son los productos frescos, como verduras o vegetales.

La siguiente columna es “Milk” o Leche correspondiente a los productos lácteos.

Seguidamente encontramos “Grocery” o Comestibles referente a todos los productos que consumimos.

La siguiente columna es “Frozen” o Congelados correspondiente a los alimentos congelados.

La penúltima columna se refiere a “Detergents Paper” o Papel y Detergentes que vendría siendo todos los productos detergentes y productos de papel.

La última columna corresponde a “Delicassen” o Delicateses referente a todos esos productos que no son incluidos en las categorías mencionadas anteriormente.

Realizado este análisis inicial ahora procedemos a analizar los datos utilizando instrucciones de Python.

Veamos primero la forma de los datos para esto utilizamos la instrucción “shape”. El conjunto de datos con los que trabajaremos cuenta con 440 filas o datos y 8 columnas que vendrían siendo las características.

#Conocer la forma de los datos
data.shape

1

Recuerda que estamos trabajando en un proyecto de aprendizaje no supervisado por lo que acá el conjunto de datos no contará con una columna de variables dependientes o en todo caso “y”, acá todas las columnas serán variables independientes o “x”.

Seguidamente verificamos si el conjunto de datos con el que estamos trabajando cuenta con datos nulos, para esto implementamos las instrucciones de Python, “isnull()” y “sum()”, con esto verificamos si existen datos nulos y a su vez los sumamos en cada columna. Una vez ejecutado este código podemos observar que ninguna columna cuenta con datos perdidos por lo que no tenemos que realizar ningún procesamiento de datos al respecto.

#Conocer los datos nulos
data.isnull().sum()

2

Por último, verificamos el formato de los datos de cada una de las columnas del conjunto de datos, para esto utilizamos la instrucción “dtypes”. Una vez ejecutado esta línea de código podemos observar que todos los datos son enteros, es decir numéricos, por lo que no tenemos que realizar ningún procesamiento especial sobre los mismos.

#Conocer el formato de los datos
data.dtypes

3

En este punto ya debemos entender los datos con los que estamos trabajando.

Antes de realizar el procesamiento de los datos separemos tres filas que vendrían a ser nuestros datos de muestras, los cuales utilizaremos posteriormente para verificar en qué clúster pertenecen. Estos datos los elegimos al alzar, perfectamente tu puedes elegir otros, e inclusive más.

Como te lo mencione antes, recuerda que este es un problema de aprendizaje no supervisado, acá no contamos con datos para comparar los resultados que obtenemos por lo que debemos buscar alguna forma de ver y analizar el resultado para ello separamos estos datos para ver si corresponde a algún clúster que encontremos finalizado nuestro análisis.

Para realizar esto primero definimos las filas que utilizaremos como muestra, en mi caso he seleccionado la número 26, 176 y 392, recuerda que deberán ser números entre 0 y 400 que es número de datos con el que contamos.

Como te lo mencione anteriormente estos son datos al azar tu puedes seleccionar otros sin ningún problema.

Definido esto y almacenado en la variable “índice”, ahora procedemos a ubicar esta información en nuestro conjunto de datos y almacenarla en la variable “muestras”. Para realizar esto utilizamos la instrucción “loc” junto con los números de las filas definidas anteriormente, incluimos el nombre de cada una de las columnas y eliminamos el resto de la información.

En este momento ya tenemos en la variable “muestras” las tres filas que utilizaremos posteriormente para conocer a que clúster corresponde.

Finalmente eliminamos estas filas de nuestro conjunto de datos original para que el modelo no se entrene con esta información, por lo tanto utilizamos la instrucción “drop()” junto la variable “índices”, fíjate que acá utilizamos “axis” igual a 0, esto es porque estamos eliminando filas y no columnas como normalmente hacemos.

### DATOS DE MUESTRA ###
#Se selecionan unos datos al azar para posteriormente verificar el clúster 
#al que pertenecen
indices = [26, 176, 392]
muestras = pd.DataFrame(data.loc[indices], 
                       columns = data.keys()).reset_index(drop = True)

data = data.drop(indices, axis = 0)

En este punto ya entendemos los datos con los que estamos trabajando y hemos separado los datos correspondientes a la muestra por lo que podemos proceder a realizar el siguiente paso que es el de procesamiento de los datos.

El procesamiento de los datos consistirá en solamente dos acciones, la primera es eliminar las columnas que no afecta a nuestro análisis y la última acción será el escalamiento de los datos ya que si observas los datos se encuentran rangos muy variantes por lo que es conveniente realizar un procesamiento de los mismos.

El primer paso para el procesamiento de los datos es eliminar las columnas que no aporta alguna información importante para nuestro análisis y esas columnas corresponden a la región y el canal, estas dos características no son importantes para nuestro objetivo final que sería conocer las relaciones de los productos más vendidos, en caso que queramos conocer la región o el canal de venta si los deberías dejar e incluir en el análisis pero para nuestro caso esta información no es necesaria por lo tanto podemos eliminar ambas columnas.

Para eliminarlas solamente debemos utilizar la instrucción “drop” junto los nombres de ambas columnas, acá también podíamos utilizar la ubicación de las mismas, ambas opciones son correctas. Al eliminar ambas columnas ya nuestro conjunto de datos no cuenta con 8 columnas sino con 6.

Este mismo procedimiento lo debemos realizar con la variable “muestras”, ya que esta es un espejo a los datos con los que vamos a trabajar. Cualquier cambio que hagamos al conjunto de datos original también lo debemos hacer a este mini conjunto de lo contrario al momento de utilizarlo nos dará error.

### PROCESAMIENTO DE LOS DATOS ###
#Eliminamos las columnas de región y canal 
data = data.drop(['Region', 'Channel'], axis = 1)
muestras = muestras.drop(['Region', 'Channel'], axis = 1)

El siguiente paso es el de realizar el escalamiento de los datos. Si observas el conjunto de datos te podrás cuenta que los rangos de datos que se manejan son muy variantes y esto puede afectar el análisis cuando apliquemos el algoritmo, por lo que es conveniente realizar el escalamiento de los datos.

Para realizar esto importamos de “sklearn” el modulo correspondiente a “preprocessing”. Acá utilizamos “normalizer()” y “fit_transform” junto con el conjunto de datos para transformar los datos.

Fíjate que ahora nuestro conjunto de datos se llama data_escalada y como puedes observar los datos son flotantes y se encuentran en una misma escala.

Exactamente como hicimos en el paso anterior, debemos escalar los datos correspondientes a las “muestras”, ahora nuestras muestras se llamarán “muestras_escalada”.

#Se realiza el escalamiento de los datos
from sklearn import preprocessing

data_escalada = preprocessing.Normalizer().fit_transform(data)
muestras_escalada = preprocessing.Normalizer().fit_transform(muestras)

En este punto ya tenemos nuestros datos listos para que podamos trabajar con ellos con nuestro algoritmo de agrupamiento.

Ha llegado el momento de realizar el análisis de Machine Learning y más específicamente del de agrupamiento Kmeans, por lo que obviamente el primer paso será importar este modulo ubicado en “sklearn.cluster”, con esto ya podemos implementar este algoritmo dentro de nuestro programa de Python.

### ANÁLISIS DE MACHINE LEARNING ###
from sklearn.cluster import KMeans

Procedemos a determinar las variables a evaluar, para ello simplemente definimos la variable “X” igual a data_escalada. Esto lo hacemos simplemente para utilizar la variable “X” como siempre se realiza en los proyectos de Machine Learning.

#Se determina las variables a evaluar
X = data_escalada.copy()

Por lo que ahora desarrollamos el primer paso que debemos hacer en cualquier proyecto de Kmeans y es el de hallar el valor óptimo de K. Recuerda que para el algoritmo Kmeans debemos indicar el valor de K, esto se debe hacer previamente antes de implementar el algoritmo.

Para este caso utilizaremos el método de codo para hallar el valor de K.

El método del codo consiste en calcular y graficar la suma de cuadrado en cada número de clústeres, y allí buscas un cambio de pendiente de empinada a poca profundidad, un codo, para determinar el número óptimo de clústeres.

Para realizar el método del codo nos valdremos de Python y desarrollaremos una función en donde calcularemos varios valores de K implementando el algoritmo kmeans, y para cada valor de K calculamos la inercia que vendría siendo la suma total del cuadrado dentro del clúster. Todo esto lo haremos de 1 a 20 veces y guardaremos los valores calculados dentro de la variable “inercia” para que posteriormente podamos realizar la gráfica y obtener el valor definitivo de K que utilizaremos para obtener nuestro modelo.

Recuerda que tengo una explicación mucho más detallada sobre el método del codo, en caso de que no entiendas lo que estamos haciendo acá.

Ejecutamos la función y ya tenemos los valores que necesitamos para realizar la gráfica, para esto nos valdremos de la librería de Python Matplotlib.

## Hallar el valor óptimo de K ##
#Se aplicará el método de codo para hallar K
#Se calcula el algoritmo de agrupación para diferentes valores de K
inercia = [] 
for i in range(1, 20):
    algoritmo = KMeans(n_clusters = i, init = 'k-means++', 
                       max_iter = 300, n_init = 10)
    algoritmo.fit(X)
    #Para cada K, se calcula la suma total del cuadrado dentro del clúster
    inercia.append(algoritmo.inertia_)

#Se traza la curva de la suma de errores cuadráticos 
plt.figure(figsize=[10,6])
plt.title('Método del Codo')
plt.xlabel('No. de clusters')
plt.ylabel('Inercia')
plt.plot(list(range(1, 20)), inercia, marker='o')
plt.show()

4

Si vemos la gráfica podemos observar que el codo se encuentra entre los valores de 5 o 6. En mi caso utilizaré 6, porque también hace referencia al número de características con la que contamos, pero si tu quieres puedes probar con 5 para ver si el resultado es distinto.

Definido el valor de K, ahora si podemos aplicar el algoritmo de agrupamiento. Por lo que acá definimos la variable “algoritmo” igual al algoritmo “KMeans”, expliquemos cada una de los parámetros que configuramos al algoritmo.

La primera de ellas es “n_cluster” que vendría siendo el número de clústeres que definimos previamente, para nuestro caso este valor será 6.

El siguiente parámetro es “init” y se refiere al método de inicialización, para nuestro caso será “k-means++”, esto lo podríamos obviar ya que estamos utilizando la configuración por defecto de este parámetro, pero no esta demás que nos recordemos del mismo.

El siguiente parámetro es “max_iter”, este es el número máximo de iteraciones del algoritmo K Means para una sola ejecución, por defecto es 300, y así lo indicamos acá, pero este valor lo puedes variar posteriormente, si así lo quieras.

El último parámetro que configuraremos será “n_init, y se refiere al número de veces que el algoritmo se ejecutará con diferentes centroides. El valor por defecto es de 10 y lo dejaremos de esa forma. Este es otro valor que puedes ajustar al momento de querer mejor el modelo.

## Se aplica el algoritmo de clustering ##
#Se define el algoritmo junto con el valor de K
algoritmo = KMeans(n_clusters = 6, init = 'k-means++', 
                   max_iter = 300, n_init = 10)

Definido el algoritmo y los parámetros del mismo ahora procedemos a entrenarlo junto con los datos que ya hemos procesado previamente y se encuentran almacenados en la variable “X”. Fíjate que acá se utiliza a instrucción “fit()” para entrenar el modelo, esta es la misma instrucción de los algoritmos de aprendizaje supervisado.

#Se entrena el algoritmo
algoritmo.fit(X)

Entrenado el modelo ahora procedemos a obtener los centroides y las etiquetas, para esto utilizamos los atributos “cluster_centers_”  y “labels_” que son propios del algoritmo KMeans en Python.

Verifiquemos lo que nos devuelve ambos atributos. El primero es “cluster_centers_”  este nos devuelve las coordenadas de los centroides de cada clúster, como en principio definimos 6 centroides, entonces nos devuelve 6 coordenadas, las cuales podemos utilizar para graficar.

El siguiente atributo es el de “labels_” y se refiere a las etiquetas de los clústeres que fueron calculados, es decir para cada uno de los datos del conjunto de datos que utilizamos para entrenar el modelo nos indica a que clúster se ubica. Acá obviamente nos indica un número del 0 al 5 que es al final el número de clúster que definimos previamente. De igual forma esta información es útil para graficarla.

#Se obtiene los datos de los centroides y las etiquetas
centroides, etiquetas = algoritmo.cluster_centers_, algoritmo.labels_

Antes de realizar la grafica de los datos obtenidos utilicemos los datos de muestras que separamos al principio para determinar a qué clúster pertenece.

Para esto simplemente utilizamos el modelo obtenido que se encuentra en la variable “algoritmo” y la instrucción “predict()” junto con los datos de muestra que fueron escalados.

#Utilicemos los datos de muestras y verifiquemos en que cluster se encuentran
muestra_prediccion = algoritmo.predict(muestras_escalada)

for i, pred in enumerate(muestra_prediccion):
    print("Muestra", i, "se encuentra en el clúster:", pred)

En caso de que tengamos nuevos datos, tenemos que ingresarlos de la misma forma que hicimos con “muestras_escalada”, recuerda que esta información deberá estar escalada y en el mismo formato que esta variable.

Finalmente graficamos los datos.

### GRAFICAR LOS DATOS JUNTO A LOS RESULTADOS ###
# Se aplica la reducción de dimensionalidad a los datos
from sklearn.decomposition import PCA

modelo_pca = PCA(n_components = 2)
modelo_pca.fit(X)
pca = modelo_pca.transform(X) 

#Se aplicar la reducción de dimsensionalidad a los centroides
centroides_pca = modelo_pca.transform(centroides)

# Se define los colores de cada clúster
colores = ['blue', 'red', 'green', 'orange', 'gray', 'brown']

#Se asignan los colores a cada clústeres
colores_cluster = [colores[etiquetas[i]] for i in range(len(pca))]

#Se grafica los componentes PCA
plt.scatter(pca[:, 0], pca[:, 1], c = colores_cluster, 
            marker = 'o',alpha = 0.4)

#Se grafican los centroides
plt.scatter(centroides_pca[:, 0], centroides_pca[:, 1],
            marker = 'x', s = 100, linewidths = 3, c = colores)

#Se guadan los datos en una variable para que sea fácil escribir el código
xvector = modelo_pca.components_[0] * max(pca[:,0])
yvector = modelo_pca.components_[1] * max(pca[:,1])
columnas = data.columns

#Se grafican los nombres de los clústeres con la distancia del vector
for i in range(len(columnas)):
    #Se grafican los vectores
    plt.arrow(0, 0, xvector[i], yvector[i], color = 'black', 
              width = 0.0005, head_width = 0.02, alpha = 0.75)
    #Se colocan los nombres
    plt.text(xvector[i], yvector[i], list(columnas)[i], color='black', 
             alpha=0.75)

plt.show()

5

Si observamos la gráfica vemos claramente los 6 clústeres representados por distintos colores, verde, rojo, marrón, naranja, azul y gris. A su vez, vemos con una X la ubicación del centroide de cada clúster.

Fíjate que algunos clústeres se encuentran concentrados en un mismo lugar, mientras que otros se encuentran más disperso como el azul y gris.

A su vez puedes ver una serie de flechas, estas las obtenemos por un código que se desarrollo en Python, pero lo que indican es que son una proyección de cada característica en el eje principal del componente. Estas flechas representan el nivel de importancia de cada característica en la escala multidimensional. Por ejemplo, los congelados y frescos contribuyen más que las otras características.

Es decir, podemos concluir que los productos frescos como frutas y verduras deben colocarse por separado, mientras que los comestibles, detergentes y leche deben colocarse uno al lado del otro, ya que son más factibles de comprarse juntos.

En conclusión los datos obtenidos acá deben ser analizados siempre por nosotros, los resultados acá son completamente distintos a los algoritmos de Aprendizaje no Supervisado, que obteníamos un valor como resultado. Inclusive acá no sabemos con seguridad si el resultado obtenido es correcto, por lo que es muy importante entender los datos y sobretodo el proceso en sí de esta forma podrás realizar un análisis final que puedas presentar, ya que con simplemente presentar la gráfica anterior no es suficiente.

A continuación el código completo:

"""

Algoritmo K Means

"""

#### IMPORTAR LAS LIBRERÍAS ####
import pandas as pd
import matplotlib.pyplot as plt

#### CARGAR LOS DATOS ####
data = pd.read_csv('Wholesale customers data.csv')

### ANALIZAR LOS DATOS ###
#Conocer la forma de los datos
data.shape

#Conocer los datos nulos
data.isnull().sum()

#Conocer el formato de los datos
data.dtypes

### DATOS DE MUESTRA ###
#Se selecionan unos datos al azar para posteriormente verificar el clúster 
#al que pertenecen
indices = [26, 176, 392]
muestras = pd.DataFrame(data.loc[indices], 
                       columns = data.keys()).reset_index(drop = True)

data = data.drop(indices, axis = 0)

### PROCESAMIENTO DE LOS DATOS ###
#Eliminamos las columnas de región y canal 
data = data.drop(['Region', 'Channel'], axis = 1)
muestras = muestras.drop(['Region', 'Channel'], axis = 1)

#Se realiza el escalamiento de los datos
from sklearn import preprocessing

data_escalada = preprocessing.Normalizer().fit_transform(data)
muestras_escalada = preprocessing.Normalizer().fit_transform(muestras)

### ANÁLISIS DE MACHINE LEARNING ###
from sklearn.cluster import KMeans

#Se determina las variables a evaluar
X = data_escalada.copy()

## Hallar el valor óptimo de K ##
#Se aplicará el método de codo para hallar K
#Se calcula el algoritmo de agrupación para diferentes valores de K
inercia = [] 
for i in range(1, 20):
    algoritmo = KMeans(n_clusters = i, init = 'k-means++', 
                       max_iter = 300, n_init = 10)
    algoritmo.fit(X)
    #Para cada K, se calcula la suma total del cuadrado dentro del clúster
    inercia.append(algoritmo.inertia_)

#Se traza la curva de la suma de errores cuadráticos 
plt.figure(figsize=[10,6])
plt.title('Método del Codo')
plt.xlabel('No. de clusters')
plt.ylabel('Inercia')
plt.plot(list(range(1, 20)), inercia, marker='o')
plt.show()

## Se aplica el algoritmo de clustering ##
#Se define el algoritmo junto con el valor de K
algoritmo = KMeans(n_clusters = 6, init = 'k-means++', 
                   max_iter = 300, n_init = 10)

#Se entrena el algoritmo
algoritmo.fit(X)

#Se obtiene los datos de los centroides y las etiquetas
centroides, etiquetas = algoritmo.cluster_centers_, algoritmo.labels_

#Utilicemos los datos de muestras y verifiquemos en que cluster se encuentran
muestra_prediccion = algoritmo.predict(muestras_escalada)

for i, pred in enumerate(muestra_prediccion):
    print("Muestra", i, "se encuentra en el clúster:", pred)

### GRAFICAR LOS DATOS JUNTO A LOS RESULTADOS ###
# Se aplica la reducción de dimensionalidad a los datos
from sklearn.decomposition import PCA

modelo_pca = PCA(n_components = 2)
modelo_pca.fit(X)
pca = modelo_pca.transform(X) 

#Se aplicar la reducción de dimsensionalidad a los centroides
centroides_pca = modelo_pca.transform(centroides)

# Se define los colores de cada clúster
colores = ['blue', 'red', 'green', 'orange', 'gray', 'brown']

#Se asignan los colores a cada clústeres
colores_cluster = [colores[etiquetas[i]] for i in range(len(pca))]

#Se grafica los componentes PCA
plt.scatter(pca[:, 0], pca[:, 1], c = colores_cluster, 
            marker = 'o',alpha = 0.4)

#Se grafican los centroides
plt.scatter(centroides_pca[:, 0], centroides_pca[:, 1],
            marker = 'x', s = 100, linewidths = 3, c = colores)

#Se guadan los datos en una variable para que sea fácil escribir el código
xvector = modelo_pca.components_[0] * max(pca[:,0])
yvector = modelo_pca.components_[1] * max(pca[:,1])
columnas = data.columns

#Se grafican los nombres de los clústeres con la distancia del vector
for i in range(len(columnas)):
    #Se grafican los vectores
    plt.arrow(0, 0, xvector[i], yvector[i], color = 'black', 
              width = 0.0005, head_width = 0.02, alpha = 0.75)
    #Se colocan los nombres
    plt.text(xvector[i], yvector[i], list(columnas)[i], color='black', 
             alpha=0.75)

plt.show()

 

1 thought on “Agrupamiento KMeans – Práctica con Python”

  1. Hola Ligdi,
    me ha resultado muy instructivo poder seguir tanto el video como toda la explicación, ahora que estoy aprendiendo sobre Machine Learning.
    Tras varios análisis, probando con los 6 clústers que indicas y también con 4 y 3, me parece llegar a la conclusión de que con 3 se acabarían explicando adecuadamente los resultados.
    La conclusión final que me parece obtener es que estarían los de congelados, los de frescos, y los del resto, con lo cual mezclar productos de otras categorías no resultaría rentable mezclarlos con frescos ni congelados, pero sí con el resto.
    ¿La conclusión iría en esa línea?
    Gracias!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *