Onderzoek een puntenwolk met PCA en k-means
Maak driedimensionale data, leer een projectie, train echte clusters en vergelijk hun scheiding.
De opdracht, leerdoelen en voorkennis
- Je combineert standaardisatie, PCA en k-means in een uitvoerbaar notebook.
- Je vergelijkt aantallen clusters met een intrinsieke maat en benoemt wat die maat niet bewijst.
- Je tekent de daadwerkelijk berekende projectie en centra.
Lees eerst: Laat ieder voorbeeld een keer onbekend zijn · Zoek groepen zonder antwoordlabels · Bewaar de richting met de meeste variatie.
Je hebt een puntenwolk met drie features en wilt weten of er zichtbare groepen in zitten. Kun je die in twee dimensies bekijken zonder de groepering meteen voor waarheid aan te nemen? In dit notebook combineer je standaardisatie, PCA en k-means en controleer je wat de grafiek en interne scores werkelijk laten zien.
We maken bewust drie compacte wolken. Dat geeft een controleerbare proef, geen bewijs dat echte planten of klanten ook drie nette groepen vormen. De algoritmen krijgen geen antwoordlabels mee.
1. Maak een herhaalbare wolk met echte variatie
Een centrum bepaalt waar een groep ongeveer ligt; normale afwijkingen geven ieder punt een eigen positie. Gebruik drie centra: [−3,0,1], [3,1,−1] en [0,4,2]. Per centrum trek je twintig punten met standaardafwijking 0.3 in iedere feature.
import numpy as np
rng = np.random.default_rng(42)
delen = []
centra = [(-3, 0, 1), (3, 1, -1),
(0, 4, 2)]
for centrum in centra:
groep = rng.normal(
loc=centrum, scale=.3,
size=(20, 3))
delen.append(groep)
x = np.vstack(delen)
print(x.shape) # (60, 3)
default_rng maakt een lokale generator; loc, scale en size bepalen centrum, standaardafwijking en vorm. vstack stapelt de matrices onder elkaar. Maak maak_wolk() met die generator en bewaar de uitkomst als x.
De rijvolgorde verraadt in deze simulatie de constructie, maar wordt geen extra feature. Twintig kopieën van elk centrum zouden geen willekeurige wolk zijn. Controleer daarom zowel de vorm als de variatie.
012. Leer één vast assenstelsel
De drie features hebben verschillende spreiding. Hier kiezen we bewust voor standaardisatie, gevolgd door PCA met twee componenten. Je kent beide stappen afzonderlijk; nu bewaren we de bibliotheekobjecten om latere punten exact hetzelfde te verwerken.
from sklearn.preprocessing import (
StandardScaler)
from sklearn.decomposition import PCA
scaler = StandardScaler()
scaled = scaler.fit_transform(x)
pca = PCA(
n_components=2, svd_solver="full")
z = pca.fit_transform(scaled)
print(z.shape) # (60, 2)
svd_solver="full" kiest een volledige matrixontbinding; de bibliotheek gebruikt daarvoor SVD in plaats van jouw eigen covariantie-eigenvectorroute. explained_variance_ratio_ bevat het aandeel variantie per behouden component. Optellen vertelt hoeveel van de geschaalde variatie deze twee assen bewaren.
Maak leer_projectie(x) als (state,z), met scaler en pca in state. Voor nieuwe punten roep je alleen transform aan:
nieuw = np.array([[0., 1., 1.]])
nieuw_scaled = scaler.transform(nieuw)
nieuw_z = pca.transform(nieuw_scaled)
print(nieuw_z.shape) # (1, 2)
Opnieuw fitten zou centrum, schaal en assenstelsel verplaatsen. Veel behouden variantie bewijst bovendien nog geen goede clusters. Bekijk straks de punten en de score naast elkaar.
023. Fit clusters met meerdere starts
Gebruik KMeans met n_init=10 en random_state=7. De tien initialisaties verminderen het risico van een ongunstige start; ze garanderen geen wereldwijd optimum. fit_predict leert de centra en geeft één clusterindex per punt.
from sklearn.cluster import KMeans
model = KMeans(
n_clusters=3, n_init=10,
random_state=7)
labels = model.fit_predict(z)
print(model.cluster_centers_.shape)
# (3, 2)
print(labels.shape) # (60,)
Maak cluster_fit(z,k) als (model,labels) en train k = 3 op de opgeslagen z. Clusterindices zijn namen: een verwisseling van 0 en 1 verandert de groepering niet. Voor nieuwe punten gebruik je dezelfde projectie en daarna model.predict(nieuw_z).
4. Begrijp silhouette vóór je de ranglijst leest
De silhouette-score vergelijkt voor ieder punt de eigen groep met de dichtstbijzijnde andere groep. a is de gemiddelde afstand tot de overige punten van het eigen cluster, dus zonder het punt zelf. b is het kleinste gemiddelde van de afstanden tot alle punten van een ander cluster.
Neem los van het notebook vier punten op een lijn: 0 en 2 vormen één groep, 10 en 12 de andere. Voor punt 0 is a = 2 en b = (10+12)/2 = 11, dus s = 9/11. Voor punt 2 is b = 9 en s = 7/9; de andere twee punten geven gespiegeld dezelfde scores.
Het gemiddelde is 79/99, ongeveer 0.79798. Veelgemaakte fout: b vervangen door de afstand tot het andere centrum; dat is niet de definitie. Deze losse speelgoedproef overschrijft de notebooklabels niet.
from sklearn.metrics import (
silhouette_score)
toy = np.array([[0., 0.], [2., 0.],
[10., 0.], [12., 0.]])
toy_labels = [0, 0, 1, 1]
score = silhouette_score(toy, toy_labels)
print(score)
# ongeveer 0.79798
Een hoge score wijst op compacte, gescheiden groepen; rond nul liggen punten dichter bij een grens en een negatieve score kan een ongunstige toewijzing signaleren. Bij een cluster met één punt gebruikt de bibliotheek de conventie nul voor dat punt. De score vereist minstens twee clusters en minder clusters dan punten.
5. Bereken de vergelijking en teken dezelfde arrays
Maak cluster_audit(z,model,labels). Fit kandidaten k = 2,3,4 op dezelfde z en bereken hun silhouette; geef punten, clusters, silhouette en vergelijking terug. De eerste silhouette hoort bij de meegegeven labels, de vergelijking bij de drie opnieuw gefitte kandidaten.
import matplotlib.pyplot as plt
plt.figure(figsize=(6, 4))
for label in sorted(set(labels)):
groep = z[labels == label]
px, py = groep[:, 0], groep[:, 1]
naam = "cluster " + str(label)
plt.scatter(px, py, label=naam)
c = model.cluster_centers_
plt.scatter(c[:, 0], c[:, 1],
marker="x", color="black",
s=90, label="centra")
plt.xlabel("Hoofdcomponent 1")
plt.ylabel("Hoofdcomponent 2")
plt.title("Groepen in de projectie")
plt.legend()
plt.tight_layout()
De booleaanse selectie z[labels==label] kiest de punten van één cluster. Teken hun echte coördinaten en de centra uit hetzelfde model. Vergelijk daarna de tabel met de grafiek: wint k = 3, en zie je ook waarom?
Dit is verkennende beoordeling op dezelfde data, geen onafhankelijke test. Een hoge interne score bewijst geen bruikbare betekenis, en een andere projectie of schaling kan een andere ranglijst geven. Bewaar x, z, state, model en labels intact tijdens de audit.
04Wat neem je mee?
- Het vaste voorbereidingspad koppelt nieuwe punten aan dezelfde projectie en centra.
- Silhouette vergelijkt gemiddelde afstanden tot groepen; ze gebruikt geen antwoordlabels.
- Een berekende tabel en echte grafiek helpen interpreteren, maar bewijzen geen natuurlijke categorieën.
Hierna bouw je modellen die hun eigen tussenliggende representaties leren: neurale netwerken.
Bronnen: scikit-learn — clustering ↗ scikit-learn — principal component analysis ↗ · Gecontroleerd 2026-10-28.
Maak herhaalbare driedimensionale punten
Maak met één lokale default_rng(42) drie groepen van twintig punten, elk met drie features en standaardafwijking 0.3, rond de genoemde centra. Stapel de echte trekkingen en bewaar x; dubbele centrumkopieën zijn geen wolk.
Bekijk voorbeeld
import numpy as np
def maak_wolk():
rng = np.random.default_rng(42)
delen = []
centra = [(-3, 0, 1), (3, 1, -1), (0, 4, 2)]
for centrum in centra:
groep = rng.normal(
loc=centrum, scale=.3, size=(20, 3))
delen.append(groep)
return np.vstack(delen)
x = maak_wolk()
print("Punten en features:", x.shape)Leer een kleinere representatie
Fit StandardScaler en daarna PCA met twee componenten en full-solver. Bewaar beide objecten in state en de projectie in z. Bekijk behouden variantie; gebruik voor nieuwe punten alleen dezelfde transform-stappen.
Bekijk voorbeeld
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
def leer_projectie(x):
scaler = StandardScaler()
scaled = scaler.fit_transform(x)
pca = PCA(n_components=2, svd_solver="full")
z = pca.fit_transform(scaled)
return {"scaler": scaler, "pca": pca}, z
state, z = leer_projectie(x)
aandeel = state["pca"].explained_variance_ratio_.sum()
print("Behouden variantie:", float(aandeel))Fit k-means met meerdere starts
Fit KMeans met k clusters,n_init=10,random_state=7 en geef model/labels terug. Train de notebookprojectie met k=3; clusterindices zijn verwisselbare namen, geen antwoordlabels.
Bekijk voorbeeld
from sklearn.cluster import KMeans
def cluster_fit(z, k):
model = KMeans(
n_clusters=k, n_init=10, random_state=7)
labels = model.fit_predict(z)
return model, labels
model, labels = cluster_fit(z, 3)
print("Clustergroottes:", np.bincount(labels))Vergelijk interne scheiding en bekijk de punten
Vergelijk silhouette voor k=2,3,4 op dezelfde projectie en teken de echte punten per label plus centra van het meegegeven model. a sluit het punt zelf uit; b is de kleinste gemiddelde afstand naar een andere groep. Bewaar alle invoer en notebookvariabelen intact.
Bekijk voorbeeld
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score
def cluster_audit(z, model, labels):
vergelijking = {}
for k in [2, 3, 4]:
kandidaat, groepen = cluster_fit(z, k)
score = silhouette_score(z, groepen)
vergelijking[k] = float(score)
plt.figure(figsize=(6, 4))
for label in sorted(set(labels)):
groep = z[labels == label]
naam = "cluster " + str(label)
plt.scatter(groep[:, 0], groep[:, 1], label=naam)
c = model.cluster_centers_
plt.scatter(c[:, 0], c[:, 1], marker="x",
color="black", s=90, label="centra")
plt.xlabel("Hoofdcomponent 1")
plt.ylabel("Hoofdcomponent 2")
plt.title("Groepen in de projectie")
plt.legend()
plt.tight_layout()
score = silhouette_score(z, labels)
return {"punten": len(z), "clusters": len(set(labels)),
"silhouette": float(score), "vergelijking": vergelijking}
rapport = cluster_audit(z, model, labels)
print("Verkennende audit:", rapport)