Dalam penelitian ini dataset yang digunakan memiliki judul Air Quality Data in India (2015-2020) yang berasal dari platform dataset terbuka yaitu Kaggle. Dataset Air Quality Data in India (2015-2020) merupakan dataset yang dipublikasikan oleh pengguna Kaggle Bernama Rohan Rao yang bersifat open access, sehingga dataset tersebut dapat digunakan untuk keperluan analisis dan penelitian. Data yang berada dalam dataset ini bersumber dari Central Polluyion Control Board (CPCB), yaitu Lembaga resmi dari pemerintah India yang memiliki tanggung jawab dalam pemantauan kualitas lingkungan di India.
Dataset Air Quality Data in India (2015-2020) berisi informasi mengenai kualitas udara di berbagai kota di India yang diukur dalam rentang waktu tahun 2015 hingga 2020. Data disajikan dalam beberapa file dengan tingkat granularitas yang berbeda, seperti data harian (city_day.csv) dan data per jam (city_hour.csv) yang mencakup berbagai parameter polutan udara serta nilai Air Quality Index (AQI). Pada penelitian ini, digunakan subset data file city_day.csv yang berisi data mengenai kualitas udara harian pada berbagai kota di India. Dataset ini mencakup sejumlah variabel yang merepresentasikan parameter polutan udara. Pemilihan data harian (city_day.csv) dilakukan untuk memperoleh representasi kualitas udara yang lebih stabil
Dataset Air Quality Data in India (2015-2020) ini dipilih dalam penelitian ini dikarenakan memiliki karakteristik yang sesuai dalam penerapan metode Clustering. Dalam dataset ini terdapat data numerik dalam jumlah yang cukup banyak serta tidak terdapat label kategori tertentu pada dataset. Dengan menerapkan Teknik clustering, data akan dikelompokkan menjadi beberapa kelompok untuk mengidentifikasi pola tersembunyi serta mengelompokkan kota-kota di India berdasarkan Tingkat kualitas udaranya. Melalui penelitian ini, hasil clustering diharapkan mampu menggambarkan perbedaan karakteristik udara antar kota di India sehingga dapat membantu proses pemahaman atas Tingkat pencemaran udara dengan lebih terstruktur.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## âś” dplyr 1.2.1 âś” readr 2.2.0
## âś” forcats 1.0.1 âś” stringr 1.6.0
## âś” ggplot2 4.0.3 âś” tibble 3.3.1
## âś” lubridate 1.9.5 âś” tidyr 1.3.2
## âś” purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## âś– dplyr::filter() masks stats::filter()
## âś– dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(reshape2)
##
## Attaching package: 'reshape2'
##
## The following object is masked from 'package:tidyr':
##
## smiths
library(psych)
##
## Attaching package: 'psych'
##
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
library(factoextra)
## Welcome to factoextra!
## Want to learn more? See two factoextra-related books at https://www.datanovia.com/en/product/practical-guide-to-principal-component-methods-in-r/
library(cluster)
library(dbscan)
##
## Attaching package: 'dbscan'
##
## The following object is masked from 'package:stats':
##
## as.dendrogram
data <- read.csv("city_day.csv")
head(data)
## City Date PM2.5 PM10 NO NO2 NOx NH3 CO SO2 O3
## 1 Ahmedabad 2015-01-01 NA NA 0.92 18.22 17.15 NA 0.92 27.64 133.36
## 2 Ahmedabad 2015-01-02 NA NA 0.97 15.69 16.46 NA 0.97 24.55 34.06
## 3 Ahmedabad 2015-01-03 NA NA 17.40 19.30 29.70 NA 17.40 29.07 30.70
## 4 Ahmedabad 2015-01-04 NA NA 1.70 18.48 17.97 NA 1.70 18.59 36.08
## 5 Ahmedabad 2015-01-05 NA NA 22.10 21.42 37.76 NA 22.10 39.33 39.31
## 6 Ahmedabad 2015-01-06 NA NA 45.41 38.48 81.50 NA 45.41 45.76 46.51
## Benzene Toluene Xylene AQI AQI_Bucket
## 1 0.00 0.02 0.00 NA
## 2 3.68 5.50 3.77 NA
## 3 6.80 16.40 2.25 NA
## 4 4.43 10.14 1.00 NA
## 5 7.01 18.89 2.78 NA
## 6 5.42 10.83 1.93 NA
dim(data)
## [1] 29531 16
str(data)
## 'data.frame': 29531 obs. of 16 variables:
## $ City : chr "Ahmedabad" "Ahmedabad" "Ahmedabad" "Ahmedabad" ...
## $ Date : chr "2015-01-01" "2015-01-02" "2015-01-03" "2015-01-04" ...
## $ PM2.5 : num NA NA NA NA NA NA NA NA NA NA ...
## $ PM10 : num NA NA NA NA NA NA NA NA NA NA ...
## $ NO : num 0.92 0.97 17.4 1.7 22.1 ...
## $ NO2 : num 18.2 15.7 19.3 18.5 21.4 ...
## $ NOx : num 17.1 16.5 29.7 18 37.8 ...
## $ NH3 : num NA NA NA NA NA NA NA NA NA NA ...
## $ CO : num 0.92 0.97 17.4 1.7 22.1 ...
## $ SO2 : num 27.6 24.6 29.1 18.6 39.3 ...
## $ O3 : num 133.4 34.1 30.7 36.1 39.3 ...
## $ Benzene : num 0 3.68 6.8 4.43 7.01 5.42 0 0 0 0 ...
## $ Toluene : num 0.02 5.5 16.4 10.14 18.89 ...
## $ Xylene : num 0 3.77 2.25 1 2.78 1.93 0 0 0 0 ...
## $ AQI : num NA NA NA NA NA NA NA NA NA NA ...
## $ AQI_Bucket: chr "" "" "" "" ...
data_fokus <- data %>%
select(City, PM2.5, PM10, NO2, CO, SO2, O3)
Pada penelitian ini, terdapat 7 Variabel utama yang digunakan, pemilihan variabel didasarkan pada parameter umum kualitas udara global yang bertujuan agar proses analisis dan clustering dapat lebih difokuskan pada parameter udara yang bersifat numerik dan paling relevan. Variabel yang digunakan dalam penelitian meliputi City, PM2.5, PM10, NO2, CO, SO2, dan O3.
head(data_fokus)
## City PM2.5 PM10 NO2 CO SO2 O3
## 1 Ahmedabad NA NA 18.22 0.92 27.64 133.36
## 2 Ahmedabad NA NA 15.69 0.97 24.55 34.06
## 3 Ahmedabad NA NA 19.30 17.40 29.07 30.70
## 4 Ahmedabad NA NA 18.48 1.70 18.59 36.08
## 5 Ahmedabad NA NA 21.42 22.10 39.33 39.31
## 6 Ahmedabad NA NA 38.48 45.41 45.76 46.51
describe(data_fokus)
## vars n mean sd median trimmed mad min max range skew
## City* 1 29531 13.89 7.59 15.00 13.99 8.90 1.00 26.00 25.00 -0.11
## PM2.5 2 24933 67.45 64.66 48.57 55.32 34.74 0.04 949.99 949.95 3.37
## PM10 3 18391 118.13 90.61 95.68 103.47 65.12 0.01 1000.00 999.99 2.05
## NO2 4 25946 28.56 24.47 21.69 24.67 16.93 0.01 362.21 362.20 2.46
## CO 5 27472 2.25 6.96 0.89 0.99 0.65 0.00 175.81 175.81 8.88
## SO2 6 25677 14.53 18.13 9.16 10.67 6.11 0.01 193.86 193.85 4.08
## O3 7 25509 34.49 21.69 30.84 32.25 19.21 0.01 257.73 257.72 1.33
## kurtosis se
## City* -1.19 0.04
## PM2.5 21.13 0.41
## PM10 6.74 0.67
## NO2 11.21 0.15
## CO 109.46 0.04
## SO2 22.06 0.11
## O3 3.43 0.14
Berdasarkan tabel hasil statistik deskriptif terhadap 6 parameter polutan, dapat ditarik beberapa kesimpulan yaitu, seluruh variabel polutan menunjukkan nilai rata-rata yang lebih besar jika dibandingkan dengan nilai mediannya. Contohnya, pada polutan PM2.5 memiliki rilai rata-rata 67,45 sedangkan mediannya bernilai 45,87 yang menunjukkan bahwa distribusi data bersifat miring ke kanan (right-skewed). Hal ini menunjukkan bahwa sebagian besar data berada memiliki Tingkat polusi yang tidak terlalu tinggi namun dikarenakan terdapat nilai ekstrem yang menyebabkan rata-rata menjadi lebih besar. Hasil statistik deskriptif juga menunjukkan bahwa data yang digunakan memiliki variabilitas yang sangat ekstrem ditunjukkan oleh rentang yang sangat lebar pada setiap variabel. Selain itu, nilai Standard Deviation (SD) yang menunjukkan nilai yang hampir mirip dengan nilai mean menunjukkan bahwa Tingkat populasi antar kota di India sangat tidak merata. Selanjutnya, nilai skewness menunjukkan nilai positif pada seluruh variabel mengindikasikan bahwa data terkonsentrasi pada nilai rendah namun memiliki ekor Panjang pada nilai tinggi ditunjukkan oleh variabel CO dengan nilai CO (8,8) dan nilai SO2 (4,08). Sementara itu nilai kurtosis yang sangat tinggi menunjukkan bahwa distribusi sangat runcing menunjukkan bahwa terdapat fluktuasi nilai yang sangat tajam pada Lokasi tertentu.
data_long <- melt(data_fokus, id.vars = "City")
ggplot(data_long, aes(x = variable, y = value, fill = variable)) + geom_boxplot() + labs(title = "Distribusi Nilai Polutan Udara (Sebelum Standarisasi)", x = "Jenis Polutan", y = "Konsentrasi") + theme_minimal() + theme(legend.position = "none")
## Warning: Removed 29258 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
Berdasarkan hasil visualisasi, hasil menunjukkan bahwa seluruh variabel
memiliki sebaran data yang cukup luas dan memiliki banyak outlier
terutama pada variabel PM2.5 dan PM10. Variabel PM10 menunjukkan rentang
nilai yang paling besar jika dibandingkan variabel lainnya, hal ini
mengindikasikan bahwa variasi tingkat polusi yang lebih tinggi
ggplot(data_long, aes(x = value, fill = variable)) + geom_histogram(bins = 30, color = "white") + facet_wrap(~variable, scales = "free") + labs(title = "Distribusi Frekuensi Tiap Polutan", x = "Konsentrasi", y = "Frekuensi") + theme_minimal() + theme(legend.position = "none")
## Warning: Removed 29258 rows containing non-finite outside the scale range
## (`stat_bin()`).
Berdasarkan hasil visualisasi histogram pada Gambar 3.2, hasil
visualisasi menunjukkan bahwa sebagian besar variabel memiliki
distribusi yang cenderung miring ke kanan (right-skewed). Hasil tersebut
ditunjukkan oleh mayoritas data yang cenderung berada pada nilai rendah
hingga sedang, serta terdapat ebberapa nilai yang sangat tinggi. Hal ini
mengindikasikan adanya nilai ekstrem yang mempengaruhi hasil visualisasi
## DATA PREPROCESSING ## Pengecekan Missing Value
print("Jumlah NA sebelum imputasi:")
## [1] "Jumlah NA sebelum imputasi:"
colSums(is.na(data_fokus))
## City PM2.5 PM10 NO2 CO SO2 O3
## 0 4598 11140 3585 2059 3854 4022
Berdasarkan hasil pengecekan missing value pada masing-maisng variabel, ditemukan jumlah missing value pada masing-masing variabel. Variabel PM2.5 memiliki missing value berjumlah 4598 data, PM10 memiliki missing value berjumlah 11140 data, NO2 memiliki missing value berjumlah 3585, CO memiliki missing value sejumlah 2059, SO2 memiliki missing value sejumlah 3854, dan O3 memiliki missing value sejumlah 4022. ## Penanganan Missing Value: Imputasi Median
data_imputasi <- data_fokus %>%
mutate(across(where(is.numeric), ~ifelse(is.na(.), median(., na.rm = TRUE), .)))
Selanjutnya, dilakukan penanganan missing value pada tiap variabel menggunakan Teknik imputasi. Teknik imputasi yang diterapkan dalam analisis ini adalah dengan menggunakan imputasi nilai median. Teknik imputasi nilai median diterapkan dalam analisis ini karena imputasi dengan nilai median lebih robust terhadap nilai ekstrem.
print("Jumlah Missing Value setelah imputasi:")
## [1] "Jumlah Missing Value setelah imputasi:"
colSums(is.na(data_imputasi))
## City PM2.5 PM10 NO2 CO SO2 O3
## 0 0 0 0 0 0 0
profil_kota <- data_imputasi %>%
group_by(City) %>%
summarise(across(everything(), mean))
nama_kota <- profil_kota$City
data_numerik <- profil_kota %>%
select(-City)
jumlah_outlier <- function(x) {
outliers <- boxplot.stats(x)$out
return(length(outliers))
}
total_outlier <-sapply(data_numerik, jumlah_outlier)
tabel_outlier <- data.frame(
Polutan = names(total_outlier),
Jumlah_Outlier = as.numeric(total_outlier)
)
print(tabel_outlier)
## Polutan Jumlah_Outlier
## 1 PM2.5 4
## 2 PM10 1
## 3 NO2 0
## 4 CO 1
## 5 SO2 3
## 6 O3 3
par(mfrow=c(2,3))
for (col in names(data_numerik)) {
boxplot(data_numerik[[col]],
main = paste("Boxplot", col, "(Sebelum)"),
col = "lightblue",
border = "darkblue")
}
Proses pengecekan outlier dilakukan dengan menggunakan metode
Interquartile Range (IQR), hasil pengecekan outlier dengan penerapan
metode IQR menunjukkan bahwa jumlah outlier pada masing-masing variabel
relative rendah. Meskipun jumlahnya cukup rendah, nilai outlier dapat
menyebabkan terjadinya perbedaan yang berpengaruh terhadap hasil
analisis. ## Penanganan outlier (Wisorizing)
cap_outliers <- function(x) {
qnt <- quantile(x, probs=c(.25, .75), na.rm =TRUE)
cap_bawah <- quantile(x, probs=0.05, na.rm = TRUE)
cap_atas <- quantile(x, probs=0.95, na.rm = TRUE)
H <- 1.5 * IQR(x, na.rm = TRUE)
x[x < (qnt - H)] <- cap_bawah
x[x > (qnt + H)] <- cap_atas
return(x)
}
data_bersih_final <- as.data.frame(lapply(data_numerik, cap_outliers))
print(data_bersih_final)
## PM2.5 PM10 NO2 CO SO2 O3
## 1 61.82629 99.50974 45.750187 2.0787942 25.840387 36.320139
## 2 26.34910 41.64751 7.197431 0.2836283 7.378053 9.321886
## 3 38.44305 77.47925 22.147655 0.6546372 13.909874 37.694353
## 4 54.74517 114.19529 18.636274 0.5723833 8.288034 23.209550
## 5 36.74642 85.47187 27.976212 1.8334395 5.529895 32.722718
## 6 50.01318 118.58799 31.044637 0.8806920 12.943218 49.475791
## 7 61.00302 118.95408 17.810149 1.6530277 9.654989 19.681525
## 8 41.49734 85.65655 11.743684 0.6313487 10.159375 9.321886
## 9 50.31755 90.74253 16.944216 1.0651419 7.900169 32.404699
## 10 29.55404 41.64751 28.582358 0.9488601 8.606218 28.849067
## 11 110.75184 148.63500 45.750187 1.9760528 15.532145 49.475791
## 12 25.52809 41.64751 9.825000 1.6096296 5.585978 30.840000
## 13 110.75184 148.63500 23.241423 1.2336569 9.392633 34.162770
## 14 63.65532 116.60490 13.584920 0.7355777 14.653426 25.081753
## 15 47.12333 92.59490 28.293280 0.5915254 9.173534 33.575090
## 16 54.43682 123.13409 32.283797 0.8014542 11.050476 46.501706
## 17 53.64676 136.95997 12.194705 0.7626433 25.840387 32.024919
## 18 31.42852 67.33543 15.195494 1.2966667 17.595988 9.321886
## 19 63.29575 114.28156 39.821695 0.7992506 8.450590 30.674091
## 20 110.75184 95.68000 33.106371 2.1130413 9.924520 36.835819
## 21 43.35181 96.08450 23.144505 0.5723644 11.531130 31.720289
## 22 110.75184 98.87375 35.753127 1.4812863 20.701200 36.290188
## 23 34.48768 54.10297 6.355484 0.3089032 7.066968 28.488774
## 24 58.85222 152.20882 15.694378 1.6944865 25.840387 9.321886
## 25 28.81216 54.32681 9.723273 0.9504946 5.754227 34.574254
## 26 47.22138 104.29332 34.563372 0.7408276 12.116696 36.476840
par(mfrow=c(2,3))
for (col in names(data_bersih_final)) {
boxplot(data_bersih_final[[col]],
main = paste("Boxplot", col, "(Sesudah)"),
col = "lightgreen",
border = "darkgreen")
}
Metode winsorizing dipilih sebagai metode penanganan outlier karena
mampu bekerja dengan membatasi nilai ekstrem ke dalam rentang tertentu
tanpa menghapus data. Sehingga, dapat menghindari terjadinya penghapusan
informasi penting dalam dataset. Dalam implementasinya, nilai yang
berada jauh di bawah batas bawah akan diganti dengan nilai di percentil
ke-5, sementara nilai yang berada jauh di atas batas atas akan diganti
dengan nilai persentil ke-95. Hasil dari penanganan outlier ini adalah
distribusi data menjadi lebih stabil serta berada dalam kondisi terbaik
untuk digunakan pada analisis lebih lanjut. ## Standarisasi menggunakan
Z-score
data_scaled <- as.data.frame(scale(data_bersih_final))
Metode Z-score dipilih dikarenakan mampu mengubah skala data sehingga setiap variabel memiliki rata-rata sebesar 0 dan standar deviasi sebesar 1. Serta, berdasarkan hasil statistik deskriptif sebelumnya diketahui bahwa data memiliki skala yang perbedaannya cukup signifikan dan memiliki nilai ekstrem, sehingga standarisasi dengan menerapkan Z-score digunakan agar setiap variabel dapat memberikan kontribusi yang seimbang dalam proses pengelompokan data. ## Menambahkan kolom City untuk mengetahui barus tersebut milik kota mana
data_final <- cbind(City = nama_kota, data_scaled)
head(data_final)
## City PM2.5 PM10 NO2 CO SO2 O3
## 1 Ahmedabad 0.23396634 0.07941285 1.9360209 1.8004311 2.1874362 0.5420247
## 2 Aizawl -1.09704714 -1.67847520 -1.3918402 -1.4593704 -0.8004018 -1.8366096
## 3 Amaravati -0.64331321 -0.58988603 -0.1013419 -0.7856636 0.2566705 0.6630974
## 4 Amritsar -0.03169935 0.52556854 -0.4044432 -0.9350267 -0.6531357 -0.6130611
## 5 Bengaluru -0.70696637 -0.34706579 0.4017772 1.3548970 -1.0994971 0.2250801
## 6 Bhopal -0.20923132 0.65902139 0.6666426 -0.3751757 0.1002325 1.7010805
ggplot(data_final, aes(x = PM2.5, y = NO2)) +
geom_point(color = "darkblue", alpha = 0.6, size = 3) +
geom_text(aes(label = City), check_overlap = TRUE, vjust = -1, size = 2.5) +
geom_vline(xintercept = 0, linetype = "dashed", color = "red") +
geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
labs(title = "Scatter Plot: PM2.5 vs NO2",
subtitle = "Melihat potensi pengelompokan kota berdasarkan polutan debu dan kendaraan",
x = "PM2.5",
y = "NO2") +
theme_bw()
Berdasarkan hasil visualisasi menggunakan visualisasi scatter plot,
ditemukan adanya korelasi positif antara variabel PM2.5 dan NO2. Hal ini
menunjukan bahwa kota-kota di India yang memiliki kadar PM2.5 tinggi
cenderung juga memiliki kadar NO2 yang tinggi juga, hal ini
mengindikasikan bahwa sumber polusi debu hasil dan asap kendaraan sering
kali muncul dari aktivitas yang berkaitan pada wilayah tersebut. Garis
putus-putus merah pada visualisasi juga menunjukkan pembagian kota di
India menjadi 4 kategori profil yaitu Kuadran kanan atas yang
menunjukkan kota dengan tingkat polusi tinggi, Kuadran kiri atas yang
menunjukkan kota yang memiliki udara yang relative lebih bersih akan
tetapi memiliki masalah pada emisi gas, Kuadran kiri bawah yang
menunjukkan kota yang memiliki kualitas udara lebih baik dan tingkat
polusi yang cukup rendah, dan Kuadran kanan bawah menunjukkan kualitas
udara yang sangat berdebu akan tetapi kadar gas nitrogen dioksidanya
rendah.
fviz_nbclust(data_scaled, kmeans, method = "wss") +
geom_vline(xintercept = 3, linetype = 2, color = "red") +
labs(title = "Elbow Method: Penentukan K Optimal untuk K-Means")
Tahap awal pada proses clustering dengan menerapkan algoritma K-Means
adalah dengan menentukan jumlah cluster optimal, proses penentuan jumlah
cluster optimal ini menggunakan metode Elbow. Metode Elbow bekerja
dengan melihat nilai Within Sum of Squares (WSS) terhadap jumlah
cluster. Pada analisis, hasil penentuan jumlah cluster optimal dengan
menggunakan metode Elbow menunjukkan hasil jumlah cluster yang paling
optimal yaitu sebanyak 3 cluster.
set.seed(123)
clustering_kmeans <- kmeans(data_scaled, centers = 3, nstart = 25)
rownames(data_scaled) <- profil_kota$City
fviz_cluster(clustering_kmeans, data = data_scaled,
geom = c("point", "text"),
labelsize = 8,
repel = TRUE,
palette = "jco",
ggtheme = theme_minimal(),
main = "Hasil Klasterisasi: K-Means (K = 3)")
Analisis K-Means Clustering pada data kualitas udara ini menghasilkan
tiga kelompok dengan gradien polusi yang kontras, di mana sumbu Dim1
(48,5%) berperan sebagai indikator utama tingkat polutan dan Dim2
(20,1%) menjelaskan variasi spesifik antar kota. Cluster 2 (Kuning) yang
berada pada koordinat negatif Dim1 diidentifikasi sebagai kelompok
dengan kualitas udara terburuk, mencakup kota-kota dengan polusi
historis tinggi seperti Delhi dan Patna. Sebaliknya, Cluster 1 (Abu-abu)
yang terpusat di sekitar titik nol menunjukkan kategori moderat, yang
mencerminkan profil udara kota metropolitan besar seperti Mumbai dan
Bengaluru di mana tingkat polusi masih dalam batas ambang kesehatan
umum. Sementara itu, Cluster 3 (Biru) yang menempati area positif di
sisi kanan sumbu Dim1 dikategorikan sebagai kelompok dengan kualitas
udara sangat baik, yang secara geografis didominasi oleh kota-kota di
wilayah pesisir dan dataran tinggi seperti Kochi, Aizawl, dan Shillong.
Pemetaan ini menunjukkan adanya korelasi kuat antara letak geografis
wilayah dengan kemampuan sirkulasi udara dalam menekan akumulasi
polutan.
# 1. Menghitung Matriks Jarak (Euclidean)
dist_matriks <- dist(data_scaled, method = "euclidean")
# 2. Clustering Hierarchical dengan metode Ward
hierarchical_cluster <- hclust(dist_matriks, method = "ward.D2")
Metode ward.D2 merupakan metode pengelompokkan data yang bekerja dengan mengelompokkan data berdasarkan minimalkan Total Within-Cluster Variance. Metode ini memilih pasangan cluster yang menghasilkan peningkatan terkecil dalam total jumlah kuadrat kesalahan (Sum of Squares Error).
# Visualisasi
fviz_dend(hierarchical_cluster, k = 3,
cex = 0.6,
horiz = TRUE,
rect = TRUE,
rect_fill = TRUE,
main = "Hasil Klasterisasi: Hierarchical Clustering (Ward Method)")
Penerapan Hierarchical Clustering dengan metode Ward’s Linkage
menghasilkan tiga kelompok utama yang terpolarisasi berdasarkan nilai
Height pada dendrogram, yang mencerminkan tingkat ekstremitas profil
udara antar kota. Cluster Merah memiliki garis horizontal tertinggi,
mengidentifikasi kelompok dengan karakteristik polusi paling ekstrem
yang mencakup kota-kota seperti Delhi, Patna, dan Lucknow. Sementara
itu, Cluster Hijau (Mumbai, Bengaluru, Chennai, dkk.) menunjukkan jarak
antar cabang yang sangat pendek, menandakan bahwa kualitas udara di
kota-kota metropolitan tersebut relatif identik dan berada pada kategori
moderat. Di sisi lain, Cluster Biru (Aizawl, Shillong, Kochi, dkk.)
membentuk dahan yang terpisah jauh dari klaster lainnya dengan nilai
ketinggian yang rendah, yang mengukuhkan kelompok ini sebagai wilayah
dengan profil udara paling bersih. Pemisahan dahan yang kontras ini
menegaskan bahwa metode Hierarki mampu menangkap perbedaan signifikan
antara wilayah dengan beban polusi berat dan wilayah dengan sirkulasi
udara yang terjaga secara geografis.
# 1. Menentukan Epsilon
dbscan::kNNdistplot(data_scaled, k = 5)
abline(h = 1.2, col = "red", lty = 2)
Dalam penelitian ini, reduksi dimensi yang digunakan terdiri dari 2
komponen utama, yaitu Dim1 dan Dim2, maka secara teori batas Minimal Pts
adalah 3. Data yang digunakan dalam penelitian memiliki outlier yang
cukup banyak, oleh karena itu dengan mempertimbangkan stabilitas model
maka MinPts yang digunakan adalah 5. MinPts 5 dipilih dengan tujuan agar
titik-titik data yang berdiri sendiri dapat diidentifikasi secara akurat
sebagai noise. Hasil Grafik menunjukkan bahwa titik siku mulai terlihat
pada jarak nilai 1.1, namun pada implementasi clustering nilai epsilon
yang digunakan adalah 2.0. Hal tersebut dilakukann dengan tujuan agar
mayoritas kota dengan profil moderat dapat tetap membentuk sebuah
cluster yang stabil.
# 2. Clustering DBSCAN
set.seed(123)
dbscan_clustering <- dbscan(data_scaled, eps = 2.0, minPts = 5)
print(dbscan_clustering)
## DBSCAN clustering for 26 objects.
## Parameters: eps = 2, minPts = 5
## Using euclidean distances and borderpoints = TRUE
## The clustering contains 1 cluster(s) and 9 noise points.
##
## 0 1
## 9 17
##
## Available fields: cluster, eps, minPts, metric, borderPoints
table(dbscan_clustering$cluster)
##
## 0 1
## 9 17
# Visualisasi
fviz_cluster(dbscan_clustering, data = data_scaled, stand = FALSE,
ellipse = FALSE, show.clust.cent = FALSE,
geom = c("point", "text"),
labelsize = 8,
repel = TRUE,
palette = "jco",
ggtheme = theme_minimal(),
main = "Hasil Klasterisasi: DBSCAN")
Berdasarkan hasil clustering DBSCAN mengidentifikasi satu cluster utama
yang terdiri dari 17 Kota. Cluster 1 ini dikategorikan sebagai Cluster
yang memiliki kualitas udara moderate karena posisi koordinat titik data
17 kota tersebut berpusat pada sekitar titik nol pada sumbu Dim1. Titik
Nol merepresentasikan kualitas udara yang standar dan yang paling umum
pada mayoritas data sampel. Hal ini menunjukkan adanya homogenitas yang
tinggi diantara kota-kota metropolitan besar yang saling berdekatan
dalam ruang kerapatan data. Sementara itu, terdeteksi 9 noise points
yang dianggap sebagai nilai ekstrem seperti kota Delhi, Patna, dan
Ahmebad yang diidentifikasi sebagai noise karena posisinya terlalu jauh
di sisi kiri grafik yang dianggap polusi sangat tinggi. Smenetara itu
kota Aizawl dianggap sebagai noise karena letaknya berada di sisi kanan
yang dianggap memiliki kualitas udara sangat baik.
# 1. Silhouette score K-Means Clustering
silhouette_kmeans <- silhouette(clustering_kmeans$cluster, dist(data_scaled))
avg_silhouette_kmeans <- mean(silhouette_kmeans[, 3])
# 2. Silhouette Score Hierarchical Clustering
silhouette_hierarchical <- silhouette(cutree(hierarchical_cluster, k =3), dist(data_scaled))
avg_silhouette_hierarchical <- mean(silhouette_hierarchical[, 3])
# 3. Sihouette Score DBSCAN
dbscan_cluster <- dbscan_clustering$cluster
data_filtered <- data_scaled[dbscan_cluster > 0, ]
cluster_filtered <- dbscan_cluster[dbscan_cluster > 0]
if(length(unique(cluster_filtered)) > 1) {
silhouette_dbscan <- silhouette(cluster_filtered, dist(data_filtered))
avg_silhouette_dbscan <- mean(silhouette_dbscan[, 3])
print(paste("Average Silhouette Score DBSCAN:", avg_silhouette_dbscan))
} else {
avg_silhouette_dbscan <- NA
print("DBSCAN hanya menemukan 1 cluster, Silhouette Score tidak dapat dihitung.")
}
## [1] "DBSCAN hanya menemukan 1 cluster, Silhouette Score tidak dapat dihitung."
print(avg_silhouette_kmeans)
## [1] 0.2236397
print(avg_silhouette_hierarchical)
## [1] 0.2265541
print(avg_silhouette_dbscan)
## [1] NA
data.frame(Metode = c("K-Means", "Hierarchical", "DBSCAN"),
Silhouette_Score = c(avg_silhouette_kmeans, avg_silhouette_hierarchical, avg_silhouette_dbscan))
## Metode Silhouette_Score
## 1 K-Means 0.2236397
## 2 Hierarchical 0.2265541
## 3 DBSCAN NA
Evaluasi performa menunjukkan bahwa Hierarchical Clustering memiliki efektivitas tertinggi dengan rata-rata Silhouette Score sebesar 0.2266, sedikit mengungguli metode K-Means yang menghasilkan skor 0.2236. Skor di kisaran 0.22 ini mengindikasikan struktur klaster yang tergolong lemah (weak structure), namun tetap representatif mengingat karakteristik data kualitas udara antar wilayah yang bersifat kontinu tanpa batasan yang kaku. Di sisi lain, metode DBSCAN menghasilkan nilai NA (Not Available) karena hanya membentuk satu klaster padat, sehingga perbandingan jarak antar-klaster tidak dapat dilakukan. Meskipun demikian, DBSCAN memberikan nilai tambah yang signifikan sebagai instrumen detektor anomali dengan keberhasilannya mengidentifikasi 9 kota sebagai pencilan (noise) ekstrem yang tidak tertangkap oleh metode konvensional lainnya. Dengan demikian, Hierarchical Clustering adalah metode terbaik untuk segmentasi profil udara secara umum, sementara DBSCAN sangat efektif untuk pemantauan titik-titik polusi kritis yang memerlukan perhatian khusus.
Melalui tahapan data understanding, preprocessing, hingga klasterisasi menggunakan metode K-Means, Hierarchical, dan DBSCAN, penelitian ini berhasil memetakan profil kualitas udara kota-kota di India ke dalam kategori buruk, moderat, dan baik. Hasil evaluasi menggunakan Silhouette Score menunjukkan bahwa Hierarchical Clustering (Metode Ward) merupakan algoritma paling optimal dengan nilai 0.2266, yang terbukti mampu menghasilkan struktur kelompok yang lebih solid dan stabil dibandingkan K-Means. Temuan ini juga mengungkap bahwa faktor geografis, seperti lokasi di wilayah pesisir dan pegunungan, menjadi pembeda signifikan dalam sebaran polutan. Sementara itu, keberhasilan DBSCAN dalam mengidentifikasi kota-kota ekstrem sebagai pencilan (noise) menegaskan pentingnya kebijakan lingkungan yang lebih spesifik bagi wilayah dengan tingkat polusi anomali, yang tidak dapat disamaratakan dengan wilayah berkategori moderat.