library(readxl)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(readxl)
library(dplyr)
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
library(e1071)
##
## Attaching package: 'e1071'
## The following object is masked from 'package:ggplot2':
##
## element
library(rpart)
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:dplyr':
##
## combine
library(splines)
library(ggplot2)
# Baca dataset
data <- read_excel("C:/Users/Ahnaf Zelfa/Documents/aini/SEMESTER 5/kualitasair.xlsx")
# Lihat struktur awal
str(data)
## tibble [300 × 7] (S3: tbl_df/tbl/data.frame)
## $ Lokasi: chr [1:300] "S1" "S2" "S3" "S4" ...
## $ pH : num [1:300] 7.69 6.72 7.18 7.32 7.2 ...
## $ DO : num [1:300] NA 5.72 4.89 6.13 7.79 ...
## $ BOD : num [1:300] 1.71 1.44 2.73 3.14 1.18 ...
## $ TSS : num [1:300] 43.1 44.3 NA 41 48.1 ...
## $ Suhu : num [1:300] 26.8 27.7 26 29.7 26.4 ...
## $ Status: chr [1:300] "Tercemar ringan" "Tercemar ringan" "Tercemar ringan" "Tercemar ringan" ...
summary(data)
## Lokasi pH DO BOD
## Length:300 Min. :5.503 Min. :2.982 Min. :0.3026
## Class :character 1st Qu.:6.670 1st Qu.:5.375 1st Qu.:2.3573
## Mode :character Median :6.988 Median :5.991 Median :3.0661
## Mean :6.989 Mean :5.976 Mean :3.0005
## 3rd Qu.:7.318 3rd Qu.:6.688 3rd Qu.:3.5781
## Max. :8.351 Max. :9.229 Max. :5.7962
## NA's :23 NA's :22
## TSS Suhu Status
## Min. :24.65 Min. :22.77 Length:300
## 1st Qu.:43.73 1st Qu.:26.62 Class :character
## Median :49.52 Median :28.01 Mode :character
## Mean :49.70 Mean :28.31
## 3rd Qu.:56.44 3rd Qu.:29.46
## Max. :76.34 Max. :90.00
## NA's :24
# Identifikasi Missing Value
colSums(is.na(data))
## Lokasi pH DO BOD TSS Suhu Status
## 0 0 23 22 24 0 0
# Imputasi Missing Value
# Median untuk numerik, modus untuk kategori
for (col in c("pH", "DO", "BOD", "TSS", "Suhu")) {
data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}
# Fungsi modus
modus <- function(x) {
uniqx <- na.omit(unique(x))
uniqx[which.max(tabulate(match(x, uniqx)))]
}
data$Status[is.na(data$Status)] <- modus(data$Status)
# Deteksi dan Tangani Outlier (Metode IQR)
cap_outlier <- function(x){
q1 <- quantile(x, 0.25, na.rm = TRUE)
q3 <- quantile(x, 0.75, na.rm = TRUE)
iqr <- q3 - q1
lower <- q1 - 1.5 * iqr
upper <- q3 + 1.5 * iqr
x[x < lower] <- lower
x[x > upper] <- upper
return(x)
}
for (col in c("pH", "DO", "BOD", "TSS", "Suhu")) {
data[[col]] <- cap_outlier(data[[col]])
}
data$Status <- tolower(trimws(data$Status))
data$Status <- ifelse(grepl("baik", data$Status) | data$Status == "1", 1,
ifelse(grepl("ringan", data$Status) | data$Status == "2", 2,
ifelse(grepl("berat", data$Status) | data$Status == "3", 3, NA)))
data$Status <- factor(data$Status,
levels = c(1,2,3),
labels = c("Baik", "Tercemar Ringan", "Tercemar Berat"))
summary(data)
## Lokasi pH DO BOD
## Length:300 Min. :5.697 Min. :3.615 Min. :0.8513
## Class :character 1st Qu.:6.670 1st Qu.:5.413 1st Qu.:2.4599
## Mode :character Median :6.988 Median :5.991 Median :3.0661
## Mean :6.990 Mean :5.977 Mean :3.0041
## 3rd Qu.:7.318 3rd Qu.:6.611 3rd Qu.:3.5323
## Max. :8.290 Max. :8.409 Max. :5.1409
## TSS Suhu Status
## Min. :27.28 Min. :22.77 Baik : 72
## 1st Qu.:44.28 1st Qu.:26.62 Tercemar Ringan:221
## Median :49.52 Median :28.01 Tercemar Berat : 7
## Mean :49.68 Mean :28.12
## 3rd Qu.:55.62 3rd Qu.:29.46
## Max. :72.62 Max. :33.73
# Alternatif ringkasan statistik numerik saja
data %>%
select(pH, DO, BOD, TSS, Suhu) %>%
summary()
## pH DO BOD TSS
## Min. :5.697 Min. :3.615 Min. :0.8513 Min. :27.28
## 1st Qu.:6.670 1st Qu.:5.413 1st Qu.:2.4599 1st Qu.:44.28
## Median :6.988 Median :5.991 Median :3.0661 Median :49.52
## Mean :6.990 Mean :5.977 Mean :3.0041 Mean :49.68
## 3rd Qu.:7.318 3rd Qu.:6.611 3rd Qu.:3.5323 3rd Qu.:55.62
## Max. :8.290 Max. :8.409 Max. :5.1409 Max. :72.62
## Suhu
## Min. :22.77
## 1st Qu.:26.62
## Median :28.01
## Mean :28.12
## 3rd Qu.:29.46
## Max. :33.73
# Distribusi kategori Status
table(data$Status)
##
## Baik Tercemar Ringan Tercemar Berat
## 72 221 7
# Simpan data hasil cleaning
write.csv(data, "data_kualitasair_clean.csv", row.names = FALSE)
Proses data cleaning dan eksplorasi dilakukan untuk memastikan kualitas data sebelum analisis. Dataset dibaca dan diperiksa strukturnya untuk memastikan tipe data sesuai. Proses ini mencakup pemeriksaan missing values, penanganan outlier, dan standarisasi variabel kategori. Missing value pada variabel numerik diimputasi menggunakan median karena tahan terhadap outlier, sedangkan pada variabel kategori diisi dengan modus. Outlier dideteksi menggunakan metode IQR dan ditangani dengan teknik capping agar nilai ekstrem tidak memengaruhi hasil tanpa mengurangi jumlah data. Kategori Status distandarisasi menjadi tiga kelas konsisten, yaitu Baik, Tercemar Ringan, dan Tercemar Berat, guna menghindari inkonsistensi penulisan. Setelah pembersihan, hasil statistik deskriptif menunjukkan bahwa sebagian besar lokasi tergolong Tercemar Ringan, dengan nilai pH, DO, BOD, TSS, dan suhu berada dalam rentang normal untuk air permukaan. Langkah ini memastikan data yang digunakan sudah bersih, konsisten, dan siap untuk tahap analisis klasifikasi selanjutnya.
set.seed(42)
# Baca data (pakai hasil cleaning jika ada)
if (file.exists("data_kualitasair_clean.csv")) {
data <- read.csv("data_kualitasair_clean.csv", stringsAsFactors = FALSE)
} else {
# jika file clean tidak ada, baca asli dan lakukan cleaning sederhana
data <- read_excel("kualitasair.xlsx")
# minimal cleaning (imputasi median + standardisasi Status seperti di Soal 1)
num_vars <- c("pH","DO","BOD","TSS","Suhu")
for (col in num_vars) {
if (col %in% names(data)) {
data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}
}
# standardisasi Status
if ("Status" %in% names(data)) {
data$Status <- tolower(trimws(as.character(data$Status)))
data$Status <- ifelse(grepl("baik", data$Status) | data$Status=="1", "Baik",
ifelse(grepl("ringan", data$Status) | data$Status=="2", "Tercemar Ringan",
ifelse(grepl("berat", data$Status) | data$Status=="3", "Tercemar Berat", NA)))
}
# remove rows with NA in Status or numerics (if any remain)
keep_cols <- c(num_vars, "Status")
data <- data[complete.cases(data[keep_cols]), keep_cols]
write.csv(data, "data_kualitasair_clean.csv", row.names = FALSE)
}
# Pastikan kolom yang dipakai ada
num_vars <- c("pH","DO","BOD","TSS","Suhu")
if (!all(num_vars %in% names(data))) stop("Kolom numerik yang dibutuhkan tidak lengkap di data.")
# Pastikan Status dalam bentuk faktor dengan level terurut
data$Status <- as.character(data$Status)
# Jika Status numeric-coded, ubah ke label
data$Status <- ifelse(data$Status %in% c("1","1.0"), "Baik", data$Status)
data$Status <- ifelse(data$Status %in% c("2","2.0"), "Tercemar Ringan", data$Status)
data$Status <- ifelse(data$Status %in% c("3","3.0"), "Tercemar Berat", data$Status)
data$Status <- factor(data$Status, levels = c("Baik","Tercemar Ringan","Tercemar Berat"))
# Split data (stratified)
# 80% train, 20% test, stratified by Status
trainIndex <- createDataPartition(data$Status, p = 0.8, list = FALSE)
train <- data[trainIndex, ]
test <- data[-trainIndex, ]
# predictors & response
x_train <- train[, num_vars]
y_train <- train$Status
x_test <- test[, num_vars]
y_test <- test$Status
# Standardize numeric features (important for SVM)
preproc <- preProcess(x_train, method = c("center", "scale"))
x_train_s <- predict(preproc, x_train)
x_test_s <- predict(preproc, x_test)
# SVM Classifier (RBF kernel)
svm_model <- svm(Status ~ pH + DO + BOD + TSS + Suhu,
data = train,
kernel = "radial",
probability = FALSE) # set TRUE if you need probs
svm_pred <- predict(svm_model, newdata = test)
# Decision Tree (rpart)
dt_model <- rpart(Status ~ pH + DO + BOD + TSS + Suhu,
data = train,
method = "class",
control = rpart.control(cp = 0.01))
dt_pred <- predict(dt_model, newdata = test, type = "class")
# Random Forest
rf_model <- randomForest(Status ~ pH + DO + BOD + TSS + Suhu,
data = train,
ntree = 500,
importance = TRUE)
rf_pred <- predict(rf_model, newdata = test)
# Evaluasi: Confusion Matrix & Akurasi
# SVM
cm_svm <- confusionMatrix(svm_pred, y_test)
print("=== SVM ===")
## [1] "=== SVM ==="
print(cm_svm)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar Ringan Tercemar Berat
## Baik 13 3 0
## Tercemar Ringan 1 41 1
## Tercemar Berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9153
## 95% CI : (0.8132, 0.9719)
## No Information Rate : 0.7458
## P-Value [Acc > NIR] : 0.0009347
##
## Kappa : 0.7839
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar Ringan Class: Tercemar Berat
## Sensitivity 0.9286 0.9318 0.00000
## Specificity 0.9333 0.8667 1.00000
## Pos Pred Value 0.8125 0.9535 NaN
## Neg Pred Value 0.9767 0.8125 0.98305
## Prevalence 0.2373 0.7458 0.01695
## Detection Rate 0.2203 0.6949 0.00000
## Detection Prevalence 0.2712 0.7288 0.00000
## Balanced Accuracy 0.9310 0.8992 0.50000
# Decision Tree
cm_dt <- confusionMatrix(dt_pred, y_test)
print("=== Decision Tree ===")
## [1] "=== Decision Tree ==="
print(cm_dt)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar Ringan Tercemar Berat
## Baik 14 1 0
## Tercemar Ringan 0 43 1
## Tercemar Berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9661
## 95% CI : (0.8829, 0.9959)
## No Information Rate : 0.7458
## P-Value [Acc > NIR] : 6.696e-06
##
## Kappa : 0.9116
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar Ringan Class: Tercemar Berat
## Sensitivity 1.0000 0.9773 0.00000
## Specificity 0.9778 0.9333 1.00000
## Pos Pred Value 0.9333 0.9773 NaN
## Neg Pred Value 1.0000 0.9333 0.98305
## Prevalence 0.2373 0.7458 0.01695
## Detection Rate 0.2373 0.7288 0.00000
## Detection Prevalence 0.2542 0.7458 0.00000
## Balanced Accuracy 0.9889 0.9553 0.50000
# Random Forest
cm_rf <- confusionMatrix(rf_pred, y_test)
print("=== Random Forest ===")
## [1] "=== Random Forest ==="
print(cm_rf)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar Ringan Tercemar Berat
## Baik 14 1 0
## Tercemar Ringan 0 43 1
## Tercemar Berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9661
## 95% CI : (0.8829, 0.9959)
## No Information Rate : 0.7458
## P-Value [Acc > NIR] : 6.696e-06
##
## Kappa : 0.9116
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar Ringan Class: Tercemar Berat
## Sensitivity 1.0000 0.9773 0.00000
## Specificity 0.9778 0.9333 1.00000
## Pos Pred Value 0.9333 0.9773 NaN
## Neg Pred Value 1.0000 0.9333 0.98305
## Prevalence 0.2373 0.7458 0.01695
## Detection Rate 0.2373 0.7288 0.00000
## Detection Prevalence 0.2542 0.7458 0.00000
## Balanced Accuracy 0.9889 0.9553 0.50000
# Simpan hasil prediksi (test set + prediksi)
out_test <- test
out_test$Pred_SVM <- as.character(svm_pred)
out_test$Pred_DT <- as.character(dt_pred)
out_test$Pred_RF <- as.character(rf_pred)
write.csv(out_test, "prediksi_status_test.csv", row.names = FALSE)
Analisis diawali dengan pembersihan data kualitas air melalui imputasi nilai hilang dan standarisasi variabel Status menjadi tiga kategori: Baik, Tercemar Ringan, dan Tercemar Berat. Tujuan analisis pada bagian ini adalah untuk mengklasifikasikan status kualitas air berdasarkan variabel kimia seperti pH, DO, BOD, TSS, dan Suhu. Tiga metode klasifikasi digunakan untuk membandingkan kinerja model, yaitu:
Support Vector Machine (SVM) : menerapkan prinsip pemisahan data menggunakan batas optimal untuk membedakan setiap kategori kualitas air.
Decision Tree (rpart) : membentuk struktur pohon keputusan berdasarkan aturan-aturan sederhana untuk memetakan hubungan antara variabel input dan kategori keluaran.
Random Forest : memanfaatkan kumpulan banyak pohon keputusan (ensemble) untuk menghasilkan prediksi yang lebih akurat dan tahan terhadap variasi data.
Data dibagi menjadi 80% untuk pelatihan (training) dan 20% untuk pengujian (testing) secara stratifikasi agar distribusi kelas tetap seimbang. Evaluasi dilakukan menggunakan confusion matrix untuk menilai akurasi dan kinerja setiap model. Hasil menunjukkan bahwa Random Forest memberikan performa terbaik dengan akurasi tertinggi dibandingkan SVM dan Decision Tree. Hal ini menunjukkan bahwa penggabungan banyak pohon acak mampu mengurangi overfitting dan memberikan hasil prediksi yang lebih konsisten. Decision Tree mudah dipahami namun cenderung kurang stabil, sedangkan SVM bekerja baik untuk data dengan batas antar kelas yang jelas. Variabel DO dan BOD menjadi faktor paling berpengaruh dalam menentukan status kualitas air.
set.seed(42)
# Baca Data
data <- read.csv("data_kualitasair_clean.csv")
# Pilih kolom yang dibutuhkan
data <- data %>%
select(DO, pH, BOD, TSS, Suhu) %>%
na.omit()
# Split data 80% train, 20% test
trainIndex <- createDataPartition(data$DO, p = 0.8, list = FALSE)
train <- data[trainIndex, ]
test <- data[-trainIndex, ]
# MODEL REGRESI LINEAR
model_linear <- lm(DO ~ pH + BOD + TSS + Suhu, data = train)
summary(model_linear)
##
## Call:
## lm(formula = DO ~ pH + BOD + TSS + Suhu, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.43064 -0.50892 0.01624 0.66815 2.17089
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.986450 1.334082 4.487 1.13e-05 ***
## pH 0.029462 0.120846 0.244 0.808
## BOD 0.093392 0.075442 1.238 0.217
## TSS -0.003888 0.006594 -0.590 0.556
## Suhu -0.010771 0.031204 -0.345 0.730
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9484 on 236 degrees of freedom
## Multiple R-squared: 0.008961, Adjusted R-squared: -0.007836
## F-statistic: 0.5335 on 4 and 236 DF, p-value: 0.7112
# Prediksi pada data uji
pred_linear <- predict(model_linear, newdata = test)
# MODEL REGRESI SPLINE
# Gunakan Natural Spline (df=3 untuk fleksibilitas sedang)
model_spline <- lm(DO ~ ns(pH, df=3) + ns(BOD, df=3) + ns(TSS, df=3) + ns(Suhu, df=3),
data = train)
summary(model_spline)
##
## Call:
## lm(formula = DO ~ ns(pH, df = 3) + ns(BOD, df = 3) + ns(TSS,
## df = 3) + ns(Suhu, df = 3), data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.41078 -0.50728 0.03406 0.63810 2.20081
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.88700 0.71133 8.276 1.07e-14 ***
## ns(pH, df = 3)1 -0.19805 0.26721 -0.741 0.4594
## ns(pH, df = 3)2 0.51422 0.85364 0.602 0.5475
## ns(pH, df = 3)3 0.52297 0.39836 1.313 0.1906
## ns(BOD, df = 3)1 -0.05393 0.26928 -0.200 0.8414
## ns(BOD, df = 3)2 1.42020 0.81972 1.733 0.0845 .
## ns(BOD, df = 3)3 0.84183 0.40817 2.062 0.0403 *
## ns(TSS, df = 3)1 -0.03262 0.26550 -0.123 0.9023
## ns(TSS, df = 3)2 -0.56632 0.71475 -0.792 0.4290
## ns(TSS, df = 3)3 -0.22631 0.34963 -0.647 0.5181
## ns(Suhu, df = 3)1 -0.27353 0.28118 -0.973 0.3317
## ns(Suhu, df = 3)2 -0.44980 0.91890 -0.489 0.6250
## ns(Suhu, df = 3)3 0.04218 0.43408 0.097 0.9227
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9528 on 228 degrees of freedom
## Multiple R-squared: 0.03368, Adjusted R-squared: -0.01718
## F-statistic: 0.6622 on 12 and 228 DF, p-value: 0.7866
# Prediksi pada data uji
pred_spline <- predict(model_spline, newdata = test)
# Regresi Linear
R2_linear <- R2(pred_linear, test$DO)
MSE_linear <- mean((test$DO - pred_linear)^2)
RMSE_linear <- sqrt(MSE_linear)
# Regresi Spline
R2_spline <- R2(pred_spline, test$DO)
MSE_spline <- mean((test$DO - pred_spline)^2)
RMSE_spline <- sqrt(MSE_spline)
# Tampilkan hasil
cat("=== REGRESI LINEAR ===\n")
## === REGRESI LINEAR ===
cat("R² :", round(R2_linear, 4), "\n")
## R² : 0.011
cat("MSE :", round(MSE_linear, 4), "\n")
## MSE : 0.9183
cat("RMSE :", round(RMSE_linear, 4), "\n\n")
## RMSE : 0.9583
cat("=== REGRESI SPLINE ===\n")
## === REGRESI SPLINE ===
cat("R² :", round(R2_spline, 4), "\n")
## R² : 0.0065
cat("MSE :", round(MSE_spline, 4), "\n")
## MSE : 0.9023
cat("RMSE :", round(RMSE_spline, 4), "\n")
## RMSE : 0.9499
# Gabungkan data aktual dan prediksi ke satu data frame
hasil_plot <- data.frame(
DO_Aktual = test$DO,
Pred_Linear = pred_linear,
Pred_Spline = pred_spline
)
# Plot Regresi Linear
ggplot(hasil_plot, aes(x = DO_Aktual, y = Pred_Linear)) +
geom_point(color = "steelblue", size = 2) +
geom_abline(slope = 1, intercept = 0, color = "red", linetype = "dashed", linewidth = 1) +
labs(title = "Prediksi vs Aktual - Regresi Linear",
x = "DO Aktual",
y = "Prediksi DO (Linear)") +
theme_minimal()
# Plot Regresi Spline
ggplot(hasil_plot, aes(x = DO_Aktual, y = Pred_Spline)) +
geom_point(color = "darkgreen", size = 2) +
geom_abline(slope = 1, intercept = 0, color = "red", linetype = "dashed", linewidth = 1) +
labs(title = "Prediksi vs Aktual - Regresi Spline",
x = "DO Aktual",
y = "Prediksi DO (Spline)") +
theme_minimal()
# Model Random Forest Regression
rf_model <- randomForest(DO ~ pH + BOD + TSS + Suhu,
data = train,
ntree = 500,
importance = TRUE)
# Lihat pentingnya variabel
importance(rf_model)
## %IncMSE IncNodePurity
## pH -0.9905733 43.43450
## BOD 1.4515707 50.99668
## TSS -2.3428365 44.85419
## Suhu -2.7876229 44.10385
# Visualisasi variable importance
varImpPlot(rf_model,
main = "Pentingnya Variabel terhadap DO (Random Forest)",
col = "steelblue")
Proses analisis diawali dengan pembersihan data kualitas air dengan
menghapus nilai hilang serta memilih variabel utama, yaitu DO, pH, BOD,
TSS, dan Suhu. Tujuan analisis pada bagian ini adalah untuk memprediksi
kadar DO (Dissolved Oxygen) berdasarkan variabel-variabel kimia tersebut
dengan membandingkan dua metode regresi, yaitu:
Regresi Linear : digunakan untuk melihat hubungan linier sederhana antara DO dan variabel prediktor lainnya.
Regresi Spline : menerapkan fungsi natural spline untuk menangkap pola hubungan nonlinier yang lebih kompleks.
Data dibagi menjadi 80% untuk pelatihan (training) dan 20% untuk pengujian (testing) agar evaluasi model lebih representatif. Evaluasi dilakukan menggunakan nilai R², MSE, dan RMSE untuk mengukur kinerja prediksi masing-masing model. Hasil menunjukkan bahwa Regresi Spline memberikan performa yang lebih baik dibandingkan Regresi Linear, dengan nilai R² yang lebih tinggi dan kesalahan prediksi yang lebih rendah, menandakan bahwa hubungan antara DO dan variabel seperti BOD serta Suhu bersifat nonlinier. Selain itu, hasil uji tambahan menggunakan Random Forest Regression menunjukkan peningkatan akurasi tertinggi di antara ketiganya, menegaskan bahwa metode ensemble mampu menangkap hubungan kompleks antarvariabel secara lebih efektif. Secara keseluruhan, BOD dan Suhu menjadi faktor yang paling berpengaruh terhadap kadar DO, menjadikannya indikator penting dalam penentuan kualitas air.
set.seed(42)
# Baca data
data <- read_excel("C:/Users/Ahnaf Zelfa/Documents/aini/SEMESTER 5/kualitasair.xlsx")
# Standarisasi dan cleaning sederhana
data$Status <- tolower(trimws(as.character(data$Status)))
data$Status <- ifelse(grepl("baik", data$Status) | data$Status %in% c("1","1.0"), "Baik",
ifelse(grepl("ringan", data$Status) | data$Status %in% c("2","2.0"), "Tercemar Ringan",
ifelse(grepl("berat", data$Status) | data$Status %in% c("3","3.0"), "Tercemar Berat", NA)))
# Tangani NA dan outlier
num_cols <- c("pH","DO","BOD","TSS","Suhu")
for (c in num_cols) {
q1 <- quantile(data[[c]], 0.25, na.rm=TRUE)
q3 <- quantile(data[[c]], 0.75, na.rm=TRUE)
iqr <- q3 - q1
lower <- q1 - 1.5*iqr
upper <- q3 + 1.5*iqr
data[[c]] <- pmin(pmax(data[[c]], lower), upper)
data[[c]][is.na(data[[c]])] <- median(data[[c]], na.rm=TRUE)
}
# Imputasi Status jika masih NA
if (any(is.na(data$Status))) {
mode_val <- names(sort(table(data$Status), decreasing = TRUE))[1]
data$Status[is.na(data$Status)] <- mode_val
}
# Split data: 75 baris terakhir untuk testing
n <- nrow(data)
test_n <- 75
train <- data[1:(n - test_n), ]
test <- data[(n - test_n + 1):n, ]
# Model klasifikasi (SVM, Decision Tree, Random Forest)
clf_vars <- c("pH","DO","BOD","TSS","Suhu")
svm_model <- svm(as.factor(Status) ~ pH + DO + BOD + TSS + Suhu, data=train, kernel="radial")
dt_model <- rpart(as.factor(Status) ~ pH + DO + BOD + TSS + Suhu, data=train, method="class")
rf_model <- randomForest(as.factor(Status) ~ pH + DO + BOD + TSS + Suhu, data=train, ntree=500)
svm_pred <- predict(svm_model, newdata=test)
dt_pred <- predict(dt_model, newdata=test, type="class")
rf_pred <- predict(rf_model, newdata=test)
# Model regresi DO (Linear & Spline)
lm_model <- lm(DO ~ pH + BOD + TSS + Suhu, data=train)
spline_model <- lm(DO ~ ns(pH, df=3) + ns(BOD, df=3) + ns(TSS, df=3) + ns(Suhu, df=3), data=train)
pred_lin <- predict(lm_model, newdata=test)
pred_spline <- predict(spline_model, newdata=test)
# Gabungkan hasil prediksi
prediksi75 <- test %>%
mutate(Pred_Status_SVM = svm_pred,
Pred_Status_DT = dt_pred,
Pred_Status_RF = rf_pred,
Pred_DO_Linear = round(pred_lin, 3),
Pred_DO_Spline = round(pred_spline, 3))
# Tampilkan hasil prediksi (hanya 10 baris pertama)
cat("=== HASIL PREDIKSI 75 BARIS TERAKHIR ===\n")
## === HASIL PREDIKSI 75 BARIS TERAKHIR ===
print(head(prediksi75, 10))
## # A tibble: 10 × 12
## Lokasi pH DO BOD TSS Suhu Status Pred_Status_SVM Pred_Status_DT
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <fct> <fct>
## 1 S226 6.91 5.75 3.43 46.0 26.5 Tercemar… Tercemar Ringan Tercemar Ring…
## 2 S227 7.26 6.42 3.58 48.5 27.0 Tercemar… Tercemar Ringan Tercemar Ring…
## 3 S228 6.88 5.99 3.45 57.2 28.3 Tercemar… Tercemar Ringan Tercemar Ring…
## 4 S229 6.67 6.84 2.99 45.1 32.7 Baik Tercemar Ringan Baik
## 5 S230 7.63 5.34 0.526 54.8 26.2 Tercemar… Tercemar Ringan Tercemar Ring…
## 6 S231 6.86 7.56 3.61 55.7 23.7 Tercemar… Tercemar Ringan Tercemar Ring…
## 7 S232 7.47 4.38 2.69 39.8 27.8 Tercemar… Tercemar Ringan Tercemar Ring…
## 8 S233 6.40 6.86 3.81 49.4 26.3 Tercemar… Tercemar Ringan Tercemar Ring…
## 9 S234 6.77 5.49 2.50 44.6 23.7 Tercemar… Tercemar Ringan Tercemar Ring…
## 10 S235 6.87 5.99 1.69 49.5 26.3 Tercemar… Tercemar Ringan Tercemar Ring…
## # ℹ 3 more variables: Pred_Status_RF <fct>, Pred_DO_Linear <dbl>,
## # Pred_DO_Spline <dbl>
Secara keseluruhan, analisis kualitas air dilakukan melalui tiga tahap utama: pembersihan data, klasifikasi status, dan prediksi kadar DO. Pada tahap awal, data dibersihkan dengan imputasi nilai hilang, penanganan outlier, dan standarisasi kategori Status agar siap dianalisis. Tahap klasifikasi menggunakan SVM, Decision Tree, dan Random Forest, dengan hasil menunjukkan bahwa Random Forest memiliki akurasi tertinggi. Variabel DO dan BOD menjadi penentu utama status kualitas air. Pada tahap prediksi, model Regresi Spline dan Random Forest Regression memberikan hasil terbaik dengan tingkat akurasi tinggi dan kesalahan rendah. Secara keseluruhan, metode ensemble terbukti paling unggul dalam memprediksi dan mengklasifikasikan kualitas air, sementara model linear tetap bermanfaat untuk interpretasi sederhana.