Clustering Lab
Paste numeric CSV or choose a local file, select columns and run k-means or hierarchical clustering. Inspect groups, centroids and every iteration or merge, then export CSV, project JSON or SVG. Use the original four-point example to try the controls before supplying your own data.
01 · Supplied data
Up to 2,000 rows, 20 selected numeric columns, 64 source columns and 5 MB. Missing and nonfinite selected cells are errors, never zero-filled or deleted.
02 · Numeric columns and method
4 rows · 2 selected columns
03 · Two-axis projection
Original example preview- Within-cluster SSE
- 4
- Clusters
- 2
SSE uses original units across every selected dimension. Different scales and selected columns change distances.
Original four-point example shown. Paste your data to explore its groups.
- Rows
- 4
- Dimensions
- 2
- Complete iterations / merges
- 2
- Scaling
- None
Inputs are validated before replacing current work. Saved results are recomputed only after Run clustering.
- Centroids (0,1), (10,1); SSE 4
- Merge distances 2, then 8
K-means pseudocode, Python and R examples
These examples use the four numeric points above, without scaling. Python uses its standard library and a reusable function for rectangular numeric rows. R uses base R with explicit starting centers and Lloyd's algorithm. Edit the rows and k to try your own numeric data. The code runs in the language you choose; it does not change this browser calculation or reproduce every library's defaults.
The Python example starts from the first row, then uses farthest-first centers. Its ties keep the earliest row or lowest cluster index; empty clusters take the farthest donor row. It refuses squared-distance or mean-offset precision loss; translate or rescale large-offset or tiny-distance data. It raises an error if assignments do not stabilize within the iteration limit. The browser uses its selected seed for the first center and can return a bounded unconverged trace.
Browser k-means pseudocode
Browser k-means pseudocode
Validate 2..2000 rows, 1..20 finite numeric columns, k in 2..20,
k <= row count, and an unsigned 32-bit seed.
If requested, transform each selected column to sample z-scores;
refuse a constant column or non-finite transformed values.
Choose the first center with the seeded generator.
Until k centers exist, choose the unused row farthest from its
nearest chosen center; distance ties keep the earliest row.
previous_assignments = none
For iteration = 1..100:
Assign each row to its nearest center using squared Euclidean distance;
ties keep the lowest cluster index.
For each empty cluster in index order:
Among rows in clusters with at least two rows, move the row farthest
from its current center into the empty cluster.
Distance ties keep the earliest row; update counts after each move.
Refuse if no donor row exists.
Set each center to its assigned rows' mean in every selected column.
Compute counts and SSE; save this complete iteration in the trace.
If assignments equal previous_assignments, stop as converged.
previous_assignments = a copy of assignments
Return assignments, centers, counts, SSE, trace and convergence status.
Refuse squared-distance overflow or underflow rather than silently proceeding.
Reaching iteration 100 returns a bounded result, not a convergence guarantee.Runnable Python k-means example
from math import fsum, isfinite, isclose
def kmeans(rows, k=2, max_iter=100):
# Lloyd's algorithm; deterministic farthest-first initialization.
rows = [tuple(map(float, row)) for row in rows]
if not rows or not rows[0] or any(len(p) != len(rows[0]) for p in rows):
raise ValueError("Use a nonempty rectangular numeric matrix")
if any(not isfinite(x) for p in rows for x in p):
raise ValueError("Use finite numbers")
if type(k) is not int or not 1 <= k <= len(set(rows)):
raise ValueError("k must be within the distinct row count")
if type(max_iter) is not int or max_iter < 1:
raise ValueError("max_iter must be a positive integer")
def distance(a, b):
terms = [(x - y) ** 2 for x, y in zip(a, b)]
value = fsum(terms)
if not isfinite(value) or any(x != y and term == 0
for x, y, term in zip(a, b, terms)):
raise ValueError("Rescale the data: squared-distance precision limit")
return value
centers = [rows[0]]
while len(centers) < k:
centers.append(max(rows, key=lambda p: min(distance(p, c) for c in centers)))
previous = None
for _ in range(max_iter):
labels = [min(range(k), key=lambda j: distance(p, centers[j])) for p in rows]
counts = [labels.count(j) for j in range(k)]
# Move the earliest farthest donor row to each empty cluster.
for j in range(k):
if counts[j] == 0:
donors = [i for i in range(len(rows)) if counts[labels[i]] > 1]
i = max(donors, key=lambda i: distance(rows[i], centers[labels[i]]))
counts[labels[i]] -= 1
labels[i] = j
counts[j] += 1
def mean(members, d):
origin = members[0][d]
offset = fsum((p[d] - origin) / len(members) for p in members)
value = origin + offset
if not isfinite(value) or not isclose(value - origin, offset, rel_tol=1e-12, abs_tol=0):
raise ValueError("Translate/rescale the data: mean offset precision limit")
return value
centers = [tuple(mean([p for i, p in enumerate(rows) if labels[i] == j], d)
for d in range(len(rows[0]))) for j in range(k)]
if labels == previous:
sse = fsum(distance(p, centers[j]) for p, j in zip(rows, labels))
if not isfinite(sse):
raise ValueError("Rescale the data: SSE overflow")
return labels, centers, sse
previous = labels[:]
raise RuntimeError("Assignments did not stabilize within max_iter")
rows = [(0, 0), (0, 2), (10, 0), (10, 2)]
labels, centers, sse = kmeans(rows, k=2)
print("labels:", labels)
print("centers:", centers)
print("SSE:", sse)
# labels: [0, 0, 1, 1]
# centers: [(0.0, 1.0), (10.0, 1.0)]
# SSE: 4.0Runnable base R k-means example
# Base R only. Each row is a numeric observation.
x <- rbind(c(0, 0), c(0, 2), c(10, 0), c(10, 2))
colnames(x) <- c("x", "y")
# Explicit starting centers remove random initialization from this example.
initial <- rbind(c(0, 0), c(10, 0))
fit <- stats::kmeans(x, centers = initial, iter.max = 100, algorithm = "Lloyd")
print(fit$cluster) # 1 1 2 2 (R numbers clusters from 1)
print(fit$centers) # rows (0,1) and (10,1)
print(fit$tot.withinss) # 4
stopifnot(identical(unname(fit$cluster), c(1L, 1L, 2L, 2L)))
stopifnot(isTRUE(all.equal(unname(fit$centers), rbind(c(0,1), c(10,1)))))
stopifnot(isTRUE(all.equal(fit$tot.withinss, 4)))Descriptive unsupervised grouping, not classification, person assessment or a claim of objectively correct clusters. Euclidean distance; deterministic original implementations. Hierarchical mode capped at500rows; no demographic inference.
Common questions
- What is k-means clustering, and what do k, centroid and inertia mean?
- K-means is unsupervised machine learning: it groups numeric rows without supplied class labels. k is the number of groups you choose, from 2 to 20 here. A centroid is the column-by-column arithmetic mean of the rows in one group. Within-cluster SSE, also called inertia in k-means, is the sum over rows and selected columns of (value - assigned centroid value) squared, using original values or z-scores according to the scaling setting. That is the objective the assignment and mean-update steps reduce; it is not a classification accuracy score or proof that the groups have a real-world meaning.
- What data can I cluster?
- Paste a comma-separated CSV or choose a local UTF-8 file up to 5 MB. Supply 2 to 2,000 data rows, up to 64 source columns and 1 to 20 selected numeric columns. Deselect identifiers and other text columns. Missing, malformed, overflowing and underflowing selected numbers produce a row and column error instead of being deleted or replaced.
- What are the steps of the k-means algorithm here?
- The integer seed selects the first source row. Each remaining center is the row farthest from its nearest existing center, with earlier rows breaking ties. The repeated steps are: assign each row to its nearest center using Euclidean distance, replace each center with the arithmetic mean of its assigned rows, then repeat. Assignment ties choose the lowest cluster number. The run stops when assignments stabilize or after 100 complete iterations. A fixed dataset, column order, settings and seed reproduce the same run.
- Can I follow a solved k-means and dendrogram example?
- Choose Load original example, keep X and Y selected, k = 2, seed = 42 and scaling off, then press Run clustering. The points (0,0), (0,2), (10,0) and (10,2) form the two vertical pairs, with centroids (0,1) and (10,1). Each row contributes 1 to SSE, so the total is 4. Inspect a complete iteration to compare assignments and centroids. For a dendrogram, choose Hierarchical and Average linkage and run again: each vertical pair first merges at distance 2, then the two pairs merge at about 10.099. Cluster numbers can differ between methods.
- What happens if a k-means cluster is empty?
- The empty cluster receives the farthest point from its current assigned center whose cluster still contains more than one row. Equal distances choose the earliest source row. Every repair identifies the row, donor and receiving cluster in the trace and CSV. Duplicate points remain separate rows.
- When should I use z-score scaling?
- Scaling changes the distance model by subtracting each selected column mean and dividing by its sample standard deviation, using n minus 1. It can make differently sized units comparable for exploration, but it does not establish the correct grouping. Constant columns cannot be z-scored. The scatter plot retains original units, while scaled centroids and SSE are labelled as z-score quantities.
- How does hierarchical clustering differ from k-means?
- K-means repeatedly assigns rows to k centroids. Hierarchical clustering starts with each row alone and merges the nearest active groups into a complete tree, then cuts it to your chosen 2 to 20 groups. Single linkage uses the nearest pair between groups, complete linkage the farthest pair, and average linkage the mean of every cross-group pair distance. The tree supports up to 500 rows. Equal-distance merges use stable node order, so the selected cut can split merges at a tied dendrogram height.
- What are the advantages, limitations and uses of k-means?
- It gives a compact description of numeric observations through group sizes, centroids and a visible trace. You can explore measurements of objects, such as length and width, after choosing meaningful columns and units. You must choose k; results depend on initialization, scaling and unusual values, and compact groups around means may poorly describe elongated or unevenly dense patterns. This tool does not infer demographic groups, assess people or provide customer-segmentation recommendations.
- How do k-means, KNN and spectral clustering differ?
- K-means discovers groups from unlabelled numeric rows. K-nearest neighbors (KNN) uses nearby labelled examples for prediction or classification. Spectral clustering uses a similarity graph and its eigenvectors before grouping, so it can represent relationships beyond distance to an arithmetic mean. This workspace runs k-means and agglomerative hierarchical clustering; it does not run KNN or spectral clustering.
- Does the tool calculate an elbow or choose the best k?
- There is no automatic elbow chart or best-k recommendation. You can change Clusters / hierarchy cut, run again and record the displayed SSE for several k values while keeping the data, selected columns, scaling and seed fixed. An elbow is a bend in that SSE-versus-k curve; it is a heuristic rather than an accuracy test, and changing the distance model makes the values unsuitable for the same comparison.
- Can I animate the iterations or export a GIF?
- Inspect a complete iteration changes the scatter plot, centroids, assignments and SSE to that completed step. Projection SVG exports the currently selected view. There is no autoplay animation, video or animated GIF export.
- What is included in the exports?
- The CSV contains every selected original value, final assignment, metric and original centroid, scaling parameter, complete k-means iteration assignment and empty-cluster repair, plus every hierarchical merge when applicable. Project JSON includes the source CSV, settings, projection and results. Restoring a project validates its inputs and requires a new run; saved result values are not trusted. SVG exports contain the current projection or complete dendrogram.
- Are my data uploaded or saved automatically?
- No. CSV parsing and clustering run locally in a cancellable worker, and this tool does not persist your data. Editing or cancelling terminates pending work and invalidates stale results. Files are created only when you request an export.
- Is this a Python, R, MATLAB or Excel implementation?
- The interactive calculation uses this tool’s browser implementation. The expandable examples in the tool provide complete standard-library Python source and a base R Lloyd-algorithm example with inputs, starting centers, assignments, centroids and SSE. Run the Python example in Python 3, including Python Playground, or the R example in R. They are separate language examples: this page does not execute Python or R, install scikit-learn or TensorFlow, run MATLAB, or provide an Excel workbook. The Python first-center rule differs from the browser’s seeded rule. No hierarchy, heatmap or anomaly-detector output is supplied by these k-means examples.
- Where can I find k-means pseudocode or source code?
- Open Browser k-means pseudocode under the result area for the actual validation, optional scaling, initialization, distance ties, empty-cluster repair, mean update and stopping rules. The Python and R sections beside it contain selectable runnable code and a four-point example with centroids (0,1) and (10,1) and SSE 4. Python labels begin at 0; R labels begin at 1.
Descriptive unsupervised grouping, not classification, person assessment or a claim of objectively correct clusters. Euclidean distance; deterministic original implementations. Hierarchical mode capped at500rows; no demographic inference.