Clustering
K-Means One Iteration
Run one full k-means iteration: assign every point to its nearest centroid, then recompute each centroid as the mean of its cluster. A single loop accumulates per-cluster sums and counts in one pass (no separate assignment list). Library: sklearn KMeans with init=given, n_init=1, max_iter=1. RESULT: updated centroid list [[cx0,cy0],[cx1,cy1]] (rounded).
By hand
points=[[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]], c0=[1,1], c1=[7,7]. Assign: d0=[1,1,4,61,85,74], d1=[61,61,52,1,1,2] → [0,0,0,1,1,1]. Update: c0=[(1+2+1)/3,(2+1+3)/3]=[1.3333,2.0]; c1=[(7+8+6)/3,(6+7+8)/3]=[7.0,7.0].
naive.py
Replay: real traced execution (multi-file project)
points = [[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]]
c0 = [1, 1]
c1 = [7, 7]
sums = [[0.0, 0.0], [0.0, 0.0]]
counts = [0, 0]
for pt in points:
d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2
d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2
c = 0 if d0 <= d1 else 1
sums[c][0] = sums[c][0] + pt[0]
sums[c][1] = sums[c][1] + pt[1]
counts[c] = counts[c] + 1
c0 = [round(sums[0][0]/counts[0], 4), round(sums[0][1]/counts[0], 4)]
c1 = [round(sums[1][0]/counts[1], 4), round(sums[1][1]/counts[1], 4)]
print('RESULT:', [c0, c1])
points ← [[1, 2], [2, 1], [1, 3], [7, 6], [8, 7], [6, 8]]
1points = [[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]]2c0 = [1, 1]values this step[[1, 2], [2, 1], [1, 3], [7, 6], [8, 7], [6, 8]]pointsc0 ← [1, 1]
1points = [[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]]2c0 = [1, 1]3c1 = [7, 7]values this step[1, 1]c0c1 ← [7, 7]
2c0 = [1, 1]3c1 = [7, 7]4sums = [[0.0, 0.0], [0.0, 0.0]]values this step[7, 7]c1sums ← [[0.0, 0.0], [0.0, 0.0]]
3c1 = [7, 7]4sums = [[0.0, 0.0], [0.0, 0.0]]5counts = [0, 0]values this step[[0.0, 0.0], [0.0, 0.0]]sumscounts ← [0, 0]
4sums = [[0.0, 0.0], [0.0, 0.0]]5counts = [0, 0]6for pt in points:values this step[0, 0]countspt ← [1, 2], d0 ← 1, d1 ← 61, c ← 0, sums ← [[1.0, 2.0], [0.0, 0.0]]
pass 1 of 65counts = [0, 0]6for pt in points:7 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**28 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**29 c = 0 if d0 <= d1 else 110 sums[c][0] = sums[c][0] + pt[0]11 sums[c][1] = sums[c][1] + pt[1]12 counts[c] = counts[c] + 113c0 = [round(sums[0][0]/counts[0], 4), round(sums[0][1]/counts[0], 4)]values this step[1, 2]pt1d061d10c[[1.0, 0.0], [0.0, 0.0]] → [[1.0, 2.0], [0.0, 0.0]]sums[0, 0] → [1, 0]countsAll 6 passes — pass 1 is the card above pass ptd0d1csumscounts1 [1, 2] 1 61 0 [[1.0, 0.0], [0.0, 0.0]] → [[1.0, 2.0], [0.0, 0.0]] [0, 0] → [1, 0] 2 [1, 2] → [2, 1] — — — [[3.0, 2.0], [0.0, 0.0]] → [[3.0, 3.0], [0.0, 0.0]] [1, 0] → [2, 0] 3 [2, 1] → [1, 3] 1 → 4 61 → 52 — [[4.0, 3.0], [0.0, 0.0]] → [[4.0, 6.0], [0.0, 0.0]] [2, 0] → [3, 0] 4 [1, 3] → [7, 6] 4 → 61 52 → 1 0 → 1 [[4.0, 6.0], [7.0, 0.0]] → [[4.0, 6.0], [7.0, 6.0]] [3, 0] → [3, 1] 5 [7, 6] → [8, 7] 61 → 85 — — [[4.0, 6.0], [15.0, 6.0]] → [[4.0, 6.0], [15.0, 13.0]] [3, 1] → [3, 2] 6 [8, 7] → [6, 8] 85 → 74 1 → 2 — [[4.0, 6.0], [21.0, 13.0]] → [[4.0, 6.0], [21.0, 21.0]] [3, 2] → [3, 3] for pt in points:
5counts = [0, 0]6for pt in points:7 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2c0 ← [1.3333, 2.0]
12 counts[c] = counts[c] + 113c0 = [round(sums[0][0]/counts[0], 4), round(sums[0][1]/counts[0], 4)]14c1 = [round(sums[1][0]/counts[1], 4), round(sums[1][1]/counts[1], 4)]values this step[1, 1] → [1.3333, 2.0]c0c1 ← [7.0, 7.0]
13c0 = [round(sums[0][0]/counts[0], 4), round(sums[0][1]/counts[0], 4)]14c1 = [round(sums[1][0]/counts[1], 4), round(sums[1][1]/counts[1], 4)]15print('RESULT:', [c0, c1])values this step[7, 7] → [7.0, 7.0]c1stdout ← RESULT: [[1.3333, 2.0], [7.0, 7.0]]
14c1 = [round(sums[1][0]/counts[1], 4), round(sums[1][1]/counts[1], 4)]15print('RESULT:', [c0, c1])values this stepRESULT: [[1.3333, 2.0], [7.0, 7.0]]stdout
With scikit-learn
KMeans(init=given, n_init=1, max_iter=1) performs exactly one assign+update
cycle starting from the supplied centroids.
library.py
import numpy as np
from sklearn.cluster import KMeans
from dalib.display import set_display
set_display()
X = np.array([[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]], dtype=float)
init = np.array([[1.0, 1.0], [7.0, 7.0]])
km = KMeans(n_clusters=2, init=init, n_init=1, max_iter=1, random_state=0)
km.fit(X)
print('labels:', km.labels_.tolist())
centroids = [[round(float(v), 4) for v in c] for c in km.cluster_centers_]
print('RESULT:', centroids)
labels: [0, 0, 0, 1, 1, 1]
RESULT: [[1.3333, 2.0], [7.0, 7.0]]
Implementation notes
- The assign and update steps are fused into a single loop:
c = 0 if d0 <= d1 else 1selects the cluster;sums[c][0]andcounts[c]accumulate inline. This avoids storing a separate assignments list while keeping each logical step visible in the trace. - sklearn's
max_iter=1performs one assign+update cycle and stops — confirmed to match the manual result exactly wheninitis supplied directly. - c1 returns to [7.0,7.0] (unchanged from initialization) because [7,7] is already the exact mean of its three cluster members. A second iteration would produce the same assignments, so the algorithm converges in one step here.
- Cross-reference:
assign-to-centroidsandupdate-centroids(this chapter) for the individual steps shown separately.