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(psych)
library(ggplot2)
##
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
##
## %+%, alpha
library(GGally)
library(readr)
library(dplyr)
library(caret)
## 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:psych':
##
## outlier
## The following object is masked from 'package:dplyr':
##
## combine
library(mgcv)
## Loading required package: nlme
##
## Attaching package: 'nlme'
## The following object is masked from 'package:dplyr':
##
## collapse
## This is mgcv 1.9-3. For overview type 'help("mgcv-package")'.
library(broom)
library(scales)
##
## Attaching package: 'scales'
## The following object is masked from 'package:readr':
##
## col_factor
## The following objects are masked from 'package:psych':
##
## alpha, rescale
library(parameters)
##
## Attaching package: 'parameters'
## The following objects are masked from 'package:e1071':
##
## kurtosis, skewness
## The following object is masked from 'package:caret':
##
## compare_models
# Import data training
training <- read.csv("D:/Kuliah/Semester 5/Statistika Lingkungan/Tugas/UTS Statling_M Zidane Yanfa N_2304220004/kualitasair.xlsx - training.csv")
# Strutur data
str(training)
## 'data.frame': 300 obs. of 7 variables:
## $ Lokasi: chr "S1" "S2" "S3" "S4" ...
## $ pH : num 7.69 6.72 7.18 7.32 7.2 ...
## $ DO : num NA 5.72 4.89 6.13 7.79 ...
## $ BOD : num 1.71 1.44 2.73 3.14 1.18 ...
## $ TSS : num 43.1 44.3 NA 41 48.1 ...
## $ Suhu : num 26.8 27.7 26 29.7 26.4 ...
## $ Status: chr "Tercemar ringan" "Tercemar ringan" "Tercemar ringan" "Tercemar ringan" ...
summary(training)
## 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
head(training)
## Lokasi pH DO BOD TSS Suhu Status
## 1 S1 7.6855 NA 1.7136 43.1415 26.7972 Tercemar ringan
## 2 S2 6.7177 5.7236 1.4402 44.2963 27.7284 Tercemar ringan
## 3 S3 7.1816 4.8906 2.7274 NA 26.0255 Tercemar ringan
## 4 S4 7.3164 6.1339 3.1398 41.0104 29.6639 Tercemar ringan
## 5 S5 7.2021 7.7853 1.1778 48.0967 26.4099 baik
## 6 S6 6.9469 8.4222 3.2324 48.5610 28.6809 Tercemar ringan
# Identifikasi Missing Value
colSums(is.na(training)) # jumlah missing value per kolom
## Lokasi pH DO BOD TSS Suhu Status
## 0 0 23 22 24 0 0
# Melakukan Imputasi menggunakan median
training2 <- training %>%
mutate(across(where(is.numeric), ~ifelse(is.na(.), median(., na.rm = TRUE), .)))
# Pada kolom kategori, isi missing dengan modus
mode_func <- function(x) names(sort(table(x), decreasing = TRUE))[1]
training3 <- training2 %>%
mutate(across(where(is.character), ~ifelse(is.na(.), mode_func(.), .)))
# Deteksi outlier menggunakan metode IQR untuk semua kolom numerik
num_cols <- select_if(training3, is.numeric)
outlier_check <- sapply(num_cols, function(x){
Q1 <- quantile(x, 0.25)
Q3 <- quantile(x, 0.75)
IQR <- Q3 - Q1
sum(x < (Q1 - 1.5*IQR) | x > (Q3 + 1.5*IQR))
})
outlier_check
## pH DO BOD TSS Suhu
## 4 4 5 5 2
# Menangani outlier
training4 <- training3 %>%
mutate(across(where(is.numeric),
~ifelse(. < quantile(., 0.05), quantile(., 0.05),
ifelse(. > quantile(., 0.95), quantile(., 0.95), .))))
# Cek dan Standarisasi Inkonsistensi Kategori pada kolom "Status"
unique(training4$Status)
## [1] "Tercemar ringan" "baik" "BAIK" "Baik"
## [5] "tercemar ringan" "Tercemar Ringan" "Tercemar berat"
# Ubah ke huruf besar kecil konsisten, misal huruf besar awal
training4$Status <- tolower(training4$Status)
training4$Status <- trimws(training4$Status)
training4$Status <- ifelse(training4$Status %in% c("baik", "bagus"), "Baik",
ifelse(training4$Status %in% c("buruk", "tidak baik"), "Buruk",
ifelse(training4$Status %in% c("sedang", "cukup"), "Sedang", training4$Status)))
# Ringkasan statistik deskriptif setelah pembersihan
summary(training4)
## Lokasi pH DO BOD
## Length:300 Min. :6.186 Min. :4.334 Min. :1.709
## Class :character 1st Qu.:6.670 1st Qu.:5.413 1st Qu.:2.460
## Mode :character Median :6.988 Median :5.991 Median :3.066
## Mean :6.988 Mean :5.976 Mean :3.009
## 3rd Qu.:7.318 3rd Qu.:6.611 3rd Qu.:3.532
## Max. :7.739 Max. :7.495 Max. :4.337
## TSS Suhu Status
## Min. :33.58 Min. :24.99 Length:300
## 1st Qu.:44.28 1st Qu.:26.62 Class :character
## Median :49.52 Median :28.01 Mode :character
## Mean :49.66 Mean :28.11
## 3rd Qu.:55.62 3rd Qu.:29.46
## Max. :65.20 Max. :31.40
# Visualisasi opsional untuk melihat distribusi data setelah bersih
ggpairs(select_if(training4, is.numeric))
Berdasarkan hasil pembersihan data training, dataset kualitas air telah mengalami proses perbaikan melalui pengisian nilai hilang (missing value) menggunakan median untuk variabel numerik dan modus untuk variabel kategorik. Outlier juga telah ditangani menggunakan metode winsorizing untuk menjaga kestabilan nilai tanpa menghapus data ekstrem. Selain itu, inkonsistensi kategori pada variabel Status telah distandarisasi sehingga penulisannya menjadi seragam, misalnya “Baik”, “Sedang”, dan “Buruk”.
Setelah proses pembersihan, data menjadi lebih konsisten dan siap digunakan untuk analisis lanjutan. Statistik deskriptif menunjukkan bahwa tidak ada lagi nilai kosong atau ekstrem yang mengganggu, sehingga hasil analisis nantinya dapat lebih akurat dan representatif terhadap kondisi kualitas air yang sebenarnya.
# Dataset setelah pembersihan keseluruhan
tc <- training4
# Pilih variabel numerik dan target
tc_model <- tc %>% select(pH, DO, BOD, TSS, Suhu, Status)
tc_model$Status <- as.factor(tc_model$Status)
# Bagi data menjadi training dan testing (80:20)
set.seed(999)
trainIndex <- createDataPartition(tc_model$Status, p = 0.8, list = FALSE)
train_data <- tc_model[trainIndex, ]
test_data <- tc_model[-trainIndex, ]
# SVM
model_svm <- svm(Status ~ ., data = train_data, kernel = "radial")
pred_svm <- predict(model_svm, test_data)
# Decission Tree
model_tree <- rpart(Status ~ ., data = train_data, method = "class")
pred_tree <- predict(model_tree, test_data, type = "class")
# Random Forest
model_rf <- randomForest(Status ~ ., data = train_data, ntree = 100)
pred_rf <- predict(model_rf, test_data)
# Evaluasi Model dengan Confusion Matrix
cm_svm <- confusionMatrix(pred_svm, test_data$Status)
cm_tree <- confusionMatrix(pred_tree, test_data$Status)
cm_rf <- confusionMatrix(pred_rf, test_data$Status)
# Hasil akurasi
cat("Akurasi SVM:", cm_svm$overall['Accuracy'], "\n")
## Akurasi SVM: 0.8813559
cat("Akurasi Decision Tree:", cm_tree$overall['Accuracy'], "\n")
## Akurasi Decision Tree: 0.9491525
cat("Akurasi Random Forest:", cm_rf$overall['Accuracy'], "\n")
## Akurasi Random Forest: 0.9661017
Secara umum, Random Forest cenderung memberikan akurasi tertinggi karena kemampuannya menggabungkan banyak pohon keputusan (ensemble learning) sehingga hasilnya lebih stabil dan tahan terhadap overfitting. Decision Tree memberikan hasil yang lebih mudah diinterpretasikan, namun akurasinya biasanya lebih rendah dari Random Forest. Sedangkan SVM bekerja baik jika data memiliki pemisahan yang jelas antar kelas, meskipun performanya bisa bergantung pada parameter kernel dan skala data.
Dengan demikian, model terbaik yang dapat dipilih berdasarkan nilai akurasi tertinggi dari hasil confusion matrix adalah Model Random Forrest, dan model tersebut dapat digunakan untuk memprediksi status kualitas air pada data baru.
# Pilih variabel yang diperlukan
df <- tc %>%
select(DO, pH, BOD, TSS, Suhu) %>%
na.omit()
# Split train/test (80:20)
set.seed(123)
trainIndex <- createDataPartition(df$DO, p = 0.8, list = FALSE)
train <- df[trainIndex, ]
test <- df[-trainIndex, ]
# Regresi linear
## model linear tanpa scaling
lm_model <- lm(DO ~ pH + BOD + TSS + Suhu, data = train)
summary(lm_model)
##
## Call:
## lm(formula = DO ~ pH + BOD + TSS + Suhu, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.78391 -0.51313 0.02351 0.62275 1.71160
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.643608 1.311722 5.065 8.24e-07 ***
## pH -0.045464 0.128759 -0.353 0.724
## BOD 0.109988 0.080821 1.361 0.175
## TSS 0.002354 0.006572 0.358 0.721
## Suhu -0.028735 0.030261 -0.950 0.343
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.8791 on 236 degrees of freedom
## Multiple R-squared: 0.01231, Adjusted R-squared: -0.004426
## F-statistic: 0.7356 on 4 and 236 DF, p-value: 0.5685
## standardized coefficients (lebih mudah membandingkan pengaruh)
std_coefs <- parameters::model_parameters(lm_model, standardize = "refit")
print(std_coefs)
## Parameter | Coefficient | SE | 95% CI | t(236) | p
## --------------------------------------------------------------------
## (Intercept) | 1.94e-16 | 0.06 | [-0.13, 0.13] | 3.01e-15 | > .999
## pH | -0.02 | 0.06 | [-0.15, 0.10] | -0.35 | 0.724
## BOD | 0.09 | 0.06 | [-0.04, 0.22] | 1.36 | 0.175
## TSS | 0.02 | 0.06 | [-0.10, 0.15] | 0.36 | 0.721
## Suhu | -0.06 | 0.06 | [-0.19, 0.07] | -0.95 | 0.343
##
## Uncertainty intervals (equal-tailed) and p-values (two-tailed) computed
## using a Wald t-distribution approximation.
## Predict pada test
pred_lm <- predict(lm_model, newdata = test)
# Regresi Spline (GAM)
gam_model <- gam(DO ~ s(pH) + s(BOD) + s(TSS) + s(Suhu), data = train, method = "REML")
summary(gam_model)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## DO ~ s(pH) + s(BOD) + s(TSS) + s(Suhu)
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.96983 0.05663 105.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(pH) 1 1.000 0.125 0.724
## s(BOD) 1 1.000 1.852 0.175
## s(TSS) 1 1.000 0.128 0.721
## s(Suhu) 1 1.001 0.900 0.344
##
## R-sq.(adj) = -0.00443 Deviance explained = 1.23%
## -REML = 318.18 Scale est. = 0.77288 n = 241
pred_gam <- predict(gam_model, newdata = test)
# Evaluasi performa model (R2/MSE/RMSE)
eval_metrics <- function(actual, pred) {
res <- actual - pred
mse <- mean(res^2)
rmse <- sqrt(mse)
# R-squared on test: 1 - SSE/SST
sse <- sum(res^2)
sst <- sum((actual - mean(actual))^2)
r2 <- 1 - sse/sst
return(list(R2 = r2, MSE = mse, RMSE = rmse))
}
metrics_lm <- eval_metrics(test$DO, pred_lm)
metrics_gam <- eval_metrics(test$DO, pred_gam)
metrics_table <- data.frame(
Model = c("Linear", "GAM (spline)"),
R2 = c(metrics_lm$R2, metrics_gam$R2),
MSE = c(metrics_lm$MSE, metrics_gam$MSE),
RMSE = c(metrics_lm$RMSE, metrics_gam$RMSE)
)
print(metrics_table)
## Model R2 MSE RMSE
## 1 Linear -0.06990108 0.7564077 0.8697170
## 2 GAM (spline) -0.06989452 0.7564030 0.8697143
# Visualisasi
viz <- test %>%
mutate(pred_lm = pred_lm,
pred_gam = pred_gam,
idx = row_number())
## Scatter plot (Linear)
p1 <- ggplot(viz, aes(x = DO, y = pred_lm)) +
geom_point() +
geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
labs(title = "Aktual vs Prediksi (Linear)", x = "DO Aktual", y = "DO Prediksi (Linear)") +
theme_minimal()
p1
## Scatter plot (GAM)
p2 <- ggplot(viz, aes(x = DO, y = pred_gam)) +
geom_point() +
geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
labs(title = "Aktual vs Prediksi (GAM - spline)", x = "DO Aktual", y = "DO Prediksi (GAM)") +
theme_minimal()
p2
## Residual plot example (GAM)
viz <- viz %>% mutate(resid_gam = DO - pred_gam)
p3 <- ggplot(viz, aes(x = pred_gam, y = resid_gam)) +
geom_point() +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(title = "Residuals vs Prediksi (GAM)", x = "Prediksi (GAM)", y = "Residual (Aktual - Prediksi)") +
theme_minimal()
p3
# Menjelaskan variabel yang paling memengaruhi DO
print(std_coefs)
## Parameter | Coefficient | SE | 95% CI | t(236) | p
## --------------------------------------------------------------------
## (Intercept) | 1.94e-16 | 0.06 | [-0.13, 0.13] | 3.01e-15 | > .999
## pH | -0.02 | 0.06 | [-0.15, 0.10] | -0.35 | 0.724
## BOD | 0.09 | 0.06 | [-0.04, 0.22] | 1.36 | 0.175
## TSS | 0.02 | 0.06 | [-0.10, 0.15] | 0.36 | 0.721
## Suhu | -0.06 | 0.06 | [-0.19, 0.07] | -0.95 | 0.343
##
## Uncertainty intervals (equal-tailed) and p-values (two-tailed) computed
## using a Wald t-distribution approximation.
summary(gam_model)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## DO ~ s(pH) + s(BOD) + s(TSS) + s(Suhu)
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.96983 0.05663 105.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(pH) 1 1.000 0.125 0.724
## s(BOD) 1 1.000 1.852 0.175
## s(TSS) 1 1.000 0.128 0.721
## s(Suhu) 1 1.001 0.900 0.344
##
## R-sq.(adj) = -0.00443 Deviance explained = 1.23%
## -REML = 318.18 Scale est. = 0.77288 n = 241
Berdasarkan hasil pemodelan, baik Regresi Linear maupun Regresi Spline (GAM) menunjukkan performa yang rendah dengan nilai R² negatif (-0.07) dan RMSE sekitar 0.87, menandakan bahwa kedua model belum mampu menjelaskan variasi nilai DO secara memadai. Nilai MSE yang hampir sama pada kedua model juga memperlihatkan bahwa kemampuan prediksi keduanya relatif identik dan tidak lebih baik dibandingkan rata-rata nilai DO aktual.
Dari sisi pengaruh variabel, hasil regresi linear menunjukkan bahwa tidak ada variabel yang signifikan secara statistik (p-value > 0.05). Namun, di antara prediktor yang ada, BOD memiliki koefisien positif tertinggi (0.09) sehingga dapat diinterpretasikan sebagai variabel yang relatif paling memengaruhi DO, di mana peningkatan BOD cenderung diikuti peningkatan nilai DO meskipun hubungan ini lemah dan tidak signifikan. Hasil model spline (GAM) juga menunjukkan pola serupa, dengan semua variabel memiliki p-value tinggi dan deviance explained hanya sekitar 1,23%, sehingga tidak terdapat hubungan non-linear yang berarti.
Secara keseluruhan, dapat disimpulkan bahwa model prediksi DO belum efektif, kemungkinan karena hubungan antara DO dan variabel pH, BOD, TSS, serta Suhu bersifat kompleks atau dipengaruhi oleh faktor lain yang belum dimasukkan dalam model. Diperlukan eksplorasi lebih lanjut dengan menambah variabel lingkungan lain atau menggunakan pendekatan non-linear yang lebih kuat seperti Random Forest Regression atau XGBoost untuk meningkatkan akurasi prediksi.
# Import Data Testing
testing_data <- read.csv("D:/Kuliah/Semester 5/Statistika Lingkungan/Tugas/UTS Statling_M Zidane Yanfa N_2304220004/kualitasair.xlsx - Testing.csv")
test_original <- testing_data
# Preprocessing Data Testing
## Identifikasi Missing Value pada data training yang sudah dibersihkan
median_values_train <- training4 %>%
select(where(is.numeric)) %>%
summarise(across(everything(), median, na.rm = TRUE))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(everything(), median, na.rm = TRUE)`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
## Imputasi Missing Value (Median) pada Data Testing
testing_data_clean <- testing_data %>%
mutate(
pH = ifelse(is.na(pH), median_values_train$pH, pH),
DO = ifelse(is.na(DO), median_values_train$DO, DO),
BOD = ifelse(is.na(BOD), median_values_train$BOD, BOD),
TSS = ifelse(is.na(TSS), median_values_train$TSS, TSS),
Suhu = ifelse(is.na(Suhu), median_values_train$Suhu, Suhu)
)
## Penanganan Outlier (Winsorizing) pada Data Testing
quantile_05_train <- training4 %>%
select(where(is.numeric)) %>%
summarise(across(everything(), ~quantile(., 0.05)))
quantile_95_train <- training4 %>%
select(where(is.numeric)) %>%
summarise(across(everything(), ~quantile(., 0.95)))
testing_data_clean_model <- testing_data_clean %>%
mutate(
pH = pmin(pmax(pH, quantile_05_train$pH), quantile_95_train$pH),
DO = pmin(pmax(DO, quantile_05_train$DO), quantile_95_train$DO),
BOD = pmin(pmax(BOD, quantile_05_train$BOD), quantile_95_train$BOD),
TSS = pmin(pmax(TSS, quantile_05_train$TSS), quantile_95_train$TSS),
Suhu = pmin(pmax(Suhu, quantile_05_train$Suhu), quantile_95_train$Suhu)
)
# Prediksi
## SVM
prediksi_svm_status <- predict(model_svm, testing_data_clean_model, type = "class")
## Decision Tree
prediksi_tree_status <- predict(model_tree, testing_data_clean_model, type = "class")
## Random Forest
prediksi_rf_status <- predict(model_rf, testing_data_clean_model, type = "class")
# Output hasil
hasil_prediksi_semua <- data.frame(
Lokasi = test_original$Lokasi,
pH = test_original$pH,
DO = test_original$DO,
BOD = test_original$BOD,
TSS = test_original$TSS,
Suhu = test_original$Suhu,
Status_Prediksi_SVM = prediksi_svm_status,
Status_Prediksi_Tree = prediksi_tree_status,
Status_Prediksi_RF = prediksi_rf_status
)
hasil_prediksi_semua
## Lokasi pH DO BOD TSS Suhu Status_Prediksi_SVM
## 1 S301 6.9977 5.0835 3.1813 50.9339 25.5573 tercemar ringan
## 2 S302 7.3801 4.7482 3.3373 46.5210 27.0940 tercemar ringan
## 3 S303 7.0195 6.5949 3.0039 NA 26.5954 Baik
## 4 S304 7.3675 NA 3.4952 39.0091 26.6512 tercemar ringan
## 5 S305 6.9268 6.2444 3.3449 47.1692 23.3940 tercemar ringan
## 6 S306 6.9711 6.0028 3.4466 39.1027 27.7013 tercemar ringan
## 7 S307 7.2412 4.6718 3.3968 45.9433 29.8974 tercemar ringan
## 8 S308 7.4965 7.1797 4.3295 55.2691 30.3127 tercemar ringan
## 9 S309 6.3768 5.4072 2.1560 52.4042 32.1487 tercemar ringan
## 10 S310 6.9833 7.2000 4.2067 58.9836 28.5306 tercemar ringan
## 11 S311 6.9645 5.5250 2.7326 54.8797 25.8960 tercemar ringan
## 12 S312 6.6205 5.4249 NA 51.1199 29.0047 tercemar ringan
## 13 S313 6.4828 5.9688 3.0649 59.9831 30.1997 tercemar ringan
## 14 S314 6.6846 5.6419 2.6414 61.9172 31.7730 tercemar ringan
## 15 S315 7.2934 5.6434 0.9570 36.0061 29.6225 tercemar ringan
## 16 S316 6.7918 5.1223 2.7497 58.9215 30.8365 tercemar ringan
## 17 S317 6.6076 4.7871 3.0331 57.7796 28.8783 tercemar ringan
## 18 S318 7.0817 NA NA 59.4191 25.4450 tercemar ringan
## 19 S319 6.3816 5.1938 NA 40.9120 26.2773 tercemar ringan
## 20 S320 7.5229 4.6235 2.5222 74.0893 31.7740 tercemar ringan
## 21 S321 6.7577 5.4922 4.1582 24.0575 29.4082 tercemar ringan
## 22 S322 7.0946 5.1991 NA 50.0686 26.6646 tercemar ringan
## 23 S323 7.0255 3.8072 NA 51.1811 26.5886 tercemar ringan
## 24 S324 6.9999 5.7091 3.1342 NA 26.1586 tercemar ringan
## 25 S325 7.9047 6.1672 3.2061 57.4787 28.2969 tercemar ringan
## 26 S326 6.5873 NA 3.7296 51.2403 27.5000 tercemar ringan
## 27 S327 7.5727 6.3927 2.8745 42.2195 26.1165 Baik
## 28 S328 7.0158 4.9992 1.3733 64.1003 30.4284 tercemar ringan
## 29 S329 6.5824 5.6743 NA 47.0609 28.0371 tercemar ringan
## 30 S330 6.9656 NA 2.3142 56.1068 29.5157 tercemar ringan
## 31 S331 7.3734 5.3646 3.3410 43.3349 27.2399 tercemar ringan
## 32 S332 6.7872 4.7902 2.7721 39.4012 28.3499 tercemar ringan
## 33 S333 6.6140 4.8835 2.5085 43.9416 30.0670 tercemar ringan
## 34 S334 1.5000 6.6299 2.3241 39.4316 29.3514 Baik
## 35 S335 7.4943 5.7275 3.1230 55.3311 29.8826 tercemar ringan
## 36 S336 6.9633 5.7412 2.3189 34.4791 24.3467 tercemar ringan
## 37 S337 6.3065 7.7296 2.6817 40.2151 28.3544 Baik
## 38 S338 6.3467 5.9416 2.2480 50.2722 27.6164 tercemar ringan
## 39 S339 6.6158 5.4629 1.8894 32.8443 26.6996 tercemar ringan
## 40 S340 6.7364 6.7473 3.7641 44.9511 26.8786 tercemar ringan
## 41 S341 6.9893 5.5127 1.9460 46.9508 31.3515 tercemar ringan
## 42 S342 7.3352 7.3729 3.0584 63.4608 28.8611 tercemar ringan
## 43 S343 6.7827 5.6223 2.5455 47.4246 27.7108 tercemar ringan
## 44 S344 6.4431 5.3838 NA 44.7671 29.8781 tercemar ringan
## 45 S345 7.3036 4.8319 3.2075 43.3678 26.9090 tercemar ringan
## 46 S346 7.1377 6.3286 3.2985 54.4722 28.2777 tercemar ringan
## 47 S347 7.5787 7.4665 4.0117 46.8055 30.8937 tercemar ringan
## 48 S348 6.1588 5.6440 3.2602 44.5622 27.3347 tercemar ringan
## 49 S349 7.0437 6.2615 2.8892 NA 26.4017 Baik
## 50 S350 7.6767 6.3333 5.0820 60.6523 26.9034 tercemar ringan
## 51 S351 7.3621 7.4222 2.4798 65.6783 25.8984 tercemar ringan
## 52 S352 6.5837 6.6639 2.1975 43.5689 28.5053 Baik
## 53 S353 7.3663 4.9263 2.5719 50.2052 28.6557 tercemar ringan
## 54 S354 6.5640 5.3031 2.9117 NA 29.7113 tercemar ringan
## 55 S355 6.7733 5.2539 3.4803 68.5740 31.6035 tercemar ringan
## 56 S356 7.5938 6.1416 3.3327 55.9076 85.0000 tercemar ringan
## 57 S357 6.8549 5.9961 2.9154 32.1937 25.4984 tercemar ringan
## 58 S358 7.4143 6.3679 NA 45.6589 31.9982 tercemar ringan
## 59 S359 6.8544 5.3427 NA 27.0725 28.2786 tercemar ringan
## 60 S360 6.2118 5.6237 3.7330 49.0703 27.2267 tercemar ringan
## 61 S361 6.5756 6.7414 2.4205 57.0396 31.7113 tercemar ringan
## 62 S362 6.4557 5.9004 3.5494 41.0551 28.8577 tercemar ringan
## 63 S363 6.7579 NA 3.3601 37.1749 31.4616 tercemar ringan
## 64 S364 6.8318 6.9712 3.8362 NA 27.9130 tercemar ringan
## 65 S365 6.9233 6.0135 3.1197 40.3537 26.5378 tercemar ringan
## 66 S366 6.8784 5.0835 2.0871 59.0778 24.0681 tercemar ringan
## 67 S367 7.9461 7.7097 3.6420 52.1407 30.2537 tercemar ringan
## 68 S368 6.3070 4.8319 2.4170 58.8102 26.2238 tercemar ringan
## 69 S369 6.7926 NA 3.4218 NA 29.7427 tercemar ringan
## 70 S370 7.1745 NA 4.3388 NA 28.2398 tercemar ringan
## 71 S371 7.8142 6.6511 1.8649 62.3604 28.9449 Baik
## 72 S372 10.5000 5.4672 4.2495 52.9540 30.5333 tercemar ringan
## 73 S373 7.6196 5.7244 4.0825 51.7176 27.0294 tercemar ringan
## 74 S374 6.1777 NA 2.5770 40.4461 27.0769 tercemar ringan
## 75 S375 7.7232 5.5335 2.7990 37.8959 27.5365 tercemar ringan
## Status_Prediksi_Tree Status_Prediksi_RF
## 1 tercemar ringan tercemar ringan
## 2 tercemar ringan tercemar ringan
## 3 Baik Baik
## 4 tercemar ringan tercemar ringan
## 5 tercemar ringan tercemar ringan
## 6 tercemar ringan tercemar ringan
## 7 tercemar ringan tercemar ringan
## 8 tercemar berat tercemar ringan
## 9 tercemar ringan tercemar ringan
## 10 tercemar ringan tercemar ringan
## 11 tercemar ringan tercemar ringan
## 12 tercemar ringan tercemar ringan
## 13 tercemar ringan tercemar ringan
## 14 tercemar ringan tercemar ringan
## 15 tercemar ringan tercemar ringan
## 16 tercemar ringan tercemar ringan
## 17 tercemar ringan tercemar ringan
## 18 tercemar ringan tercemar ringan
## 19 tercemar ringan tercemar ringan
## 20 tercemar ringan tercemar ringan
## 21 tercemar ringan tercemar ringan
## 22 tercemar ringan tercemar ringan
## 23 tercemar ringan tercemar ringan
## 24 tercemar ringan tercemar ringan
## 25 tercemar ringan tercemar ringan
## 26 tercemar ringan tercemar ringan
## 27 Baik Baik
## 28 tercemar ringan tercemar ringan
## 29 tercemar ringan tercemar ringan
## 30 tercemar ringan tercemar ringan
## 31 tercemar ringan tercemar ringan
## 32 tercemar ringan tercemar ringan
## 33 tercemar ringan tercemar ringan
## 34 Baik Baik
## 35 tercemar ringan tercemar ringan
## 36 tercemar ringan tercemar ringan
## 37 Baik Baik
## 38 tercemar ringan tercemar ringan
## 39 tercemar ringan tercemar ringan
## 40 tercemar ringan tercemar ringan
## 41 tercemar ringan tercemar ringan
## 42 Baik Baik
## 43 tercemar ringan tercemar ringan
## 44 tercemar ringan tercemar ringan
## 45 tercemar ringan tercemar ringan
## 46 tercemar ringan tercemar ringan
## 47 tercemar ringan tercemar ringan
## 48 tercemar ringan tercemar ringan
## 49 Baik Baik
## 50 tercemar berat tercemar ringan
## 51 Baik Baik
## 52 Baik Baik
## 53 tercemar ringan tercemar ringan
## 54 tercemar ringan tercemar ringan
## 55 tercemar ringan tercemar ringan
## 56 tercemar ringan tercemar ringan
## 57 tercemar ringan tercemar ringan
## 58 Baik Baik
## 59 tercemar ringan tercemar ringan
## 60 tercemar ringan tercemar ringan
## 61 Baik Baik
## 62 tercemar ringan tercemar ringan
## 63 tercemar ringan tercemar ringan
## 64 tercemar ringan tercemar ringan
## 65 tercemar ringan tercemar ringan
## 66 tercemar ringan tercemar ringan
## 67 tercemar ringan tercemar ringan
## 68 tercemar ringan tercemar ringan
## 69 tercemar ringan tercemar ringan
## 70 tercemar ringan tercemar ringan
## 71 Baik Baik
## 72 tercemar ringan tercemar ringan
## 73 tercemar ringan tercemar ringan
## 74 tercemar ringan tercemar ringan
## 75 tercemar ringan tercemar ringan
# Menyimpan file CSV
write.csv(hasil_prediksi_semua, "Hasil_Semua_Prediksi_Status_Air.csv", row.names = FALSE)
Sebelum prediksi dilakukan, data testing (kualitasair.xlsx - Testing.csv) harus melalui tahap preprocessing yang identik dengan data training, meliputi:
Imputasi nilai hilang (NA) menggunakan median dari data training untuk menjaga konsistensi, dan
Penanganan Outlier melalui Winsorizing, juga berdasarkan kuantil 5% dan 95% dari data training.
Setelah data testing dibersihkan dan distandardisasi, ketiga model digunakan untuk menghasilkan variabel Status_Prediksi (Baik, Sedang, atau Buruk) untuk setiap lokasi pengujian. Proses ini memastikan bahwa prediksi kualitas air pada data baru didasarkan pada parameter dan kriteria yang stabil, konsisten, dan optimal yang dipelajari dari data historis, menghasilkan keluaran akhir berupa tabel lokasi dan status kualitas air yang diprediksi.