34

I have a correlation matrix which states how every item is correlated to the other item. Hence for a N items, I already have a N*N correlation matrix. Using this correlation matrix how do I cluster the N items in M bins so that I can say that the Nk Items in the kth bin behave the same. Kindly help me out. All item values are categorical.

Thanks. Do let me know if you need any more information. I need a solution in Python but any help in pushing me towards the requirements will be a big help.

4 Answers4

29

Looks like a job for block modeling. Google for "block modeling" and the first few hits are helpful.

Say we have a covariance matrix where N=100 and there are actually 5 clusters: Initial covariance matrix

What block modelling is trying to do is find an ordering of the rows, so that the clusters become apparent as 'blocks': Optimized covariance matrix order

Below is a code example that performs a basic greedy search to accomplish this. It's probably too slow for your 250-300 variables, but it's a start. See if you can follow along with the comments:

import numpy as np
from matplotlib import pyplot as plt

# This generates 100 variables that could possibly be assigned to 5 clusters
n_variables = 100
n_clusters = 5
n_samples = 1000

# To keep this example simple, each cluster will have a fixed size
cluster_size = n_variables // n_clusters

# Assign each variable to a cluster
belongs_to_cluster = np.repeat(range(n_clusters), cluster_size)
np.random.shuffle(belongs_to_cluster)

# This latent data is used to make variables that belong
# to the same cluster correlated.
latent = np.random.randn(n_clusters, n_samples)

variables = []
for i in range(n_variables):
    variables.append(
        np.random.randn(n_samples) + latent[belongs_to_cluster[i], :]
    )

variables = np.array(variables)

C = np.cov(variables)

def score(C):
    '''
    Function to assign a score to an ordered covariance matrix.
    High correlations within a cluster improve the score.
    High correlations between clusters decease the score.
    '''
    score = 0
    for cluster in range(n_clusters):
        inside_cluster = np.arange(cluster_size) + cluster * cluster_size
        outside_cluster = np.setdiff1d(range(n_variables), inside_cluster)

        # Belonging to the same cluster
        score += np.sum(C[inside_cluster, :][:, inside_cluster])

        # Belonging to different clusters
        score -= np.sum(C[inside_cluster, :][:, outside_cluster])
        score -= np.sum(C[outside_cluster, :][:, inside_cluster])

    return score


initial_C = C
initial_score = score(C)
initial_ordering = np.arange(n_variables)

plt.figure()
plt.imshow(C, interpolation='nearest')
plt.title('Initial C')
print 'Initial ordering:', initial_ordering
print 'Initial covariance matrix score:', initial_score

# Pretty dumb greedy optimization algorithm that continuously
# swaps rows to improve the score
def swap_rows(C, var1, var2):
    '''
    Function to swap two rows in a covariance matrix,
    updating the appropriate columns as well.
    '''
    D = C.copy()
    D[var2, :] = C[var1, :]
    D[var1, :] = C[var2, :]

    E = D.copy()
    E[:, var2] = D[:, var1]
    E[:, var1] = D[:, var2]

    return E

current_C = C
current_ordering = initial_ordering
current_score = initial_score

max_iter = 1000
for i in range(max_iter):
    # Find the best row swap to make
    best_C = current_C
    best_ordering = current_ordering
    best_score = current_score
    for row1 in range(n_variables):
        for row2 in range(n_variables):
            if row1 == row2:
                continue
            option_ordering = best_ordering.copy()
            option_ordering[row1] = best_ordering[row2]
            option_ordering[row2] = best_ordering[row1]
            option_C = swap_rows(best_C, row1, row2)
            option_score = score(option_C)

            if option_score > best_score:
                best_C = option_C
                best_ordering = option_ordering
                best_score = option_score

    if best_score > current_score:
        # Perform the best row swap
        current_C = best_C
        current_ordering = best_ordering
        current_score = best_score
    else:
        # No row swap found that improves the solution, we're done
        break

# Output the result
plt.figure()
plt.imshow(current_C, interpolation='nearest')
plt.title('Best C')
print 'Best ordering:', current_ordering
print 'Best score:', current_score
print
print 'Cluster     [variables assigned to this cluster]'
print '------------------------------------------------'
for cluster in range(n_clusters):
    print 'Cluster %02d  %s' % (cluster + 1, current_ordering[cluster*cluster_size:(cluster+1)*cluster_size])
Rodin
  • 432
  • 1
    Isnt that technique used for Social Networks clustering? Will that be relevant here? Does it make sense to use that correlation matrix as a distance matrix? – Abhishek093 Feb 19 '15 at 11:45
  • 1
  • Yes, 2) I think so, 3) Yes (values that are highly correlated are close)
  • – Rodin Feb 19 '15 at 11:46
  • Okay. I saw through the first few links. I still dont know how this will help me solve my problem. – Abhishek093 Feb 19 '15 at 11:52
  • I've edited my answer. I hope its useful to you. – Rodin Feb 19 '15 at 13:46
  • I am gonna check it out now. I will let you know if it fit my problem. Thank you so much. – Abhishek093 Feb 20 '15 at 06:52
  • Is there a way to improve this code to handle the situation when the sizes of clusters are not known in advance? – Sergei Tikhomirov May 31 '18 at 17:02
  • I assume it's version depended, but I got a IndexError: arrays used as indices must be of integer (or boolean) type, since the indices used when calculating the score are floats by default. This can be fixed by converting inside_cluster and outside_cluster to int. – Mitchell van Zuylen Oct 19 '18 at 09:36
  • Thanks for the heads up @MitchellvanZuylen, fixed! – Rodin Oct 22 '18 at 09:48