@@ -134,12 +134,108 @@ plt.show()
134134
135135# Exercise (k-means)
136136
137- 1 . Change K to 6 and re-run the clustering. How do the patterns change?
138- 2 . Change ` random_state ` and re-run. Do you get the same clusters?
139- 3 . Why do you think k-means can give slightly different results with different random starts?
137+ Here we try to replicate what scipy does internally using simple Python.
140138
139+ 1 . Choose K initial centroids (import random)
140+ 2 . Assign each gene to the closest centroid
141+ 3 . Update centroids as the mean of assigned genes
142+ 4 . Repeat until stable
143+
144+ The most important calculation will be the eucledian distance.
145+ Implement your own `` dist( vec1, vec2) `` function.
146+
147+ ??? You can compare yours to this after you are finished
148+
149+ ``` python
150+ def dist (vec1 , vec2 ):
151+ """
152+ Calculates a single gene <-> gene or gene <-> centroid eucledian distance
153+ """
154+ diff = vec1 - vec2
155+ return np.sum(diff** 2 ) ** 0.5
156+ ```
157+
158+ We need to repeatetly get the eucledian distances of one centroid against all genes.
159+ Implement a `` dist_to_centroid (centroid, mat ) `` function.
160+
161+ ??? Again only peak after you finished yours
162+ ``` python
163+ def dist_to_centroid (centroid , mat ):
164+ """
165+ Calculates eucledian distance between one centroid and a matrix of genes
166+ """ "
167+ dists = []
168+ for i in range (mat.shape[0 ]):
169+ dists.append( dist( centroid, mat[i] ) )
170+ return dists
171+ ```
172+
173+ And finally we need to get randomness into our scripts.
174+ For this you normall use a random number generator and as we are already using numpy we should probably take the one from there:
175+
176+ ``` python
177+ rng = np.random.default_rng(seed)
178+ ```
179+
180+ With this module we can then e.g. identify a random set of k ids from a range of n ids:
181+
182+ ``` python
183+ idx = rng.choice(np.arange(1 ,11 ), size = k, replace = False )
184+ ```
185+
186+ And the loop should contain these steps:
187+ 1 . identify k random genes and use them as initial centromers
188+ 2 . compare each centromer to each gene and find for each gene the closest centromer
189+ 3 . recalculate the new centromers as the mean of the closest genes
190+ 4 . if the new centroids look like the old ones - break the loop
191+ 5 . use the new centroids and restart the loop
141192---
142193
194+ ??? Really - do not peak - use this opportunity to dig into this problem!
195+ ``` python
196+ def kmeans ( data , k , maxiter , seed ):
197+ """
198+ Clusters rows using the kmeans algorithm
199+ """
200+ data_np = np.asarray(data)
201+ # # define a reproducible 'random' state and get the initial centroids as random genes
202+ rng = np.random.default_rng(seed)
203+ idx = rng.choice(np.arange(len (data_np)), size = k, replace = False )
204+ centroids = data_np[idx]
205+
206+ # # the main loop
207+ n = len (data_np)
208+ for it in range (maxiter):
209+ # 1) Distance table D: rows=genes, cols=centroids
210+ D = np.zeros((n, k), dtype = float )
211+ for c in range (k):
212+ dists = dist_to_centroid(centroids[c], data_np)
213+ for i in range (n):
214+ D[i, c] = dists[i]
215+
216+ # 2) Assign each gene to nearest centroid
217+ labels = np.zeros(n, dtype = int )
218+ for i in range (n):
219+ labels[i] = int (np.argmin(D[i, :]))
220+
221+ # 3) Update centroids (mean of assigned points)
222+ new_centroids = centroids.copy()
223+ for c in range (k):
224+ members = data_np[labels == c]
225+ if len (members) > 0 : # avoid empty cluster crash
226+ new_centroids[c] = np.mean(members, axis = 0 )
227+ else :
228+ # re-seed empty centroid to a random point (simple, explicit)
229+ new_centroids[c] = data_np[rng.integers(0 , n)]
230+
231+ # 4) Stop if centroids no longer move
232+ if np.allclose(new_centroids, centroids):
233+ break
234+ centroids = new_centroids
235+
236+ return labels, centroids, D
237+ ```
238+
143239# The Final Project: Simulated annealing clustering
144240
145241Your final project will combine everything you learned to implement a clustering algorithm
0 commit comments