InterviewPrepKit

Home / Coding / Machine Learning Coding / Clustering & Neighbors / K-Means Clustering

K-Means Clustering

hard 00:00
Solving tips
  • Seed a local np.random.RandomState(seed) and use it for every random draw so the run is reproducible; do not touch the global np.random state.
  • Assignment is one vectorized pairwise-distance call plus argmin over centroids; never loop over points inside the iteration.
  • Stop as soon as the label vector is unchanged from the previous iteration, and guard against empty clusters when you recompute means.

Implement k-means clustering (Lloyd’s algorithm) from scratch on a 2D array of points. K-means partitions n points into k clusters by alternating two steps: assign each point to its nearest centroid, then move each centroid to the mean of the points assigned to it. It is a staple of unsupervised-learning interviews, and the interviewer is checking that you can vectorize the assignment step instead of writing a distance loop.

Algorithm

  1. Initialize k centroids by sampling k distinct rows of X at random.
  2. Assign step: label each point with the index of its nearest centroid (Euclidean distance).
  3. Update step: set each centroid to the mean of the points currently assigned to it.
  4. Repeat steps 2-3 until the labels stop changing or you reach max_iters.

Task

Complete kmeans(X, k, max_iters=100, seed=0) so it runs Lloyd’s algorithm and returns (centroids, labels).

  • Draw the initial centroids by choosing k distinct row indices of X using a local np.random.RandomState(seed) (for example rng.choice(n, size=k, replace=False)). Use only this local RNG so results are reproducible.
  • In each iteration, compute the (n, k) matrix of Euclidean distances from every point to every centroid with vectorized NumPy (no Python loop over points), then take argmin along the centroid axis to get the new labels.
  • Recompute each centroid as the mean of its assigned points. If a cluster ends up empty, keep its previous centroid so the mean is well defined.
  • Stop early when the new labels equal the previous labels; otherwise stop after max_iters iterations. Return the final centroids (shape (k, d), float) and labels (shape (n,), int).

Do not call sklearn, scipy, or any built-in k-means / pairwise-distance helper.

Example

X = np.array([[0.0, 0.0],
              [0.1, 0.0],
              [0.0, 0.1],
              [5.0, 5.0],
              [5.1, 5.0],
              [5.0, 5.1]])
centroids, labels = kmeans(X, k=2, max_iters=100, seed=0)
# The two dense blobs are recovered; one label per point, e.g.
# labels -> array([0, 0, 0, 1, 1, 1])   (cluster ids may be swapped)
# centroids ~ [[0.033, 0.033], [5.033, 5.033]]  (order matches labels)

The exact cluster ids (0 vs 1) depend on which rows the seed picks first, but the partition into the two blobs and the centroid means are determined.

Constraints

  • 1 <= k <= n, X has shape (n, d) with 1 <= n <= 10^4, 1 <= d <= 10^3.
  • Use vectorized NumPy for the assignment step; no Python loop over the n points.
  • Seed all randomness through a local np.random.RandomState(seed); do not modify the global NumPy RNG.
  • Handle empty clusters without crashing (a naive mean of an empty slice returns nan).

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug