Introduction

Hello and welcome! This lesson serves as your introduction to Hierarchical Clustering, a crucial part of unsupervised machine learning. Hierarchical Clustering is a powerful tool for grouping data based on inherent patterns and shared characteristics. By visually representing the hierarchy of clusters, it provides deep insights into the intricacies and overlapping structures of our data. Let's commence this journey into Hierarchical Clustering!

Agglomerative vs. Divisive Approaches

Hierarchical Clustering operates broadly through two approaches — Agglomerative and Divisive. The Agglomerative technique, also known as 'bottom-up,' begins by treating every data point as a distinct cluster and then merges them until only one cluster remains. Conversely, the Divisive methodology, termed 'top-down,' begins with all data points in a single cluster and splits them progressively until each point forms its cluster.

Agglomerative Clustering Algorithm

One of the most common forms of Hierarchical Clustering is Agglomerative Hierarchical Clustering. It starts with every single object in a single cluster. Then, in each successive iteration, it merges the closest pair of clusters and updates the similarity (or distance) between the newly formed cluster and each old cluster. The algorithm repeats this process until all objects are in a single remaining cluster.

The Agglomerative Hierarchical Clustering involves the following major steps:

  • Compute the similarity (or distance) matrix containing the distance between each pair of objects in the dataset.
  • Represent each data object as a singleton cluster.
  • Repeat steps 4 and 5 until only one cluster remains.
  • Merge the two closest clusters based on the distances from the distance matrix.
  • Update the similarity (or distance) matrix to reflect the distance of the newly formed cluster with the remaining clusters in the forest. The output is a tree-like diagram named dendrogram which represents the order and distances (similarity) of merges during the algorithm execution.
Understanding the Distance Matrix
Custom Agglomerative Clustering implementation in Python with the Iris dataset

Let's proceed to the hands-on implementation of Agglomerative Clustering in Python (using the Iris dataset for our example). We'll use some standard Python libraries — numpy for numerical computations and matplotlib and seaborn for data visualization.

We first setup our environment and initiate our Iris dataset:

# Utilizing the Iris dataset
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data

Next, we plot our dataset with Seaborn, a library built on matplotlib, which aids in drawing more attractive and informative statistical graphics:

# Pairplot for the Iris dataset
sns.pairplot(sns.load_dataset("iris"), hue="species")
plt.show()

The resulting plot will look like this:

image

Let's understand the plot:

  • The diagonal plots show the distribution of each feature in the dataset — for example, the plot at index (0, 0) shows the distribution of sepal length, meaning the x-axis represents the sepal length and the y-axis represents the frequency of the sepal length values.
  • The scatter plots show the relationship between each pair of features, with different colors representing different species of Iris flowers — for example, the plot at index (0, 1) shows the relationship between the sepal length and sepal width and so on

Having that, we can now formulate our Agglomerative Hierarchical Clustering function. Our function will begin by placing each data point in a separate cluster, calculating the distances between clusters, merging the nearest clusters, and proceeding until one or more specified clusters remain:

For that, let's define a function to calculate the Euclidean distance between two points:

import numpy as np

# Function to compute Euclidean Distance
def euc_dist(a, b):
    return np.sqrt(np.sum((a - b)**2))

Now we can define a separate function that computes the distance matrix:

# Function to calculate the distance matrix
def calculate_distance_matrix(X, clusters):
    # Initialize distance matrix
    dist_matrix = np.zeros((len(clusters), len(clusters)))
    # Compute the distance between each pair of clusters
    for i in range(len(clusters)):
        for j in range(i+1, len(clusters)):
            dists = []
            for k in clusters[i]:
                for l in clusters[j]:
                    dists.append(euc_dist(X[k], X[l]))
            dist_matrix[i, j] = dist_matrix[j, i] = np.mean(dists)
    return dist_matrix

Finally, we define the main function for Agglomerative Clustering:

def agglomerative_clustering(X, n_clusters):
    # Initialize each data point as its own cluster
    clusters = [[i] for i in range(len(X))]
    # Continue clustering until the desired number of clusters is reached
    while len(clusters) > n_clusters:
        dist_matrix = calculate_distance_matrix(X, clusters) # Calculate the distance matrix
        # Find the pair of clusters with the minimum distance without np
        min_dist = float('inf')
        for i in range(len(clusters)):
            for j in range(i+1, len(clusters)):
                if dist_matrix[i, j] < min_dist:
                    min_dist = dist_matrix[i, j]
                    idx1, idx2 = i, j
        clusters[idx1].extend(clusters[idx2]) # Merge these two closest clusters
        clusters.pop(idx2) # Remove the merged cluster from the list of clusters
    # Create an array to hold the cluster labels for each data point
    labels = np.empty(len(X), dtype=int)
    for label, cluster in enumerate(clusters):
        for i in cluster:
            labels[i] = label
    return labels

Notice that we perform the clustering until the number of clusters reaches the desired value. The function agglomerative_clustering returns an array of cluster labels for each data point.

Now, let's scale our data and apply the Agglomerative Clustering function to the Iris dataset:

from sklearn.preprocessing import StandardScaler

# Standardizing the features
scaler = StandardScaler()
X_std = scaler.fit_transform(X)
# Perform agglomerative clustering
y_agg = agglomerative_clustering(X_std, n_clusters=3)

# Visualize the clustering
plt.scatter(X[:, 0], X[:, 1], c=y_agg, cmap='viridis')
plt.show()

We will see the following result:

image

Sklearn's Hierarchical Clustering

Sklearn, Python's esteemed Machine Learning library, provides an optimized, efficient implementation of Hierarchical Clustering through the AgglomerativeClustering class. Let's try it with our Iris dataset:

from sklearn.cluster import AgglomerativeClustering

# Defining the agglomerative clustering
agg_cluster = AgglomerativeClustering(n_clusters=3)

# Fit model
y_agg_sklearn = agg_cluster.fit_predict(X_std)

# Visualizing clusters
plt.scatter(X_std[:,0], X_std[:, 1], c=y_agg_sklearn)
plt.show()

The resulting plot will be the following, with some differences to the previous one due to more advanced techniques used in the AgglomerativeClustering class:

image

Lesson Summary and Practice

Congratulations! You've successfully broken down the nuances of Hierarchical Clustering, focusing on Agglomerative Clustering, and brought Hierarchical Clustering to life using Python. Practice tasks await next, offering the perfect platform to apply acquired concepts and deepen your understanding of Hierarchical Clustering. Carry this momentum forward, and let's forge ahead into the captivating world of Hierarchical Clustering!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal