df <- tibble::tribble(
~Kecamatan, ~Jumlah_Penduduk, ~Kepadatan_per_km2, ~Pustu, ~Apotek, ~Tenaga_Kesehatan,
"Weru", 57910, 1269.96, 3, 11, 38,
"Bulu", 37179, 801.27, 3, 4, 32,
"Tawangsari",56995, 1449.52, 7, 10, 36,
"Sukoharjo", 10017, 2139.66, 5, 43, 595,
"Nguter", 56015, 968.28, 4, 8, 38,
"Bendosari", 65180, 1169.78, 4, 15, 207,
"Polokarto", 88708, 1326.18, 5, 20, 30,
"Mojolaban", 93242, 2435.15, 3, 26, 36,
"Grogol", 121870, 3889.88, 4, 47, 841,
"Baki", 72678, 3105.90, 3, 20, 34,
"Gatak", 54532, 2733.43, 3, 13, 36,
"Kartasura",111984, 5170.08, 3, 40, 1009
)
kable(df, caption = "Data Kecamatan")
| Kecamatan | Jumlah_Penduduk | Kepadatan_per_km2 | Pustu | Apotek | Tenaga_Kesehatan |
|---|---|---|---|---|---|
| Weru | 57910 | 1269.96 | 3 | 11 | 38 |
| Bulu | 37179 | 801.27 | 3 | 4 | 32 |
| Tawangsari | 56995 | 1449.52 | 7 | 10 | 36 |
| Sukoharjo | 10017 | 2139.66 | 5 | 43 | 595 |
| Nguter | 56015 | 968.28 | 4 | 8 | 38 |
| Bendosari | 65180 | 1169.78 | 4 | 15 | 207 |
| Polokarto | 88708 | 1326.18 | 5 | 20 | 30 |
| Mojolaban | 93242 | 2435.15 | 3 | 26 | 36 |
| Grogol | 121870 | 3889.88 | 4 | 47 | 841 |
| Baki | 72678 | 3105.90 | 3 | 20 | 34 |
| Gatak | 54532 | 2733.43 | 3 | 13 | 36 |
| Kartasura | 111984 | 5170.08 | 3 | 40 | 1009 |
num <- df %>% select(-Kecamatan)
stat <- tibble(
Variabel = names(num),
Min = sapply(num, min),
Mean = sapply(num, mean),
SD = sapply(num, sd),
Max = sapply(num, max)
)
kable(stat, digits = 2, caption = "Statistik Deskriptif")
| Variabel | Min | Mean | SD | Max |
|---|---|---|---|---|
| Jumlah_Penduduk | 10017.00 | 68859.17 | 31309.94 | 121870.00 |
| Kepadatan_per_km2 | 801.27 | 2204.92 | 1333.74 | 5170.08 |
| Pustu | 3.00 | 3.92 | 1.24 | 7.00 |
| Apotek | 4.00 | 21.42 | 14.54 | 47.00 |
| Tenaga_Kesehatan | 30.00 | 244.33 | 358.75 | 1009.00 |
Variabel-variabel yang digunakan memiliki satuan yang berbeda maka perlu dilakukan standarisasi data, misalnya dengan z-score dengan rumus: \[ Z_{np} = \frac{X_{np} - \bar{X}_{p}}{S_{p}} \]
X <- df %>% select(-Kecamatan)
X_scaled <- scale(X)
# cek sekilas hasil standardisasi
head(X_scaled)
## Jumlah_Penduduk Kepadatan_per_km2 Pustu Apotek Tenaga_Kesehatan
## [1,] -0.3497026 -0.70100716 -0.73918030 -0.7165086 -0.5751473
## [2,] -1.0118246 -1.05241640 -0.73918030 -1.1980024 -0.5918721
## [3,] -0.3789265 -0.56637864 2.48633375 -0.7852934 -0.5807223
## [4,] -1.8793447 -0.04893305 0.87357672 1.4846058 0.9774717
## [5,] -0.4102265 -0.92719748 0.06719821 -0.9228631 -0.5751473
## [6,] -0.1175079 -0.77611902 0.06719821 -0.4413693 -0.1040654
Silhouette dapat dihitung dengan rumus: \[ \mathrm{Silhouette} = \frac{b_i - a_i}{\max\{a_i,\, b_i\}} \]
set.seed(123)
k_range <- 2:6
sil <- numeric(length(k_range))
for(i in seq_along(k_range)){
k <- k_range[i]
km <- kmeans(X_scaled, centers = k, nstart = 25)
s <- silhouette(km$cluster, dist(X_scaled))
sil[i] <- mean(s[, 3])
}
sil_tbl <- tibble(K = k_range, Silhouette = sil)
kable(sil_tbl, digits = 4, caption = "Silhouette Score")
| K | Silhouette |
|---|---|
| 2 | 0.5118 |
| 3 | 0.4228 |
| 4 | 0.3719 |
| 5 | 0.3623 |
| 6 | 0.3113 |
ggplot(sil_tbl, aes(x = K, y = Silhouette)) +
geom_line() +
geom_point(size = 2) +
labs(title = "Silhouette Score vs K", x = "Jumlah Klaster (K)", y = "Silhouette Score")
best_k <- sil_tbl$K[which.max(sil_tbl$Silhouette)]
Jumlah klaster terbaik yang digunakan untuk klasterisasi adalah 2 klaster.
Pengukuran kedekatan dalam K-Means umumnya menggunakan jarak Euclidean, yang dirumuskan sebagai berikut:
\[ d_{ij} = \sqrt{\sum_{k=1}^{p} (x_{ik} - x_{jk})^2} \]
set.seed(123)
km_final <- kmeans(X_scaled, centers = best_k, nstart = 25)
hasil <- df %>%
mutate(Cluster = as.factor(km_final$cluster)) %>%
arrange(Cluster, Kecamatan)
kable(hasil, caption = "Hasil Klaster per Kecamatan")
| Kecamatan | Jumlah_Penduduk | Kepadatan_per_km2 | Pustu | Apotek | Tenaga_Kesehatan | Cluster |
|---|---|---|---|---|---|---|
| Baki | 72678 | 3105.90 | 3 | 20 | 34 | 1 |
| Bendosari | 65180 | 1169.78 | 4 | 15 | 207 | 1 |
| Bulu | 37179 | 801.27 | 3 | 4 | 32 | 1 |
| Gatak | 54532 | 2733.43 | 3 | 13 | 36 | 1 |
| Mojolaban | 93242 | 2435.15 | 3 | 26 | 36 | 1 |
| Nguter | 56015 | 968.28 | 4 | 8 | 38 | 1 |
| Polokarto | 88708 | 1326.18 | 5 | 20 | 30 | 1 |
| Sukoharjo | 10017 | 2139.66 | 5 | 43 | 595 | 1 |
| Tawangsari | 56995 | 1449.52 | 7 | 10 | 36 | 1 |
| Weru | 57910 | 1269.96 | 3 | 11 | 38 | 1 |
| Grogol | 121870 | 3889.88 | 4 | 47 | 841 | 2 |
| Kartasura | 111984 | 5170.08 | 3 | 40 | 1009 | 2 |
profil <- hasil %>%
group_by(Cluster) %>%
summarise(
Jumlah_Penduduk = mean(Jumlah_Penduduk),
Kepadatan_per_km2 = mean(Kepadatan_per_km2),
Pustu = mean(Pustu),
Apotek = mean(Apotek),
Tenaga_Kesehatan = mean(Tenaga_Kesehatan),
.groups = "drop"
)
kable(profil, digits = 2, caption = "Rata-rata Variabel per Klaster (Profiling)")
| Cluster | Jumlah_Penduduk | Kepadatan_per_km2 | Pustu | Apotek | Tenaga_Kesehatan |
|---|---|---|---|---|---|
| 1 | 59245.6 | 1739.91 | 4.0 | 17.0 | 108.2 |
| 2 | 116927.0 | 4529.98 | 3.5 | 43.5 | 925.0 |
Klaster 1 memiliki rata-rata jumlah penduduk sekitar 59246 jiwa dengan kepadatan sekitar 1740 jiwa/km². Rata-rata puskesmas pembantu sebanyak 4 unit, apotek sekitar 17 unit, dan tenaga kesehatan sekitar 108 orang. Klaster ini merepresentasikan kecamatan dengan karakteristik relatif lebih rendah.
Sebaliknya, Klaster 2 memiliki rata-rata jumlah penduduk sekitar 116927 jiwa dengan kepadatan sekitar 4530 jiwa/km². Jumlah puskesmas pembantu sekitar 4 unit, apotek sekitar 44 unit, dan tenaga kesehatan sekitar 925 orang. Klaster ini menunjukkan kecamatan dengan kepadatan dan fasilitas kesehatan lebih tinggi.