Guía definitiva de agrupamiento jerárquico con Python y Scikit-Learn

En esta guía definitiva, aprenda todo lo que necesita saber sobre el agrupamiento jerárquico de aglomeración con Python, Scikit-Learn y Pandas, con ejemplos prácticos de código, consejos y trucos de profesionales, así como PCA, DBSCAN y otras técnicas aplicadas.

Introducción

En esta guía, nos centraremos en implementar el Algoritmo de agrupación jerárquica con Scikit-Learn para resolver un problema de marketing.

Después de leer la guía, comprenderá:

  • Cuándo aplicar el agrupamiento jerárquico
  • Cómo visualizar el conjunto de datos para comprender si es apto para la agrupación
  • Cómo preprocesar características e diseñar nuevas características basadas en el conjunto de datos
  • Cómo reducir la dimensionalidad del conjunto de datos usando PCA
  • Cómo usar y leer un dendrograma para separar grupos
  • ¿Cuáles son los diferentes métodos de vinculación y métricas de distancia aplicadas a los dendrogramas y algoritmos de agrupamiento?
  • ¿Qué son las estrategias de agrupamiento aglomerante y divisivo y cómo funcionan?
  • Cómo implementar el Clustering Jerárquico Aglomerativo con Scikit-Learn
  • Cuáles son los problemas más frecuentes al trabajar con algoritmos de clustering y cómo solucionarlos

{.icon aria-hidden=“true”}

Nota: Puede descargar el cuaderno que contiene todo el código en esta guía aquí .

Motivación

Imagine un escenario en el que forma parte de un equipo de ciencia de datos que interactúa con el departamento de marketing. El departamento de marketing ha estado recopilando datos de compras de los clientes durante un tiempo y quieren comprender, en función de los datos recopilados, si hay similitudes entre los clientes. Esas similitudes dividen a los clientes en grupos y tener grupos de clientes ayuda en la orientación de campañas, promociones, conversiones y en la construcción de mejores relaciones con los clientes.

¿Hay alguna manera de ayudar a determinar qué clientes son similares? ¿Cuántos de ellos pertenecen al mismo grupo? ¿Y cuántos grupos diferentes hay?

Una forma de responder a estas preguntas es mediante el uso de un algoritmo de agrupamiento, como K-Means, DBSCAN, Hierarchical Clustering, etc. En términos generales, los algoritmos de agrupamiento encuentran similitudes entre los puntos de datos y los agrupan.

En este caso, nuestros datos de marketing son bastante pequeños. Tenemos información de sólo 200 clientes. Teniendo en cuenta el equipo de marketing, es importante que podamos explicarles claramente cómo se tomaron las decisiones en función de la cantidad de clústeres y, por lo tanto, explicarles cómo funciona realmente el algoritmo.

Dado que nuestros datos son pequeños y la explicabilidad es un factor importante, podemos aprovechar la agrupación jerárquica para resolver este problema. Este proceso también se conoce como Análisis de agrupamiento jerárquico (HCA).

Una de las ventajas de HCA es que es interpretable y funciona bien en conjuntos de datos pequeños.

Otra cosa a tener en cuenta en este escenario es que HCA es un algoritmo no supervisado. Al agrupar datos, no tendremos forma de verificar que estamos identificando correctamente que un usuario pertenece a un grupo específico (no conocemos los grupos). No hay etiquetas con las que comparar nuestros resultados. Si identificamos los grupos correctamente, el departamento de marketing lo confirmará más tarde en el día a día (medido por métricas como el ROI, las tasas de conversión, etc.).

Ahora que hemos entendido el problema que estamos tratando de resolver y cómo resolverlo, ¡podemos comenzar a echar un vistazo a nuestros datos!

Breve análisis exploratorio de datos

{.icon aria-hidden=“true”}

Nota: Puede descargar el conjunto de datos utilizado en esta guía [aquí](https://wikihtp.s3.amazonaws.com/files/hierarchical-clustering-with-python-and-scikit-learn-shopping-data .csv).

Después de descargar el conjunto de datos, observe que es un archivo CSV (valores separados por comas) llamado shopping-data.csv. Para que sea más fácil explorar y manipular los datos, los cargaremos en un DataFrame usando Pandas:

1
2
3
4
5
import pandas as pd

# Substitute the path_to_file content by the path to your shopping-data.csv file 
path_to_file = 'home/projects/datasets/shopping-data.csv'
customer_data = pd.read_csv(path_to_file)

{.icon aria-hidden=“true”}

Consejo: Si es nuevo en Pandas y DataFrames, debe leer nuestra ["Guía de Python con Pandas: tutorial de DataFrame con ejemplos"](/python-tutorial-with-pandas-dataframe-with- ejemplos/)!

Marketing dijo que había recopilado 200 registros de clientes. Podemos verificar si los datos descargados están completos con 200 filas usando el atributo shape. Nos dirá cuántas filas y columnas tenemos, respectivamente:

1
customer_data.shape

Esto resulta en:

1
(200, 5)

¡Excelente! Nuestros datos están completos con 200 filas (registros de clientes) y también tenemos 5 columnas (características). Para ver qué características ha recopilado el departamento de marketing de los clientes, podemos ver los nombres de las columnas con el atributo columns. Para hacer eso, ejecute:

1
customer_data.columns

El script anterior devuelve:

1
2
3
Index(['CustomerID', 'Genre', 'Age', 'Annual Income (k$)',
       'Spending Score (1-100)'],
      dtype='object')

Aquí, vemos que marketing generó un ‘CustomerID’, reunió el ‘Género’, ‘Edad’, ‘Ingreso anual’ (en miles de dólares) y un ‘Puntaje de gastos’ que va de 1 a 100 para cada uno de los 200 clientes. Cuando se les pidió una aclaración, dijeron que los valores en la columna “Puntuación de gasto” significan con qué frecuencia una persona gasta dinero en un centro comercial en una escala del 1 al 100. En otras palabras, si un cliente tiene una puntuación de 0, esta persona nunca gasta dinero, y si la puntuación es 100, acabamos de detectar al que más gasta.

Echemos un vistazo rápido a la distribución de este puntaje para inspeccionar los hábitos de gasto de los usuarios en nuestro conjunto de datos. Ahí es donde entra en juego el método hist() de Pandas para ayudar:

1
customer_data['Spending Score (1-100)'].hist()

img

Al observar el histograma, vemos que más de 35 clientes tienen puntajes entre 40 y 60, luego menos de 25 tienen puntajes entre 70 y 80. Por lo tanto, la mayoría de nuestros clientes son gastadores equilibrados, seguidos de los que gastan de moderado a alto. También podemos ver que hay una línea después del 0, a la izquierda de la distribución, y otra línea antes del 100, a la derecha de la distribución. Estos espacios en blanco probablemente significan que la distribución no contiene personas que no gastan, lo que tendría una puntuación de 0, y que tampoco hay personas que gastan mucho con una puntuación de 100.

img

Para verificar si eso es cierto, podemos mirar los valores mínimo y máximo de la distribución. Esos valores se pueden encontrar fácilmente como parte de las estadísticas descriptivas, por lo que podemos usar el método describe() para comprender otras distribuciones de valores numéricos:

1
2
# transpose() transposes the table, making it easier for us to compare values
customer_data.describe().transpose()

Esto nos dará una tabla desde donde podemos leer las distribuciones de otros valores de nuestro conjunto de datos:

1
2
3
4
5
                        count   mean    std         min     25%     50%     75%     max
CustomerID              200.0   100.50  57.879185   1.0     50.75   100.5   150.25  200.0
Age                     200.0   38.85   13.969007   18.0    28.75   36.0    49.00   70.0
Annual Income (k$)      200.0   60.56   26.264721   15.0    41.50   61.5    78.00   137.0
Spending Score (1-100)  200.0   50.20   25.823522   1.0     34.75   50.0    73.00   99.0

Nuestra hipótesis se confirma. El valor “mínimo” de la “puntuación de gastos” es “1” y el máximo es “99”. Así que no tenemos “0” o “100” gastadores de puntuación. Entonces echemos un vistazo a las otras columnas de la tabla describir transpuesta. Al observar las columnas media y std, podemos ver que para Age la media es 38,85 y la std es aproximadamente 13,97. Lo mismo sucede con Ingreso Anual, con media de 60.56 y std 26.26, y Spending Score con media de 50 y std de 25.82. Para todas las funciones, la “media” está lejos de la desviación estándar, lo que indica que nuestros datos tienen una alta variabilidad.

Para comprender mejor cómo varían nuestros datos, tracemos la distribución de ‘Ingresos anuales’:

1
customer_data['Annual Income (k$)'].hist()

Lo cual nos dará:

img

Observe en el histograma que la mayoría de nuestros datos, más de 35 clientes, se concentran cerca del número ‘60’, en nuestra ‘media’, en el eje horizontal. Pero, ¿qué sucede a medida que avanzamos hacia los extremos de la distribución? Yendo hacia la izquierda, desde la media de $60.560, el siguiente valor que encontraremos es $34.300 - la media ($60.560) menos la variación estándar ($26.260). Si nos alejamos más a la izquierda de nuestra distribución de datos, se aplica una regla similar, restamos la variación estándar ($26.260) del valor actual ($34.300). Por lo tanto, encontraremos un valor de $8.040. Observe cómo nuestros datos pasaron de $60k a $8k rápidamente. Está "saltando" $26.260 cada vez, variando mucho, y es por eso que tenemos una variabilidad tan alta.

img

La variabilidad y el tamaño de los datos son importantes en el análisis de conglomerados porque las mediciones de distancia de la mayoría de los algoritmos de conglomerados son sensibles a las magnitudes de los datos. La diferencia de tamaño puede cambiar los resultados de la agrupación al hacer que un punto parezca más cercano o más distante de otro de lo que realmente es, distorsionando la agrupación real de datos.

Hasta ahora, hemos visto la forma de nuestros datos, algunas de sus distribuciones y estadísticas descriptivas. Con Pandas, también podemos enumerar nuestros tipos de datos y ver si todas nuestras 200 filas están llenas o tienen algunos valores “nulos”:

1
customer_data.info()

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<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

Aquí, podemos ver que no hay valores nulos en los datos y que solo tenemos una columna categórica: Género. En esta etapa, es importante que tengamos en cuenta qué características parecen interesantes para agregar al modelo de agrupamiento. Si queremos agregar la columna Género a nuestro modelo, necesitaremos transformar sus valores de categórico a numérico.

Veamos cómo se llena Género echando un vistazo rápido a los primeros 5 valores de nuestros datos:

1
customer_data.head() 

Esto resulta en:

1
2
3
4
5
6
    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

Parece que solo tiene categorías Female y Masculino. Podemos estar seguros de eso echando un vistazo a sus valores únicos con unique:

1
customer_data['Genre'].unique()

Esto confirma nuestra suposición:

1
array(['Male', 'Female'], dtype=object)

Hasta ahora, sabemos que solo tenemos dos géneros, si planeamos usar esta característica en nuestro modelo, ‘Masculino’ podría transformarse en ‘0’ y ‘Femenino’ en ‘1’. También es importante comprobar la proporción entre géneros, para ver si están equilibrados. Podemos hacerlo con el método value_counts() y su argumento normalize=True para mostrar el porcentaje entre Masculino y Femenino:

1
customer_data['Genre'].value_counts(normalize=True)

Esto da como resultado:

1
2
3
Female    0.56
Male      0.44
Name: Genre, dtype: float64

Tenemos un 56 % de mujeres en el conjunto de datos y un 44 % de hombres. La diferencia entre ellos es solo del 16%, y nuestros datos no son 50/50 pero están lo suficientemente equilibrados para no causar ningún problema. Si los resultados fueron 70/30, 60/40, entonces podría haber sido necesario recopilar más datos o emplear algún tipo de técnica de aumento de datos para equilibrar esa proporción.

Hasta ahora, se han explorado brevemente todas las funciones menos Edad. En lo que respecta a Edad, suele ser interesante dividirlo en bins para poder segmentar a los clientes en función de sus grupos de edad. Si hacemos eso, necesitaríamos transformar las categorías de edad en un número antes de agregarlas a nuestro modelo. De esa forma, en lugar de usar la categoría 15-20 años, contaríamos cuántos clientes hay en la categoría ‘15-20’, y ese sería un número en una nueva columna llamada ‘15-20’.

{.icon aria-hidden=“true”}

Consejo: En esta guía, presentamos solo un breve análisis exploratorio de datos. Pero puedes ir más allá y debes ir más allá. Puede ver si hay diferencias de ingresos y diferencias de puntuación según el género y la edad. Esto no solo enriquece el análisis, sino que conduce a mejores resultados del modelo. Para profundizar en el Análisis Exploratorio de Datos, consulta el Proyecto Guiado Capítulo EDA en "Predicción práctica del precio de la vivienda: aprendizaje automático en Python".

Después de conjeturar sobre lo que se podría hacer con las columnas categóricas - o categóricas - Género y Edad, apliquemos lo que se ha discutido.

Variables de codificación e ingeniería de funciones {#variables de codificación e ingeniería de funciones}

Comencemos por dividir la Edad en grupos que varían en 10, de modo que tengamos 20-30, 30-40, 40-50, etc. Dado que nuestro cliente más joven tiene 15 años, podemos comenzar a los 15 y terminar a los 70, que es la edad del cliente de mayor edad en los datos. Comenzando en 15 y terminando en 70, tendríamos intervalos de 15-20, 20-30, 30-40, 40-50, 50-60 y 60-70.

Para agrupar o bin valores Edad en estos intervalos, podemos usar el método cut() de Pandas para dividirlos en intervalos y luego asignar los intervalos a una nueva columna Grupos de edad:

1
2
3
4
5
6
intervals = [15, 20, 30, 40, 50, 60, 70]
col = customer_data['Age']
customer_data['Age Groups'] = pd.cut(x=col, bins=intervals)

# To be able to look at the result stored in the variable
customer_data['Age Groups'] 

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
0      (15, 20]
1      (20, 30]
2      (15, 20]
3      (20, 30]
4      (30, 40]
         ...   
195    (30, 40]
196    (40, 50]
197    (30, 40]
198    (30, 40]
199    (20, 30]
Name: Age Groups, Length: 200, dtype: category
Categories (6, interval[int64, right]): [(15, 20] < (20, 30] < (30, 40] < (40, 50] < (50, 60] < (60, 70]]

Tenga en cuenta que al mirar los valores de la columna, también hay una línea que especifica que tenemos 6 categorías y muestra todos los intervalos de datos agrupados. De esta manera, hemos categorizado nuestros datos numéricos anteriores y hemos creado una nueva función de Grupos de edad.

¿Y cuántos clientes tenemos en cada categoría? Podemos saberlo rápidamente agrupando la columna y contando los valores con groupby() y count():

1
customer_data.groupby('Age Groups')['Age Groups'].count()

Esto resulta en:

1
2
3
4
5
6
7
8
Age Groups
(15, 20]    17
(20, 30]    45
(30, 40]    60
(40, 50]    38
(50, 60]    23
(60, 70]    17
Name: Age Groups, dtype: int64

Es fácil detectar que la mayoría de los clientes tienen entre 30 y 40 años, seguidos por los clientes entre 20 y 30 y luego los clientes entre 40 y 50. Esta también es una buena información para el departamento de Marketing.

Por el momento, tenemos dos variables categóricas, ‘Edad’ y ‘Género’, que necesitamos transformar en números para poder usar en nuestro modelo. Hay muchas formas diferentes de hacer esa transformación: usaremos el método get_dummies() de Pandas que crea una nueva columna para cada intervalo y género y luego completa sus valores con 0 y 1; este tipo de operación se llama * uno- codificación en caliente*. Vamos a ver cómo se ve:

1
2
3
4
# The _oh means one-hot
customer_data_oh = pd.get_dummies(customer_data)
# Display the one-hot encoded dataframe
customer_data_oh 

Esto nos dará una vista previa de la tabla resultante:

img

Con el resultado, es fácil ver que la columna ‘Género’ se dividió en columnas: ‘Género_Femenino’ y ‘Género_Masculino’. Cuando el cliente es mujer, Genre_Female es igual a 1, y cuando el cliente es hombre, es igual a 0.

{.icon aria-hidden=“true”}

Consejo: Si desea leer más sobre la codificación One-Hot (a veces también conocida como codificación categórica), lea nuestra "Codificación One-Hot en Python con Pandas y Scikit-Learn"!

Además, la columna ‘Grupos de edad’ se dividió en 6 columnas, una para cada intervalo, como ‘Grupos de edad_(15, 20]’, ‘Grupos de edad_(20, 30]’, etc. De la misma manera que Género, cuando el cliente tiene 18 años, el valor Grupos de edad_(15, 20] es 1 y el valor de todas las demás columnas es 0.

La ventaja de la codificación one-hot es la simplicidad en la representación de los valores de las columnas, es fácil de entender lo que está sucediendo, mientras que la desventaja es que ahora hemos creado 8 columnas adicionales, para resumir con las columnas que ya tenido.

{.icon aria-hidden=“true”}

Advertencia: si tiene un conjunto de datos en el que la cantidad de columnas codificadas en caliente excede la cantidad de filas, es mejor emplear otro método de codificación para evitar problemas de dimensionalidad de los datos.

La codificación one-hot también agrega ceros a nuestros datos, haciéndolos más escasos, lo que puede ser un problema para algunos algoritmos que son sensibles a la escasez de datos.

Para nuestras necesidades de agrupamiento, la codificación one-hot parece funcionar. Pero podemos graficar los datos para ver si realmente hay grupos distintos para agrupar.

Trazado básico y reducción de dimensionalidad {#trazado básico y reducción de dimensionalidad}

Nuestro conjunto de datos tiene 11 columnas, y hay algunas formas en las que podemos visualizar esos datos. El primero es trazarlo en 10 dimensiones (buena suerte con eso). Diez porque no se está considerando la columna Customer_ID. La segunda es trazando nuestras características numéricas iniciales, y la tercera es transformando nuestras 10 características en 2, por lo tanto, realizando una reducción de dimensionalidad.

Trazado de cada par de datos {#trazado de cada par de datos}

Dado que trazar 10 dimensiones es un poco imposible, optaremos por el segundo enfoque: trazaremos nuestras características iniciales. Podemos elegir dos de ellos para nuestro análisis de agrupamiento. Una forma en que podemos ver todos nuestros pares de datos combinados es con un pairplot() de Seaborn:

1
2
3
4
5
6
import seaborn as sns

# Dropping CustomerID column from data 
customer_data = customer_data.drop('CustomerID', axis=1)

sns.pairplot(customer_data)

Que muestra:

img

De un vistazo, podemos detectar los diagramas de dispersión que parecen tener grupos de datos. Uno que parece interesante es el diagrama de dispersión que combina ‘Ingreso anual’ y ‘Puntuación de gasto’. Observe que no hay una separación clara entre otros diagramas de dispersión de variables. A lo sumo, tal vez podamos decir que hay dos concentraciones distintas de puntos en el diagrama de dispersión “Puntuación de gasto” frente a “Edad”.

Ambos diagramas de dispersión que consisten en “Ingresos anuales” y “Puntuación de gastos” son esencialmente iguales. Podemos verlo dos veces porque se intercambiaron los ejes x e y. Al echar un vistazo a cualquiera de ellos, podemos ver lo que parecen ser cinco grupos diferentes. Grafiquemos solo esas dos características con un scatterplot() de Seaborn para echar un vistazo más de cerca:

1
2
sns.scatterplot(x=customer_data['Annual Income (k$)'],
                y=customer_data['Spending Score (1-100)'])

img

Al mirar más de cerca, definitivamente podemos distinguir 5 grupos diferentes de datos. Parece que nuestros clientes pueden agruparse en función de cuánto ganan en un año y cuánto gastan. Este es otro punto relevante en nuestro análisis. Es importante que solo estemos tomando en cuenta dos características para agrupar a nuestros clientes. Cualquier otra información que tengamos sobre ellos no entra en la ecuación. Esto le da sentido al análisis: si sabemos cuánto gana y gasta un cliente, podemos encontrar fácilmente las similitudes que necesitamos.

img

¡Eso es genial! Hasta ahora, ya tenemos dos variables para construir nuestro modelo. Además de lo que esto representa, también hace que el modelo sea más simple, parsimonioso y más explicable.

{.icon aria-hidden=“true”}

Nota: La ciencia de datos suele favorecer los enfoques más simples posibles. No solo porque es más fácil de explicar para el negocio, sino también porque es más directo: con 2 funciones y un modelo explicable, queda claro qué está haciendo el modelo y cómo funciona.

Trazado de datos después de usar PCA

Parece que nuestro segundo enfoque es probablemente el mejor, pero también echemos un vistazo a nuestro tercer enfoque. Puede ser útil cuando no podemos graficar los datos porque tienen demasiadas dimensiones, o cuando no hay concentraciones de datos o separación clara en grupos. Cuando ocurren esas situaciones, se recomienda intentar reducir las dimensiones de los datos con un método llamado Análisis de componentes principales (PCA).

{.icon aria-hidden=“true”}

Nota: La mayoría de las personas usan PCA para reducir la dimensionalidad antes de la visualización. Existen otros métodos que ayudan en la visualización de datos antes de la agrupación, como Agrupación espacial basada en la densidad de aplicaciones con ruido (DBSCAN) y la agrupación en clústeres de Self-Organizing Maps (SOM). Ambos son algoritmos de agrupamiento, pero también se pueden usar para la visualización de datos. Dado que el análisis de agrupamiento no tiene un estándar de oro, es importante comparar diferentes visualizaciones y diferentes algoritmos.

PCA reducirá las dimensiones de nuestros datos mientras intenta preservar la mayor cantidad de información posible. Primero, tengamos una idea de cómo funciona PCA, y luego podemos elegir a cuántas dimensiones de datos reduciremos nuestros datos.

Para cada par de características, PCA ve si los valores mayores de una variable se corresponden con los valores mayores de la otra variable, y hace lo mismo con los valores menores. Entonces, básicamente calcula cuánto varían los valores de las características entre sí; lo llamamos su covarianza. Esos resultados luego se organizan en una matriz, obteniendo una matriz de covarianza.

Después de obtener la matriz de covarianza, PCA intenta encontrar una combinación lineal de características que la explique mejor: ajusta los modelos lineales hasta que identifica el que explica la máxima cantidad de varianza.

{.icon aria-hidden=“true”}

Nota: PCA es una transformación lineal y la linealidad es sensible a la escala de los datos. Por lo tanto, PCA funciona mejor cuando todos los valores de los datos están en la misma escala. Esto se puede hacer restando la columna media de sus valores y dividiendo el resultado por su desviación estándar. Eso se llama estandarización de datos. ¡Antes de usar PCA, asegúrese de que los datos estén escalados! Si no está seguro de cómo, lea nuestras ["Características de escalado de datos con Scikit-Learn para aprendizaje automático en Python"](/caracteristicas-de-escalado-de-datos-con-scikit-learn-para -el-aprendizaje-automatico-en-python/)!

Con la mejor línea (combinación lineal) encontrada, PCA obtiene las direcciones de sus ejes, llamados vectores propios, y sus coeficientes lineales, los valores propios. La combinación de los vectores propios y los valores propios, o las direcciones de los ejes y los coeficientes, son los componentes principales de PCA. Y ahí es cuando podemos elegir nuestro número de dimensiones en función de la varianza explicada de cada característica, al comprender qué componentes principales queremos conservar o descartar en función de cuánta varianza explican.

Después de obtener los componentes principales, PCA usa los vectores propios para formar un vector de características que reorienta los datos de los ejes originales a los representados por los componentes principales; así es como se reducen las dimensiones de los datos.

{.icon aria-hidden=“true”}

Nota: Un detalle importante a tener en cuenta aquí es que, debido a su naturaleza lineal, PCA concentrará la mayor parte de la varianza explicada en los primeros componentes principales. Entonces, al observar la varianza explicada, por lo general nuestros primeros dos componentes serán suficientes. Pero eso puede ser engañoso en algunos casos, así que trate de seguir comparando diferentes gráficos y algoritmos al agrupar para ver si tienen resultados similares.

Antes de aplicar PCA, debemos elegir entre la columna Edad o las columnas Grupos de edad en nuestros datos codificados previamente en caliente. Dado que ambas columnas representan la misma información, introducirla dos veces afecta la varianza de nuestros datos. Si se elige la columna Grupos de edad, simplemente elimine la columna Edad usando el método drop() de Pandas y reasígnela a la variable customer_data_oh:

1
2
customer_data_oh = customer_data_oh.drop(['Age'], axis=1)
customer_data_oh.shape # (200, 10)

Ahora nuestros datos tienen 10 columnas, lo que significa que podemos obtener un componente principal por columna y elegir cuántos de ellos usaremos midiendo cuánto la introducción de una nueva dimensión explica más la varianza de nuestros datos.

Hagámoslo con Scikit-Learn PCA. Calcularemos la varianza explicada de cada dimensión, dada por explained_variance_ratio_, y luego veremos su suma acumulada con cumsum():

1
2
3
4
5
from sklearn.decomposition import PCA

pca = PCA(n_components=10)
pca.fit_transform(customer_data_oh)
pca.explained_variance_ratio_.cumsum()

Nuestras variaciones acumuladas explicadas son:

1
2
array([0.509337  , 0.99909504, 0.99946364, 0.99965506, 0.99977937,
       0.99986848, 0.99993716, 1.        , 1.        , 1.        ])

Podemos ver que la primera dimensión explica el 50% de los datos, y cuando se combinan con la segunda dimensión, explican el 99% por ciento. Esto significa que las 2 primeras dimensiones ya explican el 99% de nuestros datos. Entonces podemos aplicar un PCA con 2 componentes, obtener nuestros componentes principales y graficarlos:

1
2
3
4
5
6
7
8
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
pcs = pca.fit_transform(customer_data_oh)

pc1_values = pcs[:,0]
pc2_values = pcs[:,1]
sns.scatterplot(x=pc1_values, y=pc2_values)

img

La gráfica de datos después de PCA es muy similar a la gráfica que usa solo dos columnas de datos sin PCA. Observe que los puntos que forman grupos están más cerca y un poco más concentrados después del PCA que antes.

img

{.icon aria-hidden=“true”}

Consejo: Para ver otras aplicaciones de PCA, echa un vistazo a la "Implementación de PCA en Python con Scikit-Learn" guía.

Visualización de la estructura jerárquica con dendogramas {#visualización de la estructura jerárquica con dendogramas}

Hasta ahora, hemos explorado los datos, las columnas categóricas codificadas en caliente, decidido qué columnas eran aptas para el agrupamiento y reducido la dimensionalidad de los datos. Los gráficos indican que tenemos 5 grupos en nuestros datos, pero también hay otra forma de visualizar las relaciones entre nuestros puntos y ayudar a determinar la cantidad de grupos: creando un dendrograma (comúnmente mal escrito como dendograma). Dendro significa árbol en latín.

El dendrograma es el resultado de la vinculación de puntos en un conjunto de datos. Es una representación visual del proceso de agrupación jerárquica. ¿Y cómo funciona el proceso de agrupamiento jerárquico? Bueno... depende - probablemente una respuesta que ya hayas escuchado mucho en Data Science.

Comprender la agrupación jerárquica

Cuando el Algoritmo de agrupamiento jerárquico (HCA) comienza a vincular los puntos y encontrar agrupamientos, primero puede dividir los puntos en 2 grupos grandes y luego dividir cada uno de esos dos grupos en 2 grupos más pequeños, con 4 grupos en total, que es el enfoque divisivo y de arriba hacia abajo.

Alternativamente, puede hacer lo contrario: puede mirar todos los puntos de datos, encontrar 2 puntos que estén más cerca entre sí, vincularlos y luego encontrar otros puntos que sean los más cercanos a esos puntos vinculados y seguir construyendo los 2 grupos. de abajo hacia arriba**. Cuál es el enfoque aglomerativo que desarrollaremos.

Pasos para realizar el agrupamiento jerárquico aglomerativo

Para que el enfoque aglomerativo sea aún más claro, hay pasos del algoritmo * Agglomerative Hierarchical Clustering (AHC) *:

  1. Al principio, trate cada punto de datos como un grupo. Por lo tanto, la cantidad de grupos al principio será K, mientras que K es un número entero que representa la cantidad de puntos de datos.
  2. Forme un grupo uniendo los dos puntos de datos más cercanos que dan como resultado grupos K-1.
  3. Forme más grupos uniendo los dos grupos más cercanos, lo que da como resultado grupos K-2.
  4. Repita los tres pasos anteriores hasta que se forme un grupo grande.

{.icon aria-hidden=“true”}

Nota: Para simplificar, decimos "dos puntos de datos más cercanos" en los pasos 2 y 3. Pero hay más formas de vincular puntos, como veremos en un momento.

Si invierte los pasos del algoritmo ACH, pasando de 4 a 1, esos serían los pasos para *Clustering jerárquico divisivo (DHC)*.

Tenga en cuenta que las HCA pueden ser divisivas y de arriba hacia abajo, o aglomerativas y de abajo hacia arriba. El enfoque de DHC de arriba hacia abajo funciona mejor cuando tiene menos clústeres, pero más grandes, por lo tanto, es más costoso desde el punto de vista computacional. Por otro lado, el enfoque de AHC de abajo hacia arriba es adecuado cuando tiene muchos clústeres más pequeños. Es computacionalmente más simple, más utilizado y más disponible.

{.icon aria-hidden=“true”}

Nota: Ya sea de arriba hacia abajo o de abajo hacia arriba, la representación del dendrograma del proceso de agrupamiento siempre comenzará con una división en dos y terminará con cada punto individual discriminado, una vez que su estructura subyacente sea la de un árbol binario.

Tracemos nuestro dendrograma de datos de clientes para visualizar las relaciones jerárquicas de los datos. Esta vez, usaremos la biblioteca scipy para crear el dendrograma para nuestro conjunto de datos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import scipy.cluster.hierarchy as shc
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 7))
plt.title("Customers Dendrogram")

# Selecting Annual Income and Spending Scores by index
selected_data = customer_data_oh.iloc[:, 1:3]
clusters = shc.linkage(selected_data, 
            method='ward', 
            metric="euclidean")
shc.dendrogram(Z=clusters)
plt.show()

La salida del script se ve así:

img

En el script anterior, generamos los grupos y subgrupos con nuestros puntos, definimos cómo se vincularían nuestros puntos (aplicando el método ward) y cómo medir la distancia entre puntos (usando la métrica euclidiana ).

Con el gráfico del dendrograma, se pueden visualizar los procesos descritos de DHC y AHC. Para visualizar el enfoque de arriba hacia abajo, comience desde la parte superior del dendrograma y baje, y haga lo contrario, comenzando hacia abajo y moviéndose hacia arriba para visualizar el enfoque de abajo hacia arriba.

Métodos de enlace

Hay muchos otros métodos de vinculación, al comprender más sobre cómo funcionan, podrá elegir el adecuado para sus necesidades. Además de eso, cada uno de ellos dará resultados diferentes cuando se apliquen. No hay una regla fija en el análisis de conglomerados, si es posible, estudie la naturaleza del problema para ver cuál se adapta mejor, pruebe diferentes métodos e inspeccione los resultados.

Algunos de los métodos de vinculación son:

  • Enlace único: también conocido como Vecino más cercano (NN). La distancia entre clústeres se define por la distancia entre sus miembros más cercanos.

img

  • Enlace completo: también conocido como Vecino más lejano (FN), Algoritmo del punto más lejano o Algoritmo Voor Hees. La distancia entre grupos se define por la distancia entre sus miembros más lejanos. Este método es computacionalmente costoso.

img

  • Enlace promedio: también conocido como UPGMA (Método de grupos de pares no ponderados con media aritmética). El porcentaje del número de puntos de cada clúster se calcula con respecto al número de puntos de los dos clústeres si estuvieran fusionados.

img

  • Enlace ponderado: también conocido como WPGMA (Método de grupo de pares ponderados con media aritmética). Los puntos individuales de los dos grupos contribuyen a la distancia agregada entre un grupo más pequeño y uno más grande.
  • Enlace centroide: también conocido como UPGMC (Método de grupos de pares no ponderados usando centroides). Se calcula un punto definido por la media de todos los puntos (centroide) para cada grupo y la distancia entre grupos es la distancia entre sus respectivos centroides.

img

  • Enlace de barrio: también conocido como MISSQ (Aumento mínimo de suma de cuadrados). Especifica la distancia entre dos grupos, calcula el error de la suma de los cuadrados (ESS) y elige sucesivamente los siguientes grupos en función del ESS más pequeño. El método de Ward busca minimizar el aumento de ESS en cada paso. Por lo tanto, minimizar el error.

img

Métricas de distancia

Además de la vinculación, también podemos especificar algunas de las métricas de distancia más utilizadas:

  • Euclidiana: también conocida como distancia pitagórica o en línea recta. Calcula la distancia entre dos puntos en el espacio, midiendo la longitud de un segmento de línea que pasa entre ellos. Utiliza el teorema de Pitágoras y el valor de la distancia es el resultado (c) de la ecuación:

$$
c^2 = a^2 + b^2
$$

  • Manhattan: también llamado City-block, Taxicab distancia. Es la suma de las diferencias absolutas entre las medidas en todas las dimensiones de dos puntos. Si esas dimensiones son dos, es similar a girar a la derecha y luego a la izquierda al caminar una cuadra.

img

  • Minkowski: es una generalización de las distancias Euclidiana y Manhattan. Es una forma de calcular distancias en base a las diferencias absolutas al orden de la métrica de Minkowski p. Aunque se define para cualquier p > 0, rara vez se usa para valores distintos de 1, 2 y ∞ (infinito). La distancia de Minkowski es la misma que la distancia de Manhattan cuando p=1, y la misma que la distancia euclidiana cuando p=2.

$$
D\left(X,Y\right) = \left(\sum_{i=1}^n |x_i-y_i|^p\right)^{\frac {1}{p}}
$$

img

  • Chebyshev: también conocida como distancia Tablero de ajedrez. Es el caso extremo de la distancia de Minkowski. Cuando usamos infinito como el valor del parámetro p (p = ∞), terminamos con una métrica que define la distancia como la máxima diferencia absoluta entre coordenadas.
  • Coseno: es la distancia coseno angular entre dos sucesiones de puntos, o vectores. La similitud del coseno es el producto escalar de los vectores dividido por el producto de sus longitudes.
  • Jaccard: mide la similitud entre conjuntos finitos de puntos. Se define como el número total de puntos (cardinalidad) en los puntos comunes de cada conjunto (intersección), dividido por el número total de puntos (cardinalidad) del total de puntos de ambos conjuntos (unión).
  • Jensen-Shannon: basado en la divergencia Kullback-Leibler. Considera las distribuciones de probabilidad de los puntos y mide la similitud entre esas distribuciones. Es un método popular de teoría de probabilidad y estadística.

{.icon aria-hidden=“true”}

Nota: Para obtener una lista completa de los enlaces disponibles, visite [Documentación de Scipy sobre enlaces](https://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html ?highlight=linkage#scipy.cluster.hierarchy.linkage).
Además, para obtener una lista completa de las métricas disponibles y para qué se utilizan, visite la [Documentación de distancia de punto SciPy](https://docs.scipy.org/doc/scipy/reference/generated/scipy. espacial.distancia.pdist.html?highlight=pdist#scipy.spatial.distance.pdist).

Hemos elegido Ward y Euclidiana para el dendrograma porque son el método y la métrica más utilizados. Suelen dar buenos resultados ya que Ward enlaza puntos basados ​​en minimizar los errores, y Euclidean funciona bien en dimensiones más bajas.

En este ejemplo, estamos trabajando con dos características (columnas) de los datos de marketing y 200 observaciones o filas. Dado que el número de observaciones es mayor que el número de características (200 > 2), estamos trabajando en un espacio de baja dimensión.

Cuando el número de características (f) es mayor que el número de observaciones (N) - mayormente escrito como f >> N, significa que tenemos un espacio dimensional alto.

Si tuviéramos que incluir más atributos, por lo que tenemos más de 200 características, la distancia euclidiana podría no funcionar muy bien, ya que tendría dificultades para medir todas las distancias pequeñas en un espacio muy grande que solo se hace más grande. En otras palabras, el enfoque de distancia euclidiana tiene dificultades para trabajar con la escasez de datos. Este es un problema que se llama la maldición de la dimensionalidad. Los valores de distancia se volverían tan pequeños, como si se “diluyeran” en el espacio más grande, distorsionados hasta convertirse en 0.

{.icon aria-hidden=“true”}

Nota: Si alguna vez encuentra un conjunto de datos con f >> p, probablemente usará otras métricas de distancia, como la distancia de Mahalanobis. Como alternativa, también puede reducir las dimensiones del conjunto de datos mediante Análisis de componentes principales (PCA). Este problema es frecuente, especialmente cuando se agrupan datos de secuenciación biológica.

Ya hemos discutido las métricas, los vínculos y cómo cada uno de ellos puede afectar nuestros resultados. Continuemos ahora con el análisis del dendrograma y veamos cómo nos puede dar una indicación de la cantidad de grupos en nuestro conjunto de datos.

Encontrar un número interesante de conglomerados en un dendrograma es lo mismo que encontrar el espacio horizontal más grande que no tiene líneas verticales (el espacio con las líneas verticales más largas). Esto significa que hay más separación entre los grupos.

Podemos dibujar una línea horizontal que pase por esa distancia más larga:

1
2
3
4
5
6
7
plt.figure(figsize=(10, 7))
plt.title("Customers Dendogram with line")
clusters = shc.linkage(selected_data, 
            method='ward', 
            metric="euclidean")
shc.dendrogram(clusters)
plt.axhline(y = 125, color = 'r', linestyle = '-')

img

Después de ubicar la línea horizontal, contamos cuántas veces cruzó nuestras líneas verticales; en este ejemplo, 5 veces. Entonces, 5 parece una buena indicación de la cantidad de grupos que tienen la mayor distancia entre ellos.

{.icon aria-hidden=“true”}

Nota: El dendrograma debe considerarse solo como una referencia cuando se utiliza para elegir el número de conglomerados. Puede alejarse fácilmente de ese número y está completamente influenciado por el tipo de vinculación y las métricas de distancia. Al realizar un análisis de conglomerados en profundidad, se recomienda observar los dendogramas con diferentes vínculos y métricas y observar los resultados generados con las primeras tres líneas en las que los conglomerados tienen la mayor distancia entre ellos.

Implementación de un clúster jerárquico aglomerativo

Uso de datos originales

Hasta ahora, hemos calculado la cantidad sugerida de grupos para nuestro conjunto de datos que corrobora con nuestro análisis inicial y nuestro análisis PCA. Ahora podemos crear nuestro modelo de agrupamiento jerárquico aglomerativo usando Scikit-Learn AgglomerativeClustering y encontrar las etiquetas de los puntos de marketing con labels_:

1
2
3
4
5
from sklearn.cluster import AgglomerativeClustering

clustering_model = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')
clustering_model.fit(selected_data)
clustering_model.labels_

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
array([4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3,
       4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 1,
       4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 0, 2, 0, 2,
       1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2,
       0, 2, 0, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
       0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
       0, 2])

Hemos investigado mucho para llegar a este punto. ¿Y qué significan estas etiquetas? Aquí, tenemos cada punto de nuestros datos etiquetados como un grupo del 0 al 4:

1
2
3
4
5
6
data_labels = clustering_model.labels_
sns.scatterplot(x='Annual Income (k$)', 
                y='Spending Score (1-100)', 
                data=selected_data, 
                hue=data_labels,
                pallete="rainbow").set_title('Labeled Customer Data')

img

Estos son nuestros datos agrupados finales. Puede ver los puntos de datos codificados por colores en forma de cinco grupos.

Los puntos de datos en la parte inferior derecha (etiqueta: 0, puntos de datos morados) pertenecen a los clientes con salarios altos pero gastos bajos. Estos son los clientes que gastan su dinero con cuidado.

De manera similar, los clientes en la parte superior derecha (etiqueta: 2, puntos de datos verdes), son los clientes con salarios altos y gastos elevados. Estos son el tipo de clientes a los que se dirigen las empresas.

Los clientes en el medio (etiqueta: 1, puntos de datos azules) son los que tienen ingresos y gastos promedio. El mayor número de clientes pertenecen a esta categoría. Las empresas también pueden dirigirse a estos clientes dado que se encuentran en grandes cantidades.

Los clientes en la parte inferior izquierda (etiqueta: 4, rojo) son los clientes que tienen salarios bajos y gastos bajos, podrían atraerse ofreciendo promociones.

Y finalmente, los clientes en la parte superior izquierda (etiqueta: 3, puntos de datos naranjas) son los que tienen ingresos altos y gastos bajos, a los que el marketing se dirige idealmente.

Uso del resultado de PCA

Si estuviéramos en un escenario diferente, en el que tuviéramos que reducir la dimensionalidad de los datos. También podríamos trazar fácilmente los resultados de PCA agrupados. Eso se puede hacer creando otro modelo de agrupamiento aglomerativo y obteniendo una etiqueta de datos para cada componente principal:

1
2
3
4
5
6
7
8
9
clustering_model_pca = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')
clustering_model_pca.fit(pcs)

data_labels_pca = clustering_model_pca.labels_

sns.scatterplot(x=pc1_values, 
                y=pc2_values,
                hue=data_labels_pca,
                palette="rainbow").set_title('Labeled Customer Data Reduced with PCA')

img

Observa que ambos resultados son muy similares. La principal diferencia es que el primer resultado con los datos originales es mucho más fácil de explicar. Es evidente que los clientes se pueden dividir en cinco grupos según su puntuación anual de ingresos y gastos. Si bien, en el enfoque de PCA, tomamos en consideración todas nuestras características, tanto como podemos observar la variación explicada por cada una de ellas, este es un concepto más difícil de comprender, especialmente cuando se informa a un departamento de marketing.

Cuanto menos tengamos que transformar nuestros datos, mejor.

Si tiene un conjunto de datos muy grande y complejo en el que debe realizar una reducción de dimensionalidad antes del agrupamiento, intente analizar las relaciones lineales entre cada una de las características y sus residuos para respaldar el uso de PCA y mejorar la explicabilidad del proceso. Al hacer un modelo lineal por par de características, podrá comprender cómo interactúan las características.

Si el volumen de datos es tan grande, se vuelve imposible trazar los pares de características, seleccione una muestra de sus datos, lo más equilibrada y cercana posible a la distribución normal y realice el análisis en la muestra primero, entiéndala, ajuste y aplicarlo más tarde a todo el conjunto de datos.

Siempre puede elegir diferentes técnicas de visualización de agrupamiento según la naturaleza de sus datos (lineal, no lineal) y combinarlas o probarlas todas si es necesario.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Predicción práctica del precio de la vivienda: aprendizaje automático en Python"](https://wikihtp.com/courses/hands-on-house- precio-predicción-aprendizaje-máquina-en-python/#cta){target="_blank"}.

[](https://wikihtp.com/ cursos/predicción-de-precio-de-la-casa-práctica-aprendizaje-de-máquina-en-python/#cta)

En este proyecto guiado, aprenderá a crear potentes modelos tradicionales de aprendizaje automático, así como modelos de aprendizaje profundo, utilizar Ensemble Learning y capacitar a los meta-aprendices para predecir los precios de la vivienda a partir de una bolsa de modelos Scikit-Learn y Keras.

Con Keras, la API de aprendizaje profundo creada sobre Tensorflow, experimentaremos con arquitecturas, crearemos un conjunto de modelos apilados y entrenaremos una red neuronal meta-aprendizaje (modelo de nivel 1) para calcular el precio de un casa.

El aprendizaje profundo es sorprendente, pero antes de recurrir a él, se recomienda intentar resolver el problema con técnicas más simples, como los algoritmos de aprendizaje superficial. Nuestro rendimiento de referencia se basará en un algoritmo de Regresión de bosque aleatorio. Además, exploraremos la creación de conjuntos de modelos a través de Scikit-Learn a través de técnicas como embalaje y votación.

Este es un proyecto integral y, como todos los proyectos de aprendizaje automático, comenzaremos con Análisis exploratorio de datos, seguido de Preprocesamiento de datos y, finalmente, Creación de modelos superficiales y Aprendizaje profundo para ajustarse a los datos que hemos explorado y limpiado previamente.

Conclusión

La técnica de agrupamiento puede ser muy útil cuando se trata de datos sin etiquetar. Dado que la mayoría de los datos en el mundo real no están etiquetados y anotar los datos tiene costos más altos, se pueden usar técnicas de agrupación para etiquetar datos no etiquetados.

En esta guía, hemos planteado un problema real de ciencia de datos, ya que las técnicas de agrupación se utilizan en gran medida en el análisis de marketing (y también en el análisis biológico). También explicamos muchos de los pasos de investigación para llegar a un buen modelo de agrupamiento jerárquico y cómo leer dendrogramas y cuestionamos si PCA es un paso necesario. Nuestro principal objetivo es que se cubran algunos de los escollos y diferentes escenarios en los que nos podemos encontrar con la agrupación jerárquica.

¡Feliz agrupamiento!