Memuat
Memuat
K-Means and Hierarchical Cluster Analysis for Education and Learning
0 views
Grouping objects by mean assessment score, students per teacher and school facility index with K-Means and hierarchical clustering (the analysis is illustrated with synthetic data). Cluster analysis groups similar objects on several variables without class labels, using K-Means and Ward hierarchical clustering. It suits theses on segmentation or typologies of objects such as regions, customers or patients.

Grouping objects by mean assessment score, students per teacher and school facility index with K-Means and hierarchical clustering (the analysis is illustrated with synthetic data).
| Role | Variable | Unit |
|---|---|---|
| Main | Mean assessment score | score |
| Supporting 1 | Students per teacher | ratio |
| Supporting 2 | School facility index | index |
The highest mean silhouette (0.638) occurs at k = 4, and K-Means explains 87.6% of total variation (BSS/TSS). Ward hierarchical clustering agrees closely with K-Means (ARI = 1.000). Cluster C4 has the highest mean Mean assessment score (91.51) across 27 objects.
Computed from illustrative synthetic data, not from a real study.
Cluster analysis groups similar objects on several variables without class labels, using K-Means and Ward hierarchical clustering. It suits theses on segmentation or typologies of objects such as regions, customers or patients.
Report the chosen k with its mean silhouette and BSS/TSS ratio, then the agreement with hierarchical clustering (ARI). Name each cluster from its z-score or raw mean profile and describe the main differences.
A short starting script with this project's variable names. The delivered version is adapted to your data and fully commented.
# Contoh kode R: K-Means and Hierarchical Cluster Analysis
suppressPackageStartupMessages({
library(ggplot2)
library(patchwork)
library(cluster)
library(ggdendro)
})
set.seed(42)
# ---- Gaya grafik ----
pal <- c("#1f6feb", "#e8590c", "#2f9e44", "#ae3ec9", "#f59f00")
warna <- list(bg = "#f4f6fa", panel = "#ffffff", teks = "#1f2933", redup = "#5f6b78", grid = "#e3e8ee", bar = "#1d3557")
tata <- 1
theme_set(theme_minimal(base_size = 11) + theme(legend.position = "bottom"))
# ---- Data sintetis: objek dari 3 atau 4 kelompok laten, 4 variabel karakteristik ----
n <- 200
positif <- function(x, m) if (m > 0) pmax(x, 0.05 * m) else x # cegah nilai negatif yang tidak wajar
k_benar <- sample(3:4, 1)
repeat { # pusat kelompok yang cukup terpisah (skala z)
pusat <- matrix(rnorm(k_benar * 4, 0, 1.6), k_benar, 4)
if (min(dist(pusat)) > 3.2) break
}
anggota <- sample(seq_len(k_benar), n, replace = TRUE, prob = runif(k_benar, 0.6, 1.4))
sebaran <- runif(1, 0.5, 0.72)
z <- pusat[anggota, ] + matrix(rnorm(n * 4, 0, sebaran), n, 4)
d <- data.frame(mean_assessment_score = positif(100 + 0.6 * 15 * z[, 1], 100),
students_per_teacher = positif(50 + 0.6 * 10 * z[, 2], 50),
school_facility_index = positif(30 + 0.6 * 6 * z[, 3], 30),
x3 = round(50 + 6 * z[, 4], 2))
write.csv(d, "data_contoh.csv", row.names = FALSE)
# ---- Standarisasi variabel ----
vars <- c("mean_assessment_score", "students_per_teacher", "school_facility_index", "x3")
xs <- scale(d[vars])
jarak <- dist(xs)
# ---- Penentuan jumlah klaster: elbow (WSS) dan silhouette rata-rata, k = 2..8 ----
daftar_k <- 2:8
uji_k <- lapply(daftar_k, function(k) {
km <- kmeans(xs, centers = k, nstart = 25, iter.max = 50)
sil <- cluster::silhouette(km$cluster, jarak)
c(k = k, wss = km$tot.withinss, sil = mean(sil[, "sil_width"]))
})
uji_k <- as.data.frame(do.call(rbind, uji_k))
print(round(uji_k, 3))
k_best <- uji_k$k[which.max(uji_k$sil)]
cat("Jumlah klaster terpilih (silhouette maksimum):", k_best, "\n")
# ---- K-Means akhir (nstart 25); label klaster diurutkan menurut rerata Y ----
km <- kmeans(xs, centers = k_best, nstart = 25, iter.max = 50)
urut <- order(tapply(d$mean_assessment_score, km$cluster, mean))
d$klaster <- factor(paste0("C", match(km$cluster, urut)), levels = paste0("C", seq_len(k_best)))
sil_akhir <- mean(cluster::silhouette(km$cluster, jarak)[, "sil_width"])
rasio_bss <- km$betweenss / km$totss
# ---- Klaster hierarki Ward (ward.D2) dan kesesuaiannya dengan K-Means ----
hc <- hclust(jarak, method = "ward.D2")
ward <- cutree(hc, k = k_best)
print(table(KMeans = d$klaster, Ward = ward))
ari <- function(a, b) {
# adjusted Rand index (Hubert dan Arabie)
tb <- table(a, b); c2 <- function(x) sum(x * (x - 1) / 2)
s_ij <- c2(tb); s_a <- c2(rowSums(tb)); s_b <- c2(colSums(tb)); tot <- c2(sum(tb))
harap <- s_a * s_b / tot
(s_ij - harap) / (0.5 * (s_a + s_b) - harap)
}
ari_km_ward <- ari(d$klaster, ward)
cat(sprintf("Adjusted Rand index K-Means vs Ward = %.3f\n", ari_km_ward))
# ---- Profil klaster (rerata satuan asli) ----
profil <- aggregate(d[vars], by = list(klaster = d$klaster), FUN = mean)
profil$n <- as.numeric(table(d$klaster))
print(profil, digits = 4)
k_atas <- as.character(profil$klaster[which.max(profil$mean_assessment_score)])
# ---- Grafik ----
dk <- rbind(data.frame(k = uji_k$k, nilai = uji_k$sil, ukuran = "Mean silhouette"),
data.frame(k = uji_k$k, nilai = uji_k$wss, ukuran = "Within SS (elbow)"))
p1 <- ggplot(dk, aes(k, nilai)) +
geom_vline(xintercept = k_best, linetype = "dashed", colour = warna$redup) +
geom_line(colour = pal[1], linewidth = 0.9) +
geom_point(aes(colour = k == k_best), size = 2.2) +
scale_colour_manual(values = c(`FALSE` = pal[1], `TRUE` = pal[2]), guide = "none") +
facet_wrap(~ukuran, ncol = 1, scales = "free_y") +
scale_x_continuous(breaks = daftar_k) +
labs(title = "Choosing k", x = "Number of clusters k", y = NULL)
pc <- prcomp(xs)
ve <- 100 * pc$sdev^2 / sum(pc$sdev^2)
sk <- data.frame(PC1 = pc$x[, 1], PC2 = pc$x[, 2], klaster = d$klaster)
pusat_pc <- aggregate(cbind(PC1, PC2) ~ klaster, data = sk, FUN = mean)
p2 <- ggplot(sk, aes(PC1, PC2, colour = klaster)) +
stat_ellipse(aes(fill = klaster), geom = "polygon", alpha = 0.12, level = 0.9, show.legend = FALSE) +
geom_point(size = 1.5, alpha = 0.75) +
geom_point(data = pusat_pc, shape = 4, size = 4, stroke = 1.4, colour = warna$teks) +
geom_text(data = pusat_pc, aes(label = klaster), colour = warna$teks, fontface = "bold", size = 3.6, vjust = -1.1) +
scale_colour_manual(values = pal, name = "Cluster") +
scale_fill_manual(values = pal) +
labs(title = "K-Means clusters on the first two PCs", x = sprintf("PC1 (%.1f%%)", ve[1]), y = sprintf("PC2 (%.1f%%)", ve[2]))
zp <- aggregate(as.data.frame(xs), by = list(klaster = d$klaster), FUN = mean)
nama_var <- sapply(c("mean_assessment_score", "students_per_teacher", "school_facility_index", "x3 (extra)"), function(s) paste(strwrap(s, 14), collapse = "
"))
pl <- data.frame(klaster = rep(zp$klaster, length(vars)), variabel = rep(nama_var, each = nrow(zp)), z = unlist(zp[vars]))
pl$variabel <- factor(pl$variabel, levels = unique(nama_var))
p3 <- ggplot(pl, aes(variabel, klaster, fill = z)) +
geom_tile(colour = warna$panel, linewidth = 0.8) +
geom_text(aes(label = sprintf("%.2f", round(z, 2) + 0)), colour = warna$teks, size = 3.1) +
scale_fill_gradient2(low = pal[1], mid = warna$panel, high = pal[2], midpoint = 0, name = "z") +
labs(title = "Cluster profile (mean z-score)", x = NULL, y = NULL) +
theme(panel.grid = element_blank(), legend.position = "none", axis.text.x = element_text(size = rel(0.8)))
dd <- ggdendro::dendro_data(as.dendrogram(hc), type = "rectangle")
h_tinggi <- sort(hc$height, decreasing = TRUE)
h_potong <- mean(h_tinggi[(k_best - 1):k_best])
p4 <- ggplot(ggdendro::segment(dd)) +
geom_segment(aes(x = x, y = y, xend = xend, yend = yend), colour = pal[3], linewidth = 0.35) +
geom_hline(yintercept = h_potong, linetype = "dashed", colour = pal[2], linewidth = 0.8) +
annotate("text", x = nrow(d) * 0.98, y = h_potong, label = sprintf("cut: k = %d", k_best), hjust = 1, vjust = -0.5,
colour = warna$teks, size = 3) +
labs(title = "Ward (ward.D2) dendrogram", x = "Objects", y = "Height") +
theme(axis.text.x = element_blank(), panel.grid.major.x = element_blank())
p_hasil <- if (tata == 1) ((p2 | p1) / (p4 | p3)) + plot_layout(widths = c(1.5, 1), heights = c(1.3, 1)) else
((p2 / p4) | (p1 / p3)) + plot_layout(widths = c(1.5, 1))
print(p_hasil)
The original project file is available. It is not just the picture above: you get the working R project. It can be downloaded after payment, and we send it by WhatsApp or email.
ZIP: analisis.R + data_contoh.csv + BACA-DULU.txt
Choose how much help you need. Each higher package includes everything in the lower ones. Prices exclude 11% VAT.
Package 1
US$19
excl. 11% VAT
Duration: 3 working days
Output: R files + result figures
Deliverables
Package 2 · most chosen
US$56
excl. 11% VAT
Duration: 7 working days
Output: Code + analysis + interpretation notes
Includes all of Package 1, plus
Package 3
US$122
excl. 11% VAT
Duration: 12-15 working days
Output: Code + analysis + guidance until ready to present
Includes all of Packages 1 & 2, plus
Yes. The original project file is a ZIP containing the complete analisis.R script, a data_contoh.csv sample dataset and a BACA-DULU.txt guide. It can be downloaded after payment, and we send it by WhatsApp or email. For your own data (Package 2 and 3) the script is adapted to your dataset.
Yes. K-Means and Hierarchical Cluster Analysis is commonly used at this level in Education and Learning. We adapt the analysis to your research question, your own data and your supervisor's requirements.
No. The sheet is computed from illustrative synthetic data so you can see the kind of output R Studio gives. For your order we run the same method on your own data.
That is the point of the packages. Every script is commented, and Package 3 includes two coaching sessions plus practice questions so you can explain the method and the results at your seminar and defence.
Each project has three packages: Package 1 (code and explanation) US$19, Package 2 (your data analysis and interpretation) US$56 and Package 3 (full guidance) US$122, excluding 11% VAT. Larger datasets, extra methods or tight deadlines adjust the quote after a short consultation.
The result sheet uses illustrative synthetic data. R is a programming language of the R Foundation project; RStudio and Posit are trademarks of Posit PBC.
Back to R Studio portfolio