Ga naar hoofdinhoud

K-means-clustering in R-tutorial

Leer wat k-means is en ontdek waarom het een van de meest gebruikte clusteringalgoritmen in data science is
Bijgewerkt 2 okt 2026  · 8 min lezen

Verken met AI

ChatGPTClaudePerplexity

K-means is een populaire onbewaakte machinelearningtechniek waarmee je clusters (groepen vergelijkbare datapunten) in de data kunt identificeren. In deze tutorial leer je k-means-clustering in R met tidymodels, ggplot2 en ggmap. We behandelen:

  • hoe het k-means-clusteringalgoritme werkt
  • hoe je data visualiseert om te bepalen of clusteren zinvol is
  • een casestudy over het trainen en tunen van een k-means-clusteringmodel met een Airbnb-reviewdataset

Voordat we de diepte ingaan, raden we je aan je R‑basiskennis zoals vectors en data frames op te frissen. Ben je er al klaar voor? Sla het dan over en laten we beginnen!

Een overzicht van k-means-clustering

Clusteringmodellen proberen data in afzonderlijke “clusters” of groepen te verdelen. Dit kan op zichzelf als analyse worden gebruikt, of als feature in een bewaakt leeralgoritme.

image1.png

Links in het diagram hierboven zien we twee duidelijke verzamelingen punten zonder labels, waarbij vergelijkbare datapunten dezelfde kleur hebben. Door een k-means‑model op deze data te fitten (rechts) komen er twee afzonderlijke groepen naar voren (weergegeven met verschillende cirkels en kleuren). 

In twee dimensies kunnen mensen deze clusters makkelijk scheiden, maar bij meer dimensies heb je een model nodig.

Het k-means-algoritme

Stel je wilt meerdere fruitsalades maken, elk met vergelijkbare vruchten.

  1. Je kiest hoeveel fruitsalades je wilt maken (k). Bijvoorbeeld: je besluit 3 fruitsalades te maken.
  2. Je kiest willekeurig 3 vruchten van de tafel als startende "representanten" van elke salade.
  3. Je loopt de tafel rond en wijst elke vrucht toe aan de salade waarvan de representant er het meest op lijkt. Zo gaan appels misschien bij peren, sinaasappels bij mandarijnen en bananen bij bakbananen.
  4. Als alle vruchten zijn toegewezen, bepaal je het nieuwe "centrum" van elke fruitsalade door het gemiddelde van de vruchtkenmerken in elke groep te berekenen (bijv. grootte, kleur, smaak). Deze nieuwe centra worden de bijgewerkte representanten.
  5. Je herhaalt stap 3 en 4 meerdere keren: je wijst vruchten toe aan de meest vergelijkbare representant en werkt die representant telkens bij totdat de groeperingen niet meer veranderen.

image4.png

Algemener gezegd:

  1. Kies het aantal clusters, k.
  2. Selecteer willekeurig k punten (clusters van grootte 1).
  3. Bereken de afstand tussen elk punt en het centroid en wijs elk datapunt toe aan het dichtstbijzijnde cluster.
  4. Bereken voor elk cluster het centroid (gemiddelde positie).
  5. Blijf stap 3–4 herhalen totdat de clusters niet meer veranderen of het maximale aantal iteraties is bereikt.

Omdat k-means de clustermiddens willekeurig kiest, moet je het algoritme meerdere keren uitvoeren; dit aantal stel je zelf in. Zo vind je de beste oplossing door een maat voor modelkwaliteit te minimaliseren. Deze maat heet de total within-cluster sum of squares (WCSS): de som van de afstanden tussen de datapunten en het bijbehorende centroid voor elk cluster. Hoe kleiner deze maat, hoe beter het model.

Airbnb Listings‑dataset

Alle inhoud van deze tutorial draait om Airbnb‑verhuurlijsten in Kaapstad, beschikbaar op DataLab. De data bevat verschillende soorten informatie, zoals hosts, prijs, aantal reviews, coördinaten, enzovoort. 

Ook al lijkt de dataset veel informatie over vakantieverhuur te bieden, er zijn nog steeds duidelijke vragen te beantwoorden. Stel bijvoorbeeld dat Airbnb zijn dataspecialisten vraagt de segmentatie van de listings op het platform in Kaapstad te onderzoeken. 

Elke stad is anders en heeft andere behoeften, afhankelijk van de lokale cultuur. Het identificeren van clusters in deze stad kan helpen om nieuwe inzichten te halen die de tevredenheid van bestaande klanten verhogen met passende loyaliteitsstrategieën, en zo churn te voorkomen. Tegelijk is het cruciaal om nieuwe mensen aan te trekken met gerichte promoties. 

Airbnb-data visualiseren

Voordat we k-means toepassen, willen we de relatie tussen de variabelen verder onderzoeken door naar de correlatiematrix te kijken. Voor de leesbaarheid ronden we af op twee decimalen.

library(dplyr)
airbnb |>
  select(price, minimum_nights, number_of_reviews) |>
  cor(use = "pairwise.complete.obs") |>
  round(2)

image8.png

Uit de output lijkt er een licht negatieve relatie te zijn tussen prijs en aantal reviews: hoe hoger de prijs, hoe lager het aantal reviews. Hetzelfde geldt voor minimumaantal nachten en aantal reviews. Omdat het minimumaantal nachten weinig invloed heeft op de prijs, willen we vooral onderzoeken hoe prijs en aantal reviews zich tot elkaar verhouden:

library(ggplot2)
ggplot(data, aes(number_of_reviews, price, color = room_type, shape = room_type)) +
    geom_point(alpha = 0.25) +
    xlab("Number of reviews") +
    ylab("Price")

image3.png

De scatterplot laat zien dat de kosten van accommodaties, met name hele huizen, hoger zijn wanneer er weinig reviews zijn, en lijken te dalen naarmate het aantal reviews toeneemt. 

Normalisatie

Voor we het model fitten, is er nog een stap nodig. K-means is gevoelig voor variabelen met onvergelijkbare eenheden, wat tot misleidende resultaten kan leiden. In dit voorbeeld telt het aantal reviews in tientallen of honderden, maar de prijs in tienduizenden. Zonder bewerking lijken prijsverschillen dus groter dan verschillen in reviews, terwijl we willen dat deze variabelen gelijk worden behandeld.

Om dit te voorkomen, moeten de variabelen naar een vergelijkbare schaal worden getransformeerd. Zo kunnen ze correct worden vergeleken met de afstandsmaat.

Er zijn verschillende methoden om dit aan te pakken. De bekendste is standaardisatie: het gemiddelde van de featurewaarde aftrekken en vervolgens delen door de standaarddeviatie. Deze techniek levert features op met een gemiddelde van 0 en een standaarddeviatie van 1. 

Je kunt de variabelen schalen met de functie scale(). Omdat dit een matrix retourneert, is de code in base R-stijl hier netter dan in tidyverse-stijl.

airbnb[, c("price", "number_of_reviews")] = scale(airbnb[, c("price", "number_of_reviews")])

Het model fitten en evalueren

We kunnen nu de clusters van listings identificeren met k-means. Om te beginnen voeren we k-means uit met 3 clusters en nstart gelijk aan 20. Deze laatste parameter zorgt ervoor dat k-means 20 keer met verschillende willekeurige starts wordt uitgevoerd; daarna kiest R automatisch het beste resultaat op basis van de total within-cluster sum of squares. We zetten ook een seed om bij elk runnen dezelfde resultaten te kunnen reproduceren.

# Get the two columns of interest
airbnb_2cols <- data[, c("price", "number_of_reviews")]

set.seed(123)
km.out <- kmeans(airbnb_2cols, centers = 3, nstart = 20)
km.out

Output:

K-means clustering with 3 clusters of sizes 785, 37, 16069

Cluster means:
     price number_of_reviews
1 14264.102         5.9401274
2 83051.541         0.6756757
3  1589.879        18.2649200

Clustering vector:
   [1] 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
  [37] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
  [73] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 [109] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3
 [145] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 3 3
...
[16777] 3 3 3 3 3 3 3 3 3 3 1 3 3 1 3 1 3 3 3 3 3 1 3 3 3 3 3 3 3 3 1 3 3 3 3 3
[16813] 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3
[16849] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3
[16885] 3 3 3 3 3 3 3

Within cluster sum of squares by cluster:
[1] 41529148852 49002793251 33286433394
(between_SS / total_SS =  74.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"

Uit de output zien we dat er drie verschillende clusters zijn gevonden met groottes 785, 37 en 16069. Voor elk cluster worden de gekwadrateerde afstanden tussen de observaties en de centroiden berekend. Elke observatie wordt zo aan een van de drie clusters toegewezen. 

Ook al lijkt dit een goed resultaat, de beste manier om het optimale model te vinden is verschillende modellen met uiteenlopende aantallen clusters te proberen. We beginnen dus met een model met één cluster, daarna twee clusters, enzovoort. Deze procedure houd je bij met een grafische weergave, de screeplot, waarin het aantal clusters op de x‑as staat en WCSS op de y‑as. 

In deze casestudy bouwen we 10 k-means‑modellen, elk met een ander aantal clusters, tot maximaal 10. Bovendien gebruiken we slechts een deel van de dataset: alleen prijs en aantal reviews. Om de screeplot te tekenen, slaan we de total within-cluster sum of squares van alle modellen op in de variabele wss.

# Decide how many clusters to look at
n_clusters <- 10

# Initialize total within sum of squares error: wss
wss <- numeric(n_clusters)

set.seed(123)

# Look over 1 to n possible clusters
for (i in 1:n) {
  # Fit the model: km.out
  km.out <- kmeans(airbnb_2cols, centers = i, nstart = 20)
  # Save the within cluster sum of squares
  wss[i] <- km.out$tot.withinss
}

# Produce a scree plot
wss_df <- tibble(clusters = 1:n, wss = wss)
 
scree_plot <- ggplot(wss_df, aes(x = clusters, y = wss, group = 1)) +
    geom_point(size = 4)+
    geom_line() +
    scale_x_continuous(breaks = c(2, 4, 6, 8, 10)) +
    xlab('Number of clusters')
scree_plot

image6.png

Als we naar de screeplot kijken, zien we dat de total within-cluster sum of squares afneemt naarmate het aantal clusters toeneemt. Het criterium om het aantal clusters te kiezen is door een "elleboog" te vinden: een punt waar de WCSS veel langzamer daalt na het toevoegen van een extra cluster. In dit geval is dat niet zo duidelijk, dus voegen we horizontale lijnen toe om een beter beeld te krijgen:

scree_plot +
    geom_hline(
        yintercept = wss, 
        linetype = 'dashed', 
        col = c(rep('#000000',4),'#FF0000', rep('#000000', 5))
    )

image7.png

Als je nog eens kijkt, lijkt de keuze nu een stuk duidelijker, toch? Uit deze visualisatie kunnen we concluderen dat het beste is om het aantal clusters op 5 te zetten. Na k=5 nemen de verbeteringen van de modellen sterk af. 

# Select number of clusters
k <- 5
set.seed(123)
# Build model with k clusters: km.out
km.out <- kmeans(airbnb_2cols, centers = k, nstart = 20)

We kunnen opnieuw de scatterplot tussen prijs en aantal reviews visualiseren. We kleuren de punten ook op basis van de cluster‑ID:

data$cluster_id <- factor(km.out$cluster)
ggplot(data, aes(number_of_reviews, price, color = cluster_id)) +
    geom_point(alpha = 0.25) +
    xlab("Number of reviews") +
    ylab("Price")

image2.png

We zien dat:

  • Er een paar listings zijn met hoge prijzen en weinig reviews, groen en fuchsia gekleurd
  • Er is ook een groep datapunten met zowel lage prijzen als weinig reviews, rood gemarkeerd
  • Daarna lijkt de prijs te stabiliseren zodra het aantal reviews toeneemt.

Wanneer faalt k-means?

Let op: k-means is simpel en makkelijk toe te passen, maar niet altijd de beste keuze om data in groepen te segmenteren; het kan mislukken. Het gaat uit van sferische clusters en werkt daar goed voor, terwijl groepen met verschillende groottes en dichtheden minder goed worden gevangen door dit algoritme. 

Als aan deze voorwaarden niet is voldaan, kies je beter een alternatief zoals DBSCAN of BIRCH. Clustering in Machine Learning: 5 Essential Clustering Algorithms geeft een goed overzicht van clusteringbenaderingen als je dieper wilt duiken.

Ga een stap verder

We kunnen concluderen dat k-means een van de meest gebruikte clusteringalgoritmen blijft om duidelijke subgroepen te identificeren, ook al is het niet altijd perfect. Nu heb je de kennis om het met R toe te passen in andere casestudy’s. 

Wil je deze methode verder uitdiepen, bekijk dan de cursus Unsupervised Learning in R. Er is ook Cluster Analysis in R en An Introduction to Hierarchical Clustering in Python voor een compleet overzicht van beschikbare clusteringaanpakken, handig wanneer k-means niet genoeg betekenisvolle inzichten uit je data haalt. Wil je ook bewaakte modellen met R verkennen, dan is deze cursus aan te raden!


Eugenia Anello's photo
Author
Eugenia Anello
LinkedIn

Data Scientist - CRIF

Onderwerpen
Machine Learning
Data Science
R

Leer meer over R

Cursus

Introductie tot R

4 uur
3.1M
Beheers de basis van data-analyse in R, inclusief vectors, lists en data frames, en oefen met echte datasets.
Bekijk detailsRight Arrow
Start Cursus
Meer bekijkenRight Arrow
Gerelateerd

blog

AI vanaf nul leren in 2026: een complete gids van de experts

Ontdek alles wat je moet weten om in 2026 AI te leren, van tips om te beginnen tot handige resources en inzichten van industrie-experts.
Adel Nehme's photo

Adel Nehme

15 min

Meer BekijkenMeer Bekijken