I'm using hierarchical clustering to cluster word vectors, and I want the user to be able to display a dendrogram showing the clusters. However, since there can be thousands of words, I want this dendrogram to be truncated to some reasonable valuable, with the label for each leaf being a string of the most significant words in that cluster.
My problem is that, according to the docs, "The labels[i] value is the text to put under the ith leaf node only if it corresponds to an original observation and not a non-singleton cluster." I take this to mean I can't label clusters, only singular points?
To illustrate, here is a short python script which generates a simple labeled dendrogram:
import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage
from matplotlib import pyplot as plt
randomMatrix = np.random.uniform(-10,10,size=(20,3))
linked = linkage(randomMatrix, 'ward')
labelList = ["foo" for i in range(0, 20)]
plt.figure(figsize=(15, 12))
dendrogram(
linked,
orientation='right',
labels=labelList,
distance_sort='descending',
show_leaf_counts=False
)
plt.show()
Now let's say I want to truncate to just 5 leaves, and for each leaf, label it like "foo, foo, foo...", ie the words that make up that cluster. (Note: generating these labels is not the issue here.) I truncate it, and supply a label list to match:
labelList = ["foo, foo, foo..." for i in range(0, 5)]
dendrogram(
linked,
orientation='right',
p=5,
truncate_mode='lastp',
labels=labelList,
distance_sort='descending',
show_leaf_counts=False
)
and here's the problem, no labels:
I'm thinking there might be a use here for the parameter 'leaf_label_func' but I'm not sure how to use it.
You are correct about using the leaf_label_func parameter.
In addition to creating a plot, the dendrogram function returns a dictionary (they call it R in the docs) containing several lists. The leaf_label_func you create must take in a value from R["leaves"] and return the desired label. The easiest way to set labels is to run dendrogram twice. Once with no_plot=True
to get the dictionary used to create your label map. And then again to create the plot.
randomMatrix = np.random.uniform(-10,10,size=(20,3))
linked = linkage(randomMatrix, 'ward')
labels = ["A", "B", "C", "D"]
p = len(labels)
plt.figure(figsize=(8,4))
plt.title('Hierarchical Clustering Dendrogram (truncated)', fontsize=20)
plt.xlabel('Look at my fancy labels!', fontsize=16)
plt.ylabel('distance', fontsize=16)
# call dendrogram to get the returned dictionary
# (plotting parameters can be ignored at this point)
R = dendrogram(
linked,
truncate_mode='lastp', # show only the last p merged clusters
p=p, # show only the last p merged clusters
no_plot=True,
)
print("values passed to leaf_label_func\nleaves : ", R["leaves"])
# create a label dictionary
temp = {R["leaves"][ii]: labels[ii] for ii in range(len(R["leaves"]))}
def llf(xx):
return "{} - custom label!".format(temp[xx])
## This version gives you your label AND the count
# temp = {R["leaves"][ii]:(labels[ii], R["ivl"][ii]) for ii in range(len(R["leaves"]))}
# def llf(xx):
# return "{} - {}".format(*temp[xx])
dendrogram(
linked,
truncate_mode='lastp', # show only the last p merged clusters
p=p, # show only the last p merged clusters
leaf_label_func=llf,
leaf_rotation=60.,
leaf_font_size=12.,
show_contracted=True, # to get a distribution impression in truncated branches
)
plt.show()