R ile Denetimsiz Öğrenme: K-Means, Hiyerarşik Kümeleme ve PCA

149 ülkenin verisinde doğal gruplar var mı? K-means, hiyerarşik kümeleme ve PCA'yı uygulayıp sonuçların gerçekten orada olup olmadığını sorguluyorum.

  • Makine Öğrenmesi
  • İstatistik

Önceki üç yazıda hedefi bilinen problemlerle çalıştık: satışı tahmin ettik, mağazaları High/Low olarak sınıflandırdık. Şimdi etiketin olmadığı tarafa geçiyoruz. Elimizde yalnızca 149 ülkenin göstergeleri var ve soru şu: bu ülkeler arasında doğal gruplar var mı?

Serinin son yazısında World Happiness Report 2021 verisi üzerinde K-means kümeleme, hiyerarşik kümeleme ve temel bileşen analizini (PCA) uyguluyorum.

“Denetimsiz öğrenmede doğru cevap yoktur; yalnızca savunulabilir yorumlar vardır.”

1. Veri ve Ölçekleme

World Happiness Report 2021, 149 ülke için mutluluk skoru ve onu açıklamak için kullanılan altı gösterge içeriyor. Analize sekiz sayısal değişkenle giriyoruz; güven aralığı sütunlarını (üst/alt sınır ve standart hata) dışarıda bırakıyoruz çünkü bunlar skorun kendisinden türüyor ve aynı bilgiyi üç kez sayarlar.

Veriyi hazırlamak
hap <- read.csv("world-happiness-report-2021.csv", sep = ";", dec = ",")

sayisal <- hap[sapply(hap, is.numeric)]
sayisal <- sayisal[, !names(sayisal) %in% c(
  "Standard.error.of.ladder.score", "upperwhisker", "lowerwhisker")]

X <- scale(na.omit(sayisal))   # 149 x 8
Ölçekleme neden zorunlu? K-means ve hiyerarşik kümeleme mesafe üzerinden çalışır. Bu veride Logged.GDP.per.capita 6–11 arasında, Ladder.score ise 2–8 arasında değişiyor; aynı ölçekte olmayan değişkenler mesafeyi orantısız biçimde etkiler. scale() her değişkeni ortalaması 0, standart sapması 1 olacak şekilde dönüştürür ve hepsine eşit söz hakkı verir.

2. K-Means

K-means, gözlemleri K kümeye ayırır ve her kümenin merkezini (centroid) iteratif olarak günceller. Tek bir parametresi var ama o parametre analizin tamamını belirliyor: K kaç olmalı?

K-means çalıştırmak
library(factoextra)
library(cluster)

set.seed(123)
km <- kmeans(X, centers = 2, nstart = 25)

km$size          # 87 62
km$betweenss / km$totss   # 0.3472

fviz_cluster(km, data = X)
nstart = 25 neden önemli? K-means başlangıç merkezlerini rastgele seçer ve yerel bir optimuma takılabilir. nstart, algoritmayı farklı başlangıçlarla defalarca çalıştırıp en iyi çözümü seçer. Varsayılan değer 1'dir ve tek başına bırakıldığında sonuç tohuma göre belirgin biçimde değişebilir.

3. Kaç Küme? Üç Ölçüt

K değerine karar vermek için tek bir doğru yöntem yok. Üç yaygın ölçüte birlikte bakmak en sağlıklısı.

Elbow (dirsek) yöntemi

Farklı K değerleri için küme içi kareler toplamı hesaplanır. K arttıkça bu değer her zaman düşer; aranan şey düşüşün belirgin biçimde yavaşladığı nokta.

Küme içi kareler toplamı (k = 1…10)
  • k = 11184
  • k = 2773
  • k = 3627
  • k = 4535
  • k = 5474
  • k = 6438
  • k = 8382
  • k = 10343

En büyük düşüş 1'den 2'ye geçişte: 1184'ten 773'e, yani %35. İkinciden üçe geçişte düşüş %19'a, üçten dörde geçişte %15'e iniyor. Dirsek 2 ile 3 arasında.

Siluet skoru

Siluet, her gözlemin kendi kümesine ne kadar yakın ve diğer kümelerden ne kadar uzak olduğunu ölçer. −1 ile 1 arasında değer alır; yüksek olması daha iyi ayrışma demektir.

Ortalama siluet skoru
fviz_nbclust(X, kmeans, method = "silhouette")

# k=2 : 0.3095   <- en yuksek
# k=3 : 0.2696
# k=4 : 0.2692
# k=5 : 0.2666
# k=6 : 0.2230

Gap istatistiği

Gap istatistiği
set.seed(123)
gap <- clusGap(X, FUN = kmeans, nstart = 25, K.max = 10, B = 50)
fviz_gap_stat(gap)

Gap istatistiği, gözlenen küme içi dağılımı rastgele üretilmiş referans verilerle karşılaştırır. Üç ölçütün birlikte işaret ettiği değer k = 2.

Siluet 0,31 ne anlama geliyor? Yüksek bir değer değil. 0,31, kümelerin varlığına işaret ediyor ama sınırların keskin olmadığını da söylüyor. Ülkeler kesin ayrılmış iki kutupta değil, bir süreklilik üzerinde duruyor; kümeleme bu sürekliliği kullanışlı parçalara bölüyor. Bunu yazmak, "iki net küme bulduk" demekten çok daha dürüst.

4. Kümeleri Yorumlamak

Algoritma "1" ve "2" etiketlerini verir; bu etiketlere anlam yüklemek analistin işidir. Yöntem her zaman aynı: küme merkezlerine bakmak.

Değişken (ölçeklenmiş)Küme 1 (87 ülke)Küme 2 (62 ülke)
Ladder score (mutluluk)+0,673−0,945
Kişi başı GSYİH (log)+0,618−0,868
Sosyal destek+0,643−0,902
Sağlıklı yaşam beklentisi+0,643−0,903
Seçim özgürlüğü+0,460−0,645
Cömertlik−0,134+0,189
Yolsuzluk algısı−0,206+0,289

Tablo kendini anlatıyor. Küme 1'deki 87 ülke mutluluk, gelir, sosyal destek, sağlık ve özgürlükte ortalamanın üzerinde; yolsuzluk algısı ise ortalamanın altında. Küme 2'deki 62 ülke bütün bu göstergelerde tersi konumda.

İlginç olan cömertlik: tek değişken o ve ters yönde çalışıyor. Küme 2'deki ülkeler, geliri ve mutluluk skoru düşük olmasına rağmen cömertlik göstergesinde ortalamanın üzerinde. Bu, kümelemenin "zengin–fakir" ayrımından ibaret olmadığını gösteren küçük ama anlamlı bir ayrıntı.

Toplam değişkenliğin yalnızca %34,7'si kümeler arasında açıklanıyor (betweenss / totss). Geri kalan %65,3 küme içi farklılık; yani her iki grup da kendi içinde oldukça heterojen.

Kümelerde hangi ülkeler var?

Merkezler kümelerin ortalama profilini verir, ama bir kümeyi gerçekten anlamak için içine bakmak gerekir. En kullanışlı yöntem her kümenin merkezine en yakın gözlemleri listelemek: bunlar kümenin "tipik" üyeleridir.

Kümelerin tipik üyeleri
for (k in 1:2) {
  uyeler <- X[km$cluster == k, ]
  uzaklik <- apply(uyeler, 1, function(satir)
                     sqrt(sum((satir - km$centers[k, ])^2)))
  print(head(hap$Country.name[km$cluster == k][order(uzaklik)], 5))
}
KümeBüyüklükMerkeze en yakın beş ülkeMutluluk skoru (ort.)
Küme 187Mauritius, Kazakistan, Uruguay, Polonya, Tayvan6,26
Küme 262Etiyopya, Zambiya, Pakistan, Irak, Uganda4,52

İki kümenin mutluluk ortalaması arasındaki fark 1,74 puan ve %95 güven aralığı [1,53 – 1,95] (Welch t = 16,45; p < 0,001). Bu, kümelerin mutluluk skorunu kullanarak oluşturulduğu düşünülürse şaşırtıcı değil — ama farkın büyüklüğü, bölünmenin ne kadar belirgin olduğunu gösteriyor.

Daha ilginç olan sınırdaki ülkeler. Siluet değeri negatif olan gözlemler, kendi kümelerinden çok komşu kümeye benziyor demektir. Bu analizde böyle dört ülke var: Arnavutluk, Kuzey Makedonya, Türkiye ve Ukrayna.

Sınırdaki ülkeler hata değil, bulgudur. Türkiye'nin negatif siluet alması "algoritma yanlış sınıflandırdı" demek değil; refah göstergelerinde iki kümenin tam arasında durduğu anlamına geliyor. Kümeleme süreklilikleri kutulara böler ve kutuların kenarında duranlar her zaman olur. Bir raporda bu ülkeleri "Küme 1'dedir" diye kesin yazmak yerine "sınırda" diye işaretlemek daha dürüst.

Bu kümeler kararlı mı?

Kümeleme her veride bir sonuç üretir. Asıl soru, veri biraz değişseydi aynı kümelerin çıkıp çıkmayacağı. Standart test bootstrap kararlılığı: veriden tekrar tekrar örnek çekip kümelemeyi yeniden çalıştırmak ve orijinal kümelerin ne kadar korunduğunu Jaccard benzerliğiyle ölçmek.

Bootstrap kararlılık (B = 200)
jaccard <- function(a, b)
  length(intersect(a, b)) / length(union(a, b))

set.seed(7)
for (b in 1:200) {
  i <- sample(nrow(X), replace = TRUE)
  yeni <- kmeans(X[unique(i), ], centers = 2, nstart = 10)
  # her orijinal kume icin en iyi eslesen yeni kumenin Jaccard'i
}
# Kume 1: 0.9912   Kume 2: 0.9871
KümeOrtalama Jaccard0,75'in üzerinde kalma oranıYorum
Küme 1 (87 ülke)0,991%100Son derece kararlı
Küme 2 (62 ülke)0,987%100Son derece kararlı

Yaygın yorum kuralı şudur: 0,85'in üzeri "yüksek derecede kararlı", 0,75–0,85 arası "kararlı", 0,6'nın altı "güvenilmez". Her iki küme de 0,98'in üzerinde ve 200 bootstrap örneğinin tamamında 0,75'i geçiyor.

Düşük siluet, yüksek kararlılık — çelişki mi? Hayır, ikisi farklı şeyler ölçüyor. Siluet 0,31, kümelerin birbirinden ne kadar iyi ayrıldığını söylüyor: sınır bulanık. Jaccard 0,99 ise aynı bölünmenin tekrar edip etmediğini: ediyor. Yani sınır keskin değil ama her seferinde aynı yerden geçiyor. İkisini birlikte raporlamak, tek birine bakmaktan çok daha bilgilendirici.

Peki k = 3 deseydik?

Üç ölçüt de k = 2'yi işaret etmişti, ama kararın ne kaybettirdiğini görmek için k = 3'e bakmaya değer.

KümeBüyüklükTipik ülkelerAyırt eden özellik
144Pakistan, Etiyopya, Liberya, Uganda, ZambiyaBütün refah göstergelerinde düşük (−1,0 ile −1,2)
227Kanada, Avusturya, Almanya, Avustralya, İrlandaYüksek refah ve çok düşük yolsuzluk algısı (−1,54)
378Karadağ, Dominik Cumhuriyeti, Ekvador, Mauritius, MoldovaOrtada; cömertlik düşük (−0,45), yolsuzluk algısı yüksek

k = 3 çözümü açıklanan oranı %34,7'den %47,1'e çıkarıyor ve ortaya anlamlı bir orta grup koyuyor. Fakat yeni ayrımı yapan asıl değişken yolsuzluk algısı: ikinci küme, birinciden refahla değil kurumsal güvenle ayrılıyor.

"Doğru" k yok, amaca uygun k var. Siluet k = 2'yi seçiyor çünkü en net ayrışma orada. Ama "ülkeleri gelişmişliğe göre kabaca ikiye ayır" demek ile "yüksek refahlı ülkeleri kurumsal güvene göre ayrıştır" demek farklı sorular. İkinci soruyu soruyorsan k = 3 daha kullanışlı — ölçüt öyle demese bile.

5. Hiyerarşik Kümeleme

K-means'ten farklı olarak hiyerarşik kümeleme küme sayısını baştan sormaz. Her gözlemi kendi kümesi sayıp en yakın olanları birleştirerek bir ağaç (dendrogram) üretir; kesme noktasına sonradan karar verirsin.

Kritik seçim bağlantı yöntemi: iki küme arasındaki uzaklık nasıl tanımlanacak?

YöntemMesafe tanımıKofenetik korelasyon
Averageİki kümedeki tüm nokta çiftlerinin ortalaması0,715
SingleEn yakın iki nokta0,585
Ward.D2Birleşmenin küme içi varyansa maliyeti0,556
CompleteEn uzak iki nokta0,549
Bağlantı yöntemlerini karşılaştırmak
d <- dist(X)

for (m in c("single", "complete", "average", "ward.D2")) {
  hc <- hclust(d, method = m)
  cat(m, cor(d, cophenetic(hc)), "\n")
}

# En yuksek kofenetik korelasyon: average
hc <- hclust(d, method = "average")
fviz_dend(hc, k = 2, cex = 0.5)
Kofenetik korelasyon nedir? Dendrogramda iki gözlemin birleştiği yükseklik ile gerçek uzaklıkları arasındaki korelasyon. Yüksek olması, ağacın orijinal mesafe yapısını iyi koruduğu anlamına gelir. Burada average yöntemi 0,715 ile açık ara önde; ward.D2 görsel olarak daha dengeli ağaçlar üretse de bu veride mesafe yapısını daha çok bozuyor.

Burada önemli bir ayrım var: en yüksek kofenetik korelasyon, "en iyi kümeleme" demek değil. Yalnızca ağacın mesafeleri en sadık biçimde temsil ettiğini söyler. Ward yöntemi çoğu uygulamada daha yorumlanabilir kümeler üretir; seçim amaca göre yapılmalı.

Peki bu ağaç k-means ile aynı şeyi mi söylüyor?

Yukarıdaki cümle genel bir uyarı gibi duruyor. Bu veride somut olarak sınanabilir: her iki yöntemin ağacını ikiye kesip k-means'in bulduğu bölünmeyle karşılaştırmak yeterli.

İki yöntem ne kadar örtüşüyor?
for (m in c("average", "ward.D2")) {
  hc <- hclust(d, method = m)
  print(table(kmeans = km$cluster, hiyerarsik = cutree(hc, 2)))
}
YöntemKofenetik korelasyonKesme sonucu küme büyüklükleriK-means ile uyumDüzeltilmiş Rand
Average0,715148 + 1%59,10,005
Ward.D20,55682 + 67%92,60,725

Sonuç çarpıcı. Kofenetik korelasyonu en yüksek olan average yöntemi ikiye kesildiğinde 148 ülkeyi bir kümeye, tek bir ülkeyi diğerine koyuyor. Mesafeleri sadakatle temsil ediyor ama kullanılabilir bir bölünme üretmiyor. Düzeltilmiş Rand indeksi 0,005 — yani k-means'le uyumu rastgele eşleşmeden farksız.

Ward.D2 ise en düşük kofenetik korelasyona sahip olmasına rağmen 82 + 67'lik dengeli bir bölünme veriyor ve k-means'in sonucuyla %92,6 örtüşüyor (ARI = 0,725). 149 ülkeden yalnızca 11'i farklı kümeye düşüyor.

Hangi ölçüt neyi ölçer? Kofenetik korelasyon ağacın mesafe yapısını ne kadar koruduğunu söyler. Düzeltilmiş Rand indeksi iki bölünmenin birbirine ne kadar benzediğini. İkisi aynı yöntemi işaret etmek zorunda değil ve burada etmiyor. Kümeleme amacıyla ağaç kuruyorsan ikinciye, mesafe yapısını incelemek için kuruyorsan birincisine bakmalısın. Tek bir skora bakıp "en iyi bağlantı yöntemi bu" demek, hangi soruyu sorduğunu unutmaktır.

Düzeltilmiş Rand indeksi (ARI) iki kümelemenin uyumunu ölçer: 1 tam örtüşme, 0 ise rastgele eşleşme düzeyinde benzerlik demektir. Küme etiketlerinin numaralarından bağımsız çalışır — Küme 1 ile Küme 2'nin yer değiştirmesi sonucu etkilemez.

6. Temel Bileşen Analizi (PCA)

Sekiz değişkenli bir veriyi iki boyutta görselleştirmek için bir yola ihtiyaç var. PCA, değişkenlerin doğrusal birleşimlerinden, mümkün olduğunca çok değişkenliği taşıyan yeni eksenler üretir.

PCA ve açıklanan varyans
pca <- prcomp(sayisal, center = TRUE, scale. = TRUE)
summary(pca)

fviz_eig(pca)                      # scree plot
fviz_pca_var(pca, repel = TRUE)   # degisken yukleri
fviz_pca_biplot(pca, habillage = factor(km$cluster))
BileşenStandart sapmaAçıklanan varyansKümülatif
PC11,983%49,2%49,2
PC21,142%16,3%65,5
PC31,083%14,7%80,1
PC40,839%8,8%88,9
PC50,713%6,4%95,3

İlk iki bileşen sekiz değişkenin taşıdığı bilginin %65,5'ini iki eksende topluyor. Üçüncüyü de eklersek %80'e çıkıyoruz. Bu, sekiz boyutlu veriyi iki boyutlu bir grafikte göstermenin makul bir yaklaşım olduğu anlamına geliyor — ama %34,5'lik bir bilginin grafikte görünmediğini de unutmamak gerekiyor.

DeğişkenPC1 yüküPC2 yükü
Ladder score−0,471+0,128
Kişi başı GSYİH−0,454−0,238
Sağlıklı yaşam beklentisi−0,449−0,187
Sosyal destek−0,429−0,186
Seçim özgürlüğü−0,340+0,348
Yolsuzluk algısı+0,250−0,395
Dystopia−0,082+0,296
Cömertlik+0,042+0,702

PC1 bir refah ekseni. Mutluluk, gelir, sağlık, sosyal destek ve özgürlük hepsi aynı yönde yükleniyor; yolsuzluk algısı ters yönde. Bu eksen tek başına değişkenliğin yarısını açıklıyor ve pratikte "gelişmişlik düzeyi" olarak okunabilir.

PC2 ise bir cömertlik ekseni. 0,702'lik yükle cömertlik bu bileşene hâkim; yolsuzluk algısı (−0,395) ve seçim özgürlüğü (+0,348) ona eşlik ediyor. Yani refahla açıklanamayan ikinci bir yapı var ve bu yapı toplumsal davranışla ilgili.

İşaretler keyfîdir: PC1'de mutluluk ve gelirin negatif yüklenmiş olması kafa karıştırabilir. PCA'da bir bileşenin işareti matematiksel olarak keyfîdir: bütün yükleri −1 ile çarpmak aynı çözümü verir. Önemli olan işaretin kendisi değil, değişkenlerin birbirine göre yönü.

Kaç bileşen tutulmalı?

"İlk iki bileşen %65,5'ini açıklıyor" cümlesi bir gözlem, karar değil. Kaç bileşenin tutulacağına karar vermek için birkaç ölçüt var ve bu veride ikisi farklı cevap veriyor.

BileşenÖzdeğerAçıklanan %Kümülatif %Kırık çubuk eşiğiTutulsun mu?
PC13,93349,1649,162,718Evet (her iki ölçüt)
PC21,30316,2965,451,718Yalnızca Kaiser
PC31,17314,6780,111,218Yalnızca Kaiser
PC40,7048,8088,910,885Hayır
PC50,5086,3595,270,635Hayır
ÖlçütKuralıBu veride sonucu
KaiserÖzdeğeri 1'den büyük bileşenleri tut.3 bileşen (3,93 · 1,30 · 1,17)
Scree (dirsek)Eğrinin kırıldığı noktaya kadar tut.PC1'den sonra belirgin düşüş → 1–2 bileşen
Kırık çubukÖzdeğeri, rastgele bölünmede beklenen değeri aşanları tut.1 bileşen
Kümülatif oran%70–80'e ulaşana kadar tut.3 bileşen (%80,1)

Kaiser ölçütü üç bileşen diyor, kırık çubuk ölçütü bir. Bu çelişki tesadüf değil: Kaiser'in "özdeğer > 1" kuralı bilinen biçimde cömerttir ve gereğinden fazla bileşen tutma eğilimindedir. Kırık çubuk ölçütü ise özdeğerleri, bir çubuğun rastgele kırılmasından beklenen uzunluklarla karşılaştırır ve belirgin biçimde daha muhafazakârdır.

Bu veride dürüst cevap: bir güçlü boyut var. PC1 tek başına %49,2 açıklıyor ve her iki ölçüt de onu tutuyor. PC2 ile PC3'ün özdeğerleri (1,30 ve 1,17) 1'in hemen üzerinde — yani sınırda. Görselleştirme için ilk iki bileşeni kullanmak meşru; "veri üç boyutludur" demek ise fazla iddialı olur.

PCA'dan sonra kümelemek aynı sonucu verir mi?

Yaygın bir iş akışı var: önce PCA ile boyut indir, sonra bileşenler üzerinde kümele. Gürültüyü azalttığı ve mesafeleri daha anlamlı kıldığı söylenir. Bu veride test edilebilir.

Ham veri mi, bileşenler mi?
Z <- pca$x[, 1:3]                 # ilk uc bilesen
set.seed(123)
km_pca <- kmeans(scale(Z), centers = 2, nstart = 25)

table(ham = km$cluster, pca = km_pca$cluster)
# uyum: %84.6

İlk üç bileşen üzerinde kümeleme, ham ölçeklenmiş veri üzerindeki kümelemeyle %84,6 örtüşüyor. 149 ülkeden 23'ü farklı kümeye düşüyor — azımsanacak bir fark değil.

Sebebi şu: ilk üç bileşen toplam değişkenliğin %80,1'ini taşıyor, geri kalan %19,9'u atıyor. Atılan kısım "gürültü" olabilir, ama kümeleme için anlamlı bilgi de olabilir. Bu veride atılan kısmın bir bölümü ikinci grubu kurumsal güvene göre ayıran bilgiydi.

PCA'yı kümeleme öncesi otomatik bir adım yapma. Değişken sayısı çok fazlaysa (yüzlerce) veya değişkenler yüksek korelasyonluysa yardımcı olur. Sekiz değişkenli bir veride ise kazancı belirsiz, kaybı somut. En azından iki sonucu karşılaştırmadan birini seçmemek gerekir.

7. Sık Yapılan Hatalar

HataNeden problem?Daha iyi yaklaşım
Ölçeklemeden kümelemekBüyük ölçekli değişken mesafeyi domine eder.scale() uygulamak, ardından sonucu kontrol etmek.
K'yı tek ölçütle seçmekElbow çoğu zaman net bir dirsek göstermez.Elbow, siluet ve gap'i birlikte okumak.
nstart varsayılanda bırakmakYerel optimuma takılma riski.nstart = 25 gibi bir değer vermek.
Siluet değerini raporlamamakKümelerin ne kadar ayrıştığı bilinmez.Skoru yazmak ve düşükse bunu açıkça söylemek.
Küme etiketlerini sabit sanmakNumaralar her çalıştırmada değişebilir.Kümeleri numaraya göre değil profile göre adlandırmak.
PCA'yı ölçeklemeden yapmakBüyük varyanslı değişken ilk bileşeni ele geçirir.prcomp(..., scale. = TRUE) kullanmak.

Sonuç

Denetimsiz öğrenmede modelin doğruluğunu ölçecek bir test seti yok. Onun yerine iç tutarlılık ölçütleri (siluet, kofenetik korelasyon, açıklanan varyans) ve en önemlisi sonucun anlamlı olup olmadığı var.

Bu analizde üç yöntem de aynı yapıya işaret etti: 149 ülke, refah göstergelerine göre kabaca ikiye ayrılıyor ve bu ayrım toplam değişkenliğin üçte birini açıklıyor. PCA ise ikinci bir boyut gösterdi — refahla açıklanamayan, cömertlik etrafında şekillenen bir eksen. Bu, kümelemenin tek başına anlatmadığı bir bulgu.

Dört yazılık seri burada bitiyor. Veri hazırlığından başlayıp regresyon, sınıflandırma ve kümelemeye uzanan bu akışta tek bir ortak tema vardı: modeli kurmak kolay, ona güvenip güvenemeyeceğine karar vermek zor.

“Kümeleme her veride bir sonuç üretir. Asıl soru, ürettiği sonucun gerçekten orada olup olmadığıdır.”

İlgili yazılar ve kaynak kod: K-Means'in algoritma ayrıntısını K-Means Kümeleme Algoritması yazısında, RFM ile gerçek bir segmentasyon uygulamasını ise müşteri segmentasyonu vaka çalışmasında ele aldım. Kaynak kod: github.com/ozgurnrttn/Machine-Learning

In the previous three posts we worked on problems with a known target: we predicted sales and classified stores as High or Low. Now we move to the side where there are no labels. All we have are indicators for 149 countries, and the question is: are there natural groups among them?

In this final post of the series I apply K-means clustering, hierarchical clustering and principal component analysis (PCA) to the World Happiness Report 2021 data.

“In unsupervised learning there is no right answer; there are only defensible interpretations.”

1. The Data and Scaling

The World Happiness Report 2021 holds a happiness score and six explanatory indicators for 149 countries. We go in with eight numeric variables, leaving out the confidence interval columns (upper and lower whisker and the standard error) because they are derived from the score itself and would count the same information three times.

Preparing the data
hap <- read.csv("world-happiness-report-2021.csv", sep = ";", dec = ",")

numeric_vars <- hap[sapply(hap, is.numeric)]
numeric_vars <- numeric_vars[, !names(numeric_vars) %in% c(
  "Standard.error.of.ladder.score", "upperwhisker", "lowerwhisker")]

X <- scale(na.omit(numeric_vars))   # 149 x 8
Why scaling is mandatory: K-means and hierarchical clustering work on distances. Here Logged.GDP.per.capita ranges from 6 to 11 while Ladder.score ranges from 2 to 8; variables on different scales distort the distance disproportionately. scale() transforms each variable to mean 0 and standard deviation 1, giving them equal say.

2. K-Means

K-means divides observations into K clusters and iteratively updates each cluster's centroid. It has a single parameter, and that parameter decides the whole analysis: what should K be?

Running K-means
library(factoextra)
library(cluster)

set.seed(123)
km <- kmeans(X, centers = 2, nstart = 25)

km$size          # 87 62
km$betweenss / km$totss   # 0.3472

fviz_cluster(km, data = X)
Why nstart = 25 matters: K-means picks its starting centroids at random and can settle into a local optimum. nstart runs the algorithm repeatedly from different starts and keeps the best solution. The default is 1, and left alone the result can shift noticeably with the seed.

3. How Many Clusters? Three Criteria

There is no single right way to choose K. Reading three common criteria together is the soundest approach.

The elbow method

The within-cluster sum of squares is computed for different values of K. It always falls as K grows; what you look for is the point where the fall clearly slows down.

Within-cluster sum of squares (k = 1…10)
  • k = 11184
  • k = 2773
  • k = 3627
  • k = 4535
  • k = 5474
  • k = 6438
  • k = 8382
  • k = 10343

The largest drop is from 1 to 2: 1184 down to 773, a fall of 35%. From two to three the drop is 19%, and from three to four 15%. The elbow sits between 2 and 3.

Silhouette score

The silhouette measures how close each observation is to its own cluster and how far from the others. It ranges from −1 to 1; higher means cleaner separation.

Average silhouette score
fviz_nbclust(X, kmeans, method = "silhouette")

# k=2 : 0.3095   <- highest
# k=3 : 0.2696
# k=4 : 0.2692
# k=5 : 0.2666
# k=6 : 0.2230

The gap statistic

The gap statistic
set.seed(123)
gap <- clusGap(X, FUN = kmeans, nstart = 25, K.max = 10, B = 50)
fviz_gap_stat(gap)

The gap statistic compares the observed within-cluster dispersion against randomly generated reference data. All three criteria point to k = 2.

What does a silhouette of 0.31 mean? Not a high value. It indicates that clusters exist, but also that the boundaries are not sharp. The countries do not sit in two clean poles; they lie along a continuum, and the clustering cuts that continuum into useful pieces. Writing that down is far more honest than claiming "we found two clear clusters".

4. Interpreting the Clusters

The algorithm hands you labels "1" and "2"; giving them meaning is the analyst's job. The method is always the same: look at the cluster centres.

Variable (scaled)Cluster 1 (87 countries)Cluster 2 (62 countries)
Ladder score (happiness)+0.673−0.945
GDP per capita (log)+0.618−0.868
Social support+0.643−0.902
Healthy life expectancy+0.643−0.903
Freedom to make life choices+0.460−0.645
Generosity−0.134+0.189
Perceptions of corruption−0.206+0.289

The table speaks for itself. The 87 countries in cluster 1 sit above average on happiness, income, social support, health and freedom, and below average on perceived corruption. The 62 countries in cluster 2 are the mirror image on every one of those indicators.

The interesting one is generosity: it is the only variable running the other way. Countries in cluster 2 score above average on generosity despite lower income and happiness. It is a small but meaningful detail showing that the clustering is not simply a rich–poor divide.

Only 34.7% of the total variation is explained between the clusters (betweenss / totss). The remaining 65.3% is within-cluster variation — both groups are quite heterogeneous internally.

Which countries are in each cluster?

Centroids give a cluster's average profile, but understanding a cluster means looking inside it. The most useful move is to list the observations closest to each centroid: those are the cluster's "typical" members.

Typical members of each cluster
for (k in 1:2) {
  members <- X[km$cluster == k, ]
  d <- apply(members, 1, function(row)
              sqrt(sum((row - km$centers[k, ])^2)))
  print(head(hap$Country.name[km$cluster == k][order(d)], 5))
}
ClusterSizeFive countries closest to the centreMean ladder score
Cluster 187Mauritius, Kazakhstan, Uruguay, Poland, Taiwan6.26
Cluster 262Ethiopia, Zambia, Pakistan, Iraq, Uganda4.52

The gap between the two mean happiness scores is 1.74 points, with a 95% confidence interval of [1.53, 1.95] (Welch t = 16.45, p < 0.001). Given that the clusters were built using the ladder score, that is no surprise — but the size of the gap shows how pronounced the split is.

The border countries are more interesting. An observation with a negative silhouette resembles its neighbouring cluster more than its own. There are four of them here: Albania, North Macedonia, Turkey and Ukraine.

Border cases are findings, not errors. Turkey's negative silhouette does not mean "the algorithm misclassified it"; it means the country sits squarely between the two clusters on the welfare indicators. Clustering cuts a continuum into boxes, and some cases always land on the edge. Flagging those countries as "on the border" is more honest than writing that they are firmly in Cluster 1.

Are these clusters stable?

Clustering returns a result on any dataset. The real question is whether the same clusters would appear if the data changed slightly. The standard check is bootstrap stability: resample the data repeatedly, rerun the clustering, and measure how well the original clusters survive using Jaccard similarity.

Bootstrap stability (B = 200)
jaccard <- function(a, b)
  length(intersect(a, b)) / length(union(a, b))

set.seed(7)
for (b in 1:200) {
  i <- sample(nrow(X), replace = TRUE)
  new <- kmeans(X[unique(i), ], centers = 2, nstart = 10)
  # best-matching Jaccard for each original cluster
}
# Cluster 1: 0.9912   Cluster 2: 0.9871
ClusterMean JaccardShare above 0.75Reading
Cluster 1 (87 countries)0.991100%Highly stable
Cluster 2 (62 countries)0.987100%Highly stable

The usual rule of thumb: above 0.85 is "highly stable", 0.75–0.85 is "stable", below 0.6 is unreliable. Both clusters sit above 0.98 and clear 0.75 in all 200 bootstrap samples.

Low silhouette, high stability — a contradiction? No, they measure different things. A silhouette of 0.31 says how well the clusters separate: the boundary is blurry. A Jaccard of 0.99 says whether the same split keeps reappearing: it does. The boundary is not sharp, but it falls in the same place every time. Reporting both is far more informative than reporting either alone.

And if we had chosen k = 3?

All three criteria pointed at k = 2, but it is worth seeing what that decision costs.

ClusterSizeTypical countriesDistinguishing feature
144Pakistan, Ethiopia, Liberia, Uganda, ZambiaLow on every welfare indicator (−1.0 to −1.2)
227Canada, Austria, Germany, Australia, IrelandHigh welfare and very low perceived corruption (−1.54)
378Montenegro, Dominican Republic, Ecuador, Mauritius, MoldovaIn between; low generosity (−0.45), high perceived corruption

The k = 3 solution lifts the explained share from 34.7% to 47.1% and produces a meaningful middle group. But the variable doing the new separating is perceived corruption: the second cluster is set apart from the third by institutional trust rather than by wealth.

There is no "right" k, only a k that fits the question. The silhouette picks k = 2 because that is where separation is cleanest. But "split countries roughly by development level" and "separate high-welfare countries by institutional trust" are different questions. If you are asking the second one, k = 3 is more useful — even when the criteria disagree.

5. Hierarchical Clustering

Unlike K-means, hierarchical clustering does not ask for the number of clusters up front. It treats every observation as its own cluster, merges the closest ones, and produces a tree (a dendrogram); you decide where to cut afterwards.

The critical choice is the linkage method: how is the distance between two clusters defined?

MethodDistance definitionCophenetic correlation
AverageMean over all pairs of points in the two clusters0.715
SingleThe two closest points0.585
Ward.D2The cost of merging in within-cluster variance0.556
CompleteThe two farthest points0.549
Comparing linkage methods
d <- dist(X)

for (m in c("single", "complete", "average", "ward.D2")) {
  hc <- hclust(d, method = m)
  cat(m, cor(d, cophenetic(hc)), "\n")
}

# Highest cophenetic correlation: average
hc <- hclust(d, method = "average")
fviz_dend(hc, k = 2, cex = 0.5)
What is cophenetic correlation? The correlation between the height at which two observations merge in the dendrogram and their actual distance. A high value means the tree preserves the original distance structure well. Here average leads clearly at 0.715; ward.D2 produces more visually balanced trees but distorts the distance structure more on this data.

There is an important distinction here: the highest cophenetic correlation does not mean "the best clustering". It only says the tree represents the distances most faithfully. Ward's method produces more interpretable clusters in many applications; the choice should follow the purpose.

But does this tree say the same thing as k-means?

That sentence reads like a general warning. On this dataset it can be tested directly: cut each tree into two and compare the result with the split k-means found.

How much do the two methods overlap?
for (m in c("average", "ward.D2")) {
  hc <- hclust(d, method = m)
  print(table(kmeans = km$cluster, hierarchical = cutree(hc, 2)))
}
MethodCophenetic correlationCluster sizes after the cutAgreement with k-meansAdjusted Rand
Average0.715148 + 159.1%0.005
Ward.D20.55682 + 6792.6%0.725

The result is striking. Cut in two, the average method — the one with the highest cophenetic correlation — puts 148 countries in one cluster and a single country in the other. It represents the distances faithfully while producing no usable split. Its adjusted Rand index against k-means is 0.005, no better than chance agreement.

Ward.D2, despite having the lowest cophenetic correlation, yields a balanced 82 + 67 split that overlaps 92.6% with the k-means solution (ARI = 0.725). Only 11 of the 149 countries land in a different cluster.

What does each criterion measure? Cophenetic correlation says how well the tree preserves the distance structure. The adjusted Rand index says how similar two partitions are. They need not point at the same method, and here they do not. If you are building the tree in order to cluster, look at the second; if you are building it to study distance structure, look at the first. Reading a single score and declaring "this is the best linkage" is forgetting which question you asked.

The adjusted Rand index (ARI) measures agreement between two clusterings: 1 is a perfect match, 0 is the level of similarity you would get by chance. It is invariant to the cluster labels — swapping "Cluster 1" and "Cluster 2" does not change it.

6. Principal Component Analysis

Visualizing eight-variable data in two dimensions needs a method. PCA builds new axes from linear combinations of the variables, carrying as much variation as possible.

PCA and explained variance
pca <- prcomp(numeric_vars, center = TRUE, scale. = TRUE)
summary(pca)

fviz_eig(pca)                      # scree plot
fviz_pca_var(pca, repel = TRUE)   # variable loadings
fviz_pca_biplot(pca, habillage = factor(km$cluster))
ComponentStandard deviationVariance explainedCumulative
PC11.98349.2%49.2%
PC21.14216.3%65.5%
PC31.08314.7%80.1%
PC40.8398.8%88.9%
PC50.7136.4%95.3%

The first two components gather 65.5% of the information in eight variables onto two axes. Adding the third takes us to 80%. That makes a two-dimensional plot a reasonable way to show eight-dimensional data — while remembering that 34.5% of the information is not visible in it.

VariablePC1 loadingPC2 loading
Ladder score−0.471+0.128
GDP per capita−0.454−0.238
Healthy life expectancy−0.449−0.187
Social support−0.429−0.186
Freedom to make life choices−0.340+0.348
Perceptions of corruption+0.250−0.395
Dystopia−0.082+0.296
Generosity+0.042+0.702

PC1 is a prosperity axis. Happiness, income, health, social support and freedom all load in the same direction, with perceived corruption against them. This single axis explains half the variation and reads in practice as "level of development".

PC2 is a generosity axis. With a loading of 0.702, generosity dominates this component, accompanied by perceived corruption (−0.395) and freedom of choice (+0.348). In other words, there is a second structure that prosperity does not explain, and it concerns social behaviour.

The signs are arbitrary: seeing happiness and income load negatively on PC1 can be confusing. The sign of a component is mathematically arbitrary in PCA: multiplying every loading by −1 gives the same solution. What matters is not the sign itself but the direction of the variables relative to one another.

How many components should we keep?

"The first two components explain 65.5%" is an observation, not a decision. Several criteria exist for choosing how many to keep, and on this data two of them disagree.

ComponentEigenvalueExplained %Cumulative %Broken-stick thresholdKeep?
PC13.93349.1649.162.718Yes (both criteria)
PC21.30316.2965.451.718Kaiser only
PC31.17314.6780.111.218Kaiser only
PC40.7048.8088.910.885No
PC50.5086.3595.270.635No
CriterionRuleAnswer on this data
KaiserKeep components with an eigenvalue above 1.3 components (3.93, 1.30, 1.17)
Scree (elbow)Keep up to the point where the curve bends.Sharp drop after PC1 → 1–2 components
Broken stickKeep eigenvalues that exceed what a random split would give.1 component
Cumulative shareKeep until you reach 70–80%.3 components (80.1%)

Kaiser says three components, the broken stick says one. The clash is not accidental: the "eigenvalue > 1" rule is known to be generous and tends to retain more components than it should. The broken-stick criterion compares eigenvalues with the lengths expected from randomly breaking a stick, and is markedly more conservative.

The honest answer here: there is one strong dimension. PC1 alone explains 49.2% and both criteria keep it. The eigenvalues of PC2 and PC3 (1.30 and 1.17) sit just above 1 — right on the border. Using the first two components for visualisation is legitimate; claiming "the data is three-dimensional" would be overstating it.

Does clustering after PCA give the same answer?

A common workflow is to reduce dimensions with PCA first and cluster on the components afterwards, on the grounds that it removes noise and makes distances more meaningful. That claim is testable here.

Raw data or components?
Z <- pca$x[, 1:3]                 # first three components
set.seed(123)
km_pca <- kmeans(scale(Z), centers = 2, nstart = 25)

table(raw = km$cluster, pca = km_pca$cluster)
# agreement: 84.6%

Clustering on the first three components overlaps 84.6% with clustering on the raw scaled data. Twenty-three of the 149 countries land in a different cluster — not a trivial difference.

The reason is straightforward: the first three components carry 80.1% of the total variation and discard the remaining 19.9%. What gets discarded may be noise, but it may equally be information the clustering needed. On this data, part of what was dropped is exactly what separated the second group on institutional trust.

Do not make PCA an automatic pre-clustering step. It helps when there are hundreds of variables, or when predictors are strongly correlated. On eight variables the gain is uncertain while the loss is concrete. At the very least, compare both results before committing to one.

7. Common Mistakes

MistakeWhy it is a problemA better approach
Clustering without scalingThe large-scale variable dominates the distance.Apply scale(), then check the result.
Choosing K by one criterionThe elbow often shows no clear bend.Read elbow, silhouette and gap together.
Leaving nstart at the defaultRisk of settling in a local optimum.Set a value such as nstart = 25.
Not reporting the silhouetteNobody knows how separated the clusters are.State the score, and say so plainly when it is low.
Treating cluster labels as fixedThe numbers can change on every run.Name clusters by profile, not by number.
Running PCA without scalingA high-variance variable captures the first component.Use prcomp(..., scale. = TRUE).

Conclusion

In unsupervised learning there is no test set to measure accuracy against. In its place you have internal consistency measures — silhouette, cophenetic correlation, explained variance — and, above all, whether the result makes sense.

All three methods pointed to the same structure here: 149 countries divide roughly in two along prosperity indicators, and that split explains about a third of the total variation. PCA then revealed a second dimension that prosperity does not account for, organized around generosity — a finding the clustering alone does not tell you.

This four-part series ends here. From data preparation through regression, classification and clustering, one theme ran through all of it: fitting a model is easy; deciding whether to trust it is hard.

“Clustering produces a result on any data. The real question is whether that result was actually there.”

Related posts and source code: I covered the K-means algorithm in detail in the K-Means post, and a real segmentation project with RFM in the customer segmentation case study. Source code: github.com/ozgurnrttn/Machine-Learning