forked from chrisjmccormick/simsearch
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrunElbowMethod.py
More file actions
119 lines (92 loc) · 3.44 KB
/
Copy pathrunElbowMethod.py
File metadata and controls
119 lines (92 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# -*- coding: utf-8 -*-
"""
This script uses the elbow method to help identify a good value of 'k' to use
for k-means clustering.
@author: Chris McCormick
"""
from scipy.spatial.distance import cdist, pdist
from sklearn.cluster import KMeans
from simsearch import SimSearch
import numpy as np
import matplotlib.pyplot as plt
# Load the pre-built corpus.
print('Loading the saved SimSearch and corpus...')
(ksearch, ssearch) = SimSearch.load(save_dir='./mhc_corpus/')
# Get the dataset to be clustered.
# Note - The index is store with all of the vectors *already normalized*.
X = ssearch.index.index
# If you needed to normalize the vectors:
# norms = np.linalg.norm(X)
# norms = norms.reshape(-1, 1)
# X = X / norms
# These lists will store the actual values to plot.
plotx = []
ploty1 = []
ploty2 = []
print('Calculating avg(x^2)')
tss = sum(pdist(X)**2)/X.shape[0]
for k in range(10, 200, 10):
plotx.append(k)
# Create the KMeans object
kmeans_model = KMeans(n_clusters=k, init='k-means++', max_iter=100)
# Run clustering.
print 'K-Means with', k, 'clusters...'
kmeans_model.fit(X)
# Get the resulting centroids
centroids = kmeans_model.cluster_centers_
# Calculate the distances matrix between all data points and the final
# centroids.
# TODO - I changed this to squared euclidean, let's see if it does any better?
D = cdist(X, centroids, 'sqeuclidean')
# For each data point, find the minimum distance value (the distance to the
# closest centroid)
# This could also be done using the '.labels_' object from kmeans...
dist = np.min(D, axis=1)
# Calculate the average distance
# TODO - This is really the average member-centroid L2 distance...
avgWithinSS = sum(dist) / X.shape[0]
# Append it to the plot.
ploty1.append(avgWithinSS)
# Total with-in sum of square
wcss = sum(dist**2)
bss = tss - wcss / tss * 100
ploty2.append(bss)
###############################################################################
# Find the point with the highest second derivative.
dx = []
for i in range(0, len(plotx) - 1):
dxi = (ploty1[i + 1] - ploty1[i]) / (plotx[i + 1] - plotx[i])
dx.append(dxi)
ddx = []
for i in range(0, len(dx) - 1):
ddxi = (dx[i + 1] - dx[i]) / (plotx[i + 1] - plotx[i])
# Append the absolute value of the second derivative.
ddx.append(abs(ddxi))
# Get the index of the point with the highest second derivative.
# This is given by the index of the highest second derivative value, plus 1,
# see the example below:
# Points 0 1 2 3 4
# dx 0 1 2 3
# ddx 0 1 2
kIdx = np.argmax(ddx) + 1
###############################################################################
# Plot the elbow curve.
#
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(plotx, ploty1, 'b*-')
ax.plot(plotx[kIdx], ploty1[kIdx], marker='o', markersize=12,
markeredgewidth=2, markeredgecolor='r', markerfacecolor='None')
plt.grid(True)
plt.xlabel('Number of clusters')
plt.ylabel('Average within-cluster sum of squares')
plt.title('Elbow for KMeans clustering')
###############################################################################
# Plot the explained variance elbow.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(plotx, ploty2, 'b*-')
plt.grid(True)
plt.xlabel('Number of clusters')
plt.ylabel('Percentage of variance explained')
plt.title('Elbow for KMeans clustering')