Clustering
Assign to Centroids
Assign each point to its nearest centroid (k-means assignment step). For each
point compute squared distance to each centroid; assign to the closer one.
Library: NumPy vectorized subtraction, np.sum(..., axis=1), np.where.
RESULT: cluster assignment list (0/1 per point).
By hand
points=[[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]], c0=[1,1], c1=[7,7]. Squared distances to c0: 1,1,4,61,85,74. To c1: 61,61,52,1,1,2. Nearest: [0,0,0,1,1,1].
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]
assignments = []
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
assignments.append(0 if d0 <= d1 else 1)
print('RESULT:', assignments)
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]4assignments = []values this step[7, 7]c1assignments ← []
3c1 = [7, 7]4assignments = []5for pt in points:values this step[]assignmentspt ← [1, 2]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[1, 2]ptd0 ← 1
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2values this step1d0d1 ← 61
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)values this step61d1assignments ← [0]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[] → [0]assignmentspt ← [2, 1]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[1, 2] → [2, 1]ptd0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)assignments ← [0, 0]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[0] → [0, 0]assignmentspt ← [1, 3]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[2, 1] → [1, 3]ptd0 ← 4
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2values this step1 → 4d0d1 ← 52
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)values this step61 → 52d1assignments ← [0, 0, 0]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[0, 0] → [0, 0, 0]assignmentspt ← [7, 6]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[1, 3] → [7, 6]ptd0 ← 61
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2values this step4 → 61d0d1 ← 1
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)values this step52 → 1d1assignments ← [0, 0, 0, 1]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[0, 0, 0] → [0, 0, 0, 1]assignmentspt ← [8, 7]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[7, 6] → [8, 7]ptd0 ← 85
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2values this step61 → 85d0d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)assignments ← [0, 0, 0, 1, 1]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[0, 0, 0, 1] → [0, 0, 0, 1, 1]assignmentspt ← [6, 8]
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2values this step[8, 7] → [6, 8]ptd0 ← 74
5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**2values this step85 → 74d0d1 ← 2
6d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**27d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28assignments.append(0 if d0 <= d1 else 1)values this step1 → 2d1assignments ← [0, 0, 0, 1, 1, 1]
7 d1 = (pt[0]-c1[0])**2 + (pt[1]-c1[1])**28 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this step[0, 0, 0, 1, 1] → [0, 0, 0, 1, 1, 1]assignmentsfor pt in points:
4assignments = []5for pt in points:6 d0 = (pt[0]-c0[0])**2 + (pt[1]-c0[1])**2stdout ← RESULT: [0, 0, 0, 1, 1, 1]
8 assignments.append(0 if d0 <= d1 else 1)9print('RESULT:', assignments)values this stepRESULT: [0, 0, 0, 1, 1, 1]stdout
With NumPy
np.sum((points - c)**2, axis=1) computes squared distances to one centroid
for all points simultaneously. np.where applies the threshold.
library.py
import numpy as np
from dalib.display import set_display
set_display()
points = np.array([[1,2],[2,1],[1,3],[7,6],[8,7],[6,8]])
c0 = np.array([1, 1])
c1 = np.array([7, 7])
d0 = np.sum((points - c0)**2, axis=1)
d1 = np.sum((points - c1)**2, axis=1)
assignments = np.where(d0 <= d1, 0, 1).tolist()
print('d0:', d0.tolist())
print('d1:', d1.tolist())
print('RESULT:', assignments)
d0: [1, 1, 4, 61, 85, 74]
d1: [61, 61, 52, 1, 1, 2]
RESULT: [0, 0, 0, 1, 1, 1]
Implementation notes
- Squared distance is used for comparison (avoids sqrt); ranking is preserved
since √a < √b ↔ a < b for non-negative a, b. Cross-reference:
euclidean-distance(ch02) for the distance formula. - Points [1,2],[2,1],[1,3] cluster tightly around c0=[1,1]; [7,6],[8,7],[6,8] around c1=[7,7] — no ties possible. Picking centroid positions to eliminate ties avoids undefined assignment behavior.
- This is ONE step of k-means. Full k-means alternates assign↔update until
centroids stop moving. Cross-reference:
update-centroids(this chapter).