In this blog post, we will be looking at Agglomerative Hierarchical Clustering. This is a bottom up approach of hierarchical clustering.

# import libraries
import numpy as np
import pandas as pd
from scipy import ndimage
from scipy.cluster import hierarchy
from scipy.spatial import distance_matrix
from matplotlib import pyplot as plt
from sklearn import manifold, datasets
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets.samples_generator import make_blobs
%matplotlib inline

Generate random data

We will be generating a set of data using the make_blobs class.
The input parameters for this class are:

  • n_samples: The total number of points qually divided among clusters.
  • centers: The number of centers to generate, or the fixed center locations.
  • cluster_std: The standard deviation of the clusters. The larger the number, the further apart the clusters.

We will save the result to X1 and y1.

X1, y1 = make_blobs(n_samples=50, centers=[[4,4], [-2,-1], [1,1], [10,4]], cluster_std=0.9)

Plot the scatter plot of the randomly generated data.

plt.scatter(X1[:, 0], X1[:, 1], marker='o')
<matplotlib.collections.PathCollection at 0x1e595219198>

Agglomerative clustering

Cluster the random data points we just created.
The AgglomerativeClustering class requires two inputs:

  • n_clusters: The number of clusters to form as well as the number of centroids to generate.
  • linkage: Which linkage criterion to use. This determines which distance to use between sets of observations. The algorithm will merge the pairs of clusters that minimize this criterion.
agglom = AgglomerativeClustering(n_clusters=4, linkage='average')

Fit the model with X2 and y2 from the generated data above.

agglom.fit(X1, y1)
AgglomerativeClustering(affinity='euclidean', compute_full_tree='auto',
                        connectivity=None, distance_threshold=None,
                        linkage='average', memory=None, n_clusters=4)

Plot the clustering.

plt.figure(figsize=(12,8))

# scale down the data points to prevent scattering

# create a minimum and maximum range of X1
x_min, x_max = np.min(X1, axis=0), np.max(X1, axis=0)
# get the average distance for X1
X1 = (X1 - x_min) / (x_max - x_min)

# loop to display all of the datapoints
for i in range(X1.shape[0]):
    # Replace the data points with their respective cluster value 
    # (ex. 0) and is color coded with a colormap (plt.cm.spectral)
    plt.text(X1[i, 0], X1[i, 1], str(y1[i]),
             color=plt.cm.nipy_spectral(agglom.labels_[i] / 10.),
             fontdict={'weight': 'bold', 'size': 9})
    
# remove the x ticks, y ticks, x and y axis
plt.xticks([])
plt.yticks([])

# display the plot of the original data before clustering
plt.scatter(X1[:, 0], X1[:, 1], marker='.')

# display the plot
plt.show()

Dendrogram

A distance matrix contains the distance from each point to every other point of a dataset.
Use the function distance_matrix, which requires two inputs. Use the feature matrix, X2, as both inputs, and save the distance matrix to a variable called dist_matrix. One way to check that your matrix is correct is to make sure that the distance values are symmetric, with a diagonal of zeros.

dist_matrix = distance_matrix(X1, X1)
print(dist_matrix)
[[0.         0.10264836 0.75840228 ... 0.71198038 0.58237479 0.42606018]
 [0.10264836 0.         0.67498119 ... 0.62861156 0.48764469 0.33707566]
 [0.75840228 0.67498119 0.         ... 0.04644316 0.22929692 0.3396303 ]
 ...
 [0.71198038 0.62861156 0.04644316 ... 0.         0.19162587 0.29360964]
 [0.58237479 0.48764469 0.22929692 ... 0.19162587 0.         0.1638079 ]
 [0.42606018 0.33707566 0.3396303  ... 0.29360964 0.1638079  0.        ]]

Save the result to a variable called Z.

Z = hierarchy.linkage(dist_matrix, 'complete')
C:\Users\prana\Anaconda3\lib\site-packages\ipykernel_launcher.py:1: ClusterWarning: scipy.cluster: The symmetric non-negative hollow observation matrix looks suspiciously like an uncondensed distance matrix
  """Entry point for launching an IPython kernel.

Hierarchical clustering is typically visualized as a dendrogram. Each erge is represented by a horizontal line. The y-coordinate of the horizontal line is the similarity of the two clusters that were merged. By moving up from the bottom layer to the top node, a dendrogram allows us to reconstruct the history of merges that resulted in the depicted clustering.

dendro = hierarchy.dendrogram(Z)