Find hidden patterns
Group sensor readings, compress features, and spot unusual activity without labels. This module has 3 lessons and takes about 1h 30m. Each lesson has a practice exercise that runs real Python in your browser and a short quiz.
Start module 14 in the Learning LabGroup machines with k-means
Implement the two repeating steps of k-means and inspect machine groups.
A workshop records temperature and vibration for six fictional motors. There are no fault labels. Clustering groups readings that look similar, which can help an engineer decide what to inspect next.
K-means alternates between assigning each row to its nearest centre and moving each centre to the average of its assigned rows. Standardise the two columns first so temperature does not dominate just because its numbers are larger.
The group numbers have no built-in meaning. A group is not a diagnosis, and choosing two groups does not prove that two operating states exist. Compare several choices and inspect the original units.
See it in action
import numpy as np
# Synthetic rows: temperature in C, vibration in mm/s.
readings = np.array([[40, 1.0], [42, 1.2], [39, 0.9],
[78, 4.8], [80, 5.0], [76, 4.6]])
mean, scale = readings.mean(axis=0), readings.std(axis=0)
X = (readings - mean) / scale
centres = X[[0, 3]].copy()
for step in range(20):
distances = ((X[:, None, :] - centres[None, :, :]) ** 2).sum(axis=2)
labels = distances.argmin(axis=1)
updated = centres.copy()
for group in range(2):
members = X[labels == group]
if len(members): # Keep the old centre if a group is empty.
updated[group] = members.mean(axis=0)
if np.allclose(updated, centres):
break
centres = updated
print("Groups:", labels.tolist())
print("Centres in original units:", (centres * scale + mean).round(2))
A cluster is a useful pattern to investigate, not a verified label.
Compress correlated sensors with PCA
Project related sensor columns onto a smaller set of directions and measure reconstruction error.
Imagine a solar plant with three sensors that mostly move together as sunlight changes. Principal component analysis, or PCA, finds directions that capture variation across the columns. One new coordinate can summarise much of a strongly shared pattern.
Centre and scale the training data, then use a singular value decomposition to find the directions. Project onto the first direction and reconstruct the original columns to see what information was lost.
A high explained-variance ratio means much of the numeric variation was preserved. It does not mean the retained direction is useful for your prediction target. Fit PCA on training rows only when it is part of a prediction workflow.
See it in action
import numpy as np
rng = np.random.default_rng(14)
sunlight = np.linspace(1, 6, 12)
sensors = np.column_stack([sunlight, 2 * sunlight, 0.5 * sunlight])
sensors += rng.normal(0, 0.08, sensors.shape)
mean, scale = sensors.mean(axis=0), sensors.std(axis=0)
X = (sensors - mean) / scale
_, singular_values, directions = np.linalg.svd(X, full_matrices=False)
component = directions[:1]
compressed = X @ component.T
reconstructed = compressed @ component
ratio = singular_values[0] ** 2 / (singular_values ** 2).sum()
error = np.mean((X - reconstructed) ** 2)
print("Shape:", X.shape, "->", compressed.shape)
print("Variance retained:", round(float(ratio), 4))
print("Reconstruction MSE:", round(float(error), 4))
Use reconstruction error and the later task to judge whether compression is useful.
Flag unusual payments with a robust score
Build a median-based anomaly score and separate alerts from confirmed events.
A fictional laboratory processes routine supplier payments. A very large payment may be an input mistake, a legitimate equipment purchase, or something that needs review. An anomaly detector can prioritise a queue, but it cannot establish why a payment is unusual.
The median is less affected by a few extreme values than the mean. The median absolute deviation, or MAD, measures typical distance from the median. Dividing a new amount's distance by MAD gives a simple, robust score.
Fit the median and MAD using a reference period. The threshold of 5 below is a teaching choice, not a calibrated fraud rule. When MAD is zero, the example raises an error instead of inventing a reliable score.
See it in action
import numpy as np
reference = np.array([98, 100, 102, 99, 101, 97, 103], dtype=float)
incoming = np.array([101, 99, 280, 104], dtype=float)
centre = np.median(reference)
mad = np.median(np.abs(reference - centre))
if mad == 0:
raise ValueError("Reference data has no usable MAD scale")
scores = np.abs(incoming - centre) / mad
for amount, score in zip(incoming, scores):
print(f"Amount {amount:.0f}: score={score:.1f}, review={score > 5}")
Keep anomaly scores, review decisions, and confirmed outcomes as separate fields.
Free, open and run by a nonprofit.
This page is part of Python to AI, a free course of 20 modules and 60 lessons in the engineering.fit( ) Learning Lab. The Learning Lab is built and run by engineering.fit( ), a nonprofit founded by Abdullateef Opeyemi Bakare that helps African engineering students gain research experience. You do not need an account or pay a fee to learn.