Nomor 1

# Install & load package (kalau belum terpasang)
if (!require(readxl)) install.packages("readxl"); library(readxl)
## Loading required package: readxl
## Warning: package 'readxl' was built under R version 4.5.1
if (!require(dplyr)) install.packages("dplyr"); library(dplyr)
## Loading required package: 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
if (!require(psych)) install.packages("psych"); library(psych)
## Loading required package: psych
## Warning: package 'psych' was built under R version 4.5.1
# Import file Excel
data <- read_excel("C:/SEMESTER 5/STATISTIKA LINGKUNGAN/kualitasair.xlsx")

Langkah pertama adalah memuat paket yang dibutuhkan dan membaca dataset kualitasair.xlsx.Dataset ini berisi parameter kualitas air seperti pH, DO, BOD, TSS, dan Suhu beserta kategori status air.

# Melihat struktur data dan ringkasan 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

Tahap ini digunakan untuk mengenali tipe data setiap kolom (numerik/kategorik) dan melihat ringkasan statistik awal, seperti nilai minimum, maksimum, dan jumlah data hilang.

# Mengecek jumlah nilai yang hilang di setiap kolom
colSums(is.na(data))
## Lokasi     pH     DO    BOD    TSS   Suhu Status 
##      0      0     23     22     24      0      0

Langkah ini memastikan apakah ada nilai NA yang perlu diimputasi sebelum analisis lebih lanjut.

# Imputasi nilai hilang pada variabel numerik dengan median
num_col <- c("pH", "DO", "BOD", "TSS", "Suhu")
for (col in num_col) {
  data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}

Median dipilih karena lebih stabil terhadap outlier dibandingkan mean, sehingga hasil imputasi tidak bias.

# Deteksi dan tandai outlier menggunakan IQR
for (col in num_col) {
  Q1 <- quantile(data[[col]], 0.25, na.rm = TRUE)
  Q3 <- quantile(data[[col]], 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  batas_bawah <- Q1 - 1.5 * IQR
  batas_atas <- Q3 + 1.5 * IQR
  
  # Ganti outlier dengan NA
  data[[col]][data[[col]] < batas_bawah | data[[col]] > batas_atas] <- NA
}

Metode IQR digunakan untuk mendeteksi nilai ekstrem tanpa harus menghapus data, menjaga keutuhan dataset.

# Mengganti outlier yang jadi NA dengan median baru
for (col in num_col) {
  data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}

Nilai ekstrem yang dianggap tidak wajar diisi ulang dengan median agar tidak memengaruhi analisis statistik berikutnya.

# Standarisasi tulisan kategori status
data$Status <- tolower(data$Status)
data$Status <- gsub("baik|1", "Baik", data$Status)
data$Status <- gsub("tercemar ringan|2", "Tercemar ringan", data$Status)
data$Status <- gsub("tercemar berat|3", "Tercemar berat", data$Status)

# Ubah menjadi faktor
data$Status <- factor(data$Status,
                      levels = c("Baik", "Tercemar ringan", "Tercemar berat"))

Langkah ini menyamakan variasi penulisan agar kategori status air terbaca seragam oleh model klasifikasi.

# Cek hasil akhir setelah pembersihan
summary(data)
##     Lokasi                pH              DO             BOD        
##  Length:300         Min.   :5.780   Min.   :3.811   Min.   :0.8993  
##  Class :character   1st Qu.:6.690   1st Qu.:5.425   1st Qu.:2.4712  
##  Mode  :character   Median :6.992   Median :5.991   Median :3.0661  
##                     Mean   :6.998   Mean   :5.977   Mean   :2.9981  
##                     3rd Qu.:7.317   3rd Qu.:6.598   3rd Qu.:3.5093  
##                     Max.   :8.230   Max.   :8.242   Max.   :5.0988  
##       TSS             Suhu                   Status   
##  Min.   :27.73   Min.   :22.77   Baik           : 72  
##  1st Qu.:44.51   1st Qu.:26.62   Tercemar ringan:221  
##  Median :49.52   Median :28.00   Tercemar berat :  7  
##  Mean   :49.75   Mean   :28.08                        
##  3rd Qu.:55.13   3rd Qu.:29.43                        
##  Max.   :72.41   Max.   :33.40
describe(data[num_col])
##      vars   n  mean   sd median trimmed  mad   min   max range  skew kurtosis
## pH      1 300  7.00 0.47   6.99    7.00 0.47  5.78  8.23  2.45  0.04    -0.29
## DO      2 300  5.98 0.90   5.99    5.99 0.87  3.81  8.24  4.43 -0.13    -0.20
## BOD     3 300  3.00 0.75   3.07    3.00 0.76  0.90  5.10  4.20 -0.12    -0.04
## TSS     4 300 49.75 8.65  49.52   49.79 7.80 27.73 72.41 44.69 -0.01    -0.10
## Suhu    5 300 28.08 2.01  28.00   28.07 2.10 22.77 33.40 10.62  0.06    -0.24
##        se
## pH   0.03
## DO   0.05
## BOD  0.04
## TSS  0.50
## Suhu 0.12

Setelah pembersihan, dataset kini bersih tanpa missing value dan outlier ekstrem.

Hasil Akhir Setelah pembersihan data, seluruh nilai hilang dan outlier berhasil ditangani dengan imputasi median. Nilai pH berkisar 5,78–8,23 (umumnya netral), DO rata-rata 5,98 mg/L menunjukkan oksigen terlarut masih cukup, dan BOD rata-rata 3,0 mg/L menandakan kadar bahan organik masih aman. Sebagian besar sampel berstatus “Tercemar ringan”, sedangkan sisanya “Baik” dan sedikit “Tercemar berat.” Secara umum, kondisi kualitas air tergolong cukup baik.

Nomor 2

# Install dan load library yang dibutuhkan
if (!require(caret)) install.packages("caret"); library(caret)
## Loading required package: caret
## Warning: package 'caret' was built under R version 4.5.1
## Loading required package: ggplot2
## 
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
## 
##     %+%, alpha
## Loading required package: lattice
if (!require(e1071)) install.packages("e1071"); library(e1071)
## Loading required package: e1071
## Warning: package 'e1071' was built under R version 4.5.1
if (!require(rpart)) install.packages("rpart"); library(rpart)
## Loading required package: rpart
if (!require(rpart.plot)) install.packages("rpart.plot"); library(rpart.plot)
## Loading required package: rpart.plot
## Warning: package 'rpart.plot' was built under R version 4.5.1
if (!require(randomForest)) install.packages("randomForest"); library(randomForest)
## Loading required package: randomForest
## Warning: package 'randomForest' was built under R version 4.5.1
## 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
set.seed(123) # agar hasil konsisten

# Bagi data menjadi training (80%) dan testing (20%)
trainIndex <- createDataPartition(data$Status, p = 0.8, list = FALSE)
trainData <- data[trainIndex, ]
testData  <- data[-trainIndex, ]

dim(trainData); dim(testData)
## [1] 241   7
## [1] 59  7

Data dibagi menjadi 80% untuk melatih model dan 20% untuk menguji performanya, agar hasil evaluasi lebih objektif.

# Model SVM dengan kernel radial
svm_model <- svm(Status ~ pH + DO + BOD + TSS + Suhu,
                 data = trainData, kernel = "radial")

# Prediksi dan evaluasi
svm_pred <- predict(svm_model, testData)
svm_conf <- confusionMatrix(svm_pred, testData$Status)
svm_conf
## Confusion Matrix and Statistics
## 
##                  Reference
## Prediction        Baik Tercemar ringan Tercemar berat
##   Baik               9               1              0
##   Tercemar ringan    5              43              1
##   Tercemar berat     0               0              0
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8814          
##                  95% CI : (0.7707, 0.9509)
##     No Information Rate : 0.7458          
##     P-Value [Acc > NIR] : 0.008645        
##                                           
##                   Kappa : 0.6515          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: Baik Class: Tercemar ringan Class: Tercemar berat
## Sensitivity               0.6429                 0.9773               0.00000
## Specificity               0.9778                 0.6000               1.00000
## Pos Pred Value            0.9000                 0.8776                   NaN
## Neg Pred Value            0.8980                 0.9000               0.98305
## Prevalence                0.2373                 0.7458               0.01695
## Detection Rate            0.1525                 0.7288               0.00000
## Detection Prevalence      0.1695                 0.8305               0.00000
## Balanced Accuracy         0.8103                 0.7886               0.50000

SVM digunakan karena mampu menangkap hubungan non-linear antar variabel.

# Bangun model pohon keputusan
tree_model <- rpart(Status ~ pH + DO + BOD + TSS + Suhu,
                    data = trainData, method = "class")

# Visualisasi pohon
rpart.plot(tree_model, type = 2, extra = 104, cex = 0.8, main = "Pohon Keputusan")

# Prediksi dan evaluasi
tree_pred <- predict(tree_model, testData, type = "class")
tree_conf <- confusionMatrix(tree_pred, testData$Status)
tree_conf
## Confusion Matrix and Statistics
## 
##                  Reference
## Prediction        Baik Tercemar ringan Tercemar berat
##   Baik              13               0              1
##   Tercemar ringan    1              44              0
##   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.9096          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: Baik Class: Tercemar ringan Class: Tercemar berat
## Sensitivity               0.9286                 1.0000               0.00000
## Specificity               0.9778                 0.9333               1.00000
## Pos Pred Value            0.9286                 0.9778                   NaN
## Neg Pred Value            0.9778                 1.0000               0.98305
## Prevalence                0.2373                 0.7458               0.01695
## Detection Rate            0.2203                 0.7458               0.00000
## Detection Prevalence      0.2373                 0.7627               0.00000
## Balanced Accuracy         0.9532                 0.9667               0.50000

Decision Tree mudah diinterpretasi karena menghasilkan aturan klasifikasi berupa cabang logis berdasarkan nilai parameter air. Model pohon keputusan ini efektif dan mudah diinterpretasi, dengan hasil menunjukkan bahwa kadar DO dan BOD merupakan penentu utama status kualitas air.

# Bangun model Random Forest
set.seed(123)
rf_model <- randomForest(Status ~ pH + DO + BOD + TSS + Suhu,
                         data = trainData, ntree = 500, mtry = 3, importance = TRUE)

# Prediksi dan evaluasi
rf_pred <- predict(rf_model, testData)
rf_conf <- confusionMatrix(rf_pred, testData$Status)
rf_conf
## Confusion Matrix and Statistics
## 
##                  Reference
## Prediction        Baik Tercemar ringan Tercemar berat
##   Baik              13               0              1
##   Tercemar ringan    1              44              0
##   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.9096          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: Baik Class: Tercemar ringan Class: Tercemar berat
## Sensitivity               0.9286                 1.0000               0.00000
## Specificity               0.9778                 0.9333               1.00000
## Pos Pred Value            0.9286                 0.9778                   NaN
## Neg Pred Value            0.9778                 1.0000               0.98305
## Prevalence                0.2373                 0.7458               0.01695
## Detection Rate            0.2203                 0.7458               0.00000
## Detection Prevalence      0.2373                 0.7627               0.00000
## Balanced Accuracy         0.9532                 0.9667               0.50000
# Cek variabel paling penting
importance(rf_model)
##           Baik Tercemar ringan Tercemar berat MeanDecreaseAccuracy
## pH   -1.870894       -1.947077      0.7192570            -2.638631
## DO   71.781677       69.656547      6.5540615            86.735854
## BOD  74.429637       65.673882      2.7609224            85.104875
## TSS  -1.570659        1.718986      0.6040976             0.552901
## Suhu -2.768718       -2.415967     -2.6730971            -3.901362
##      MeanDecreaseGini
## pH           3.528891
## DO          42.704410
## BOD         38.982770
## TSS          5.710166
## Suhu         5.097049
varImpPlot(rf_model, main = "Pentingnya Variabel pada Random Forest")

Random Forest menggabungkan banyak pohon keputusan untuk meningkatkan akurasi dan mengurangi risiko overfitting. Model klasifikasi menunjukkan akurasi tinggi (96,6%) dengan DO dan BOD sebagai faktor paling berpengaruh. Air dengan DO ≥ 6 dan BOD < 3,1 tergolong Baik, sedangkan DO < 6 menunjukkan Tercemar ringan. Secara keseluruhan, model sangat akurat dan mudah diinterpretasi.

svm_acc  <- svm_conf$overall["Accuracy"]
tree_acc <- tree_conf$overall["Accuracy"]
rf_acc   <- rf_conf$overall["Accuracy"]

accuracy_table <- data.frame(
  Model = c("SVM", "Decision Tree", "Random Forest"),
  Akurasi = c(svm_acc, tree_acc, rf_acc)
)

accuracy_table
##           Model   Akurasi
## 1           SVM 0.8813559
## 2 Decision Tree 0.9661017
## 3 Random Forest 0.9661017

Tabel ini menunjukkan perbandingan tingkat akurasi dari ketiga model klasifikasi yang digunakan.

library(ggplot2)
ggplot(accuracy_table, aes(x = Model, y = Akurasi, fill = Model)) +
  geom_col() +
  geom_text(aes(label = round(Akurasi, 3)), vjust = -0.5) +
  labs(title = "Perbandingan Akurasi Model Klasifikasi",
       x = "Model", y = "Akurasi") +
  theme_minimal()

Grafik ini membantu membandingkan performa model secara visual dan menegaskan model mana yang paling akurat.Grafik menunjukkan bahwa Decision Tree dan Random Forest memiliki akurasi tertinggi sebesar 0,966, sedangkan SVM sedikit lebih rendah yaitu 0,881. Ini menegaskan bahwa model berbasis pohon keputusan lebih stabil dan akurat dalam mengklasifikasikan kualitas air dibandingkan SVM.

Hasil Akhir Model Decision Tree dan Random Forest menunjukkan akurasi tertinggi, masing-masing sekitar 96,6%, sedangkan SVM memperoleh akurasi sekitar 88,1%. Ini berarti model berbasis pohon lebih mampu mengenali pola data kualitas air dibandingkan SVM. Parameter yang paling berpengaruh dalam menentukan status air adalah DO dan BOD, karena keduanya berkaitan langsung dengan tingkat pencemaran organik.

Nomor 3

# Model regresi linier untuk memprediksi DO
model_DO <- lm(DO ~ pH + BOD + TSS + Suhu, data = data)

# Lihat ringkasan model
summary(model_DO)
## 
## Call:
## lm(formula = DO ~ pH + BOD + TSS + Suhu, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.18608 -0.54471  0.00143  0.64989  2.36707 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5.974540   1.177524   5.074 6.91e-07 ***
## pH           0.023785   0.112810   0.211    0.833    
## BOD          0.009364   0.070576   0.133    0.895    
## TSS          0.005725   0.006073   0.943    0.347    
## Suhu        -0.016992   0.026184  -0.649    0.517    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.908 on 295 degrees of freedom
## Multiple R-squared:  0.004674,   Adjusted R-squared:  -0.008821 
## F-statistic: 0.3464 on 4 and 295 DF,  p-value: 0.8465

Model ini digunakan untuk melihat pengaruh variabel pH, BOD, TSS, dan Suhu terhadap kadar DO (oksigen terlarut) dalam air. Output summary() memberikan informasi koefisien, nilai R², dan signifikansi tiap variabel.

# Uji normalitas residual
res <- residuals(model_DO)
shapiro.test(res)
## 
##  Shapiro-Wilk normality test
## 
## data:  res
## W = 0.98999, p-value = 0.03775
# Uji multikolinearitas
if (!require(car)) install.packages("car"); library(car)
## Loading required package: car
## Warning: package 'car' was built under R version 4.5.1
## Loading required package: carData
## Warning: package 'carData' was built under R version 4.5.1
## 
## Attaching package: 'car'
## The following object is masked from 'package:psych':
## 
##     logit
## The following object is masked from 'package:dplyr':
## 
##     recode
vif(model_DO)
##       pH      BOD      TSS     Suhu 
## 1.007026 1.005883 1.000579 1.007686

Uji Shapiro-Wilk digunakan untuk memeriksa apakah residual berdistribusi normal.Nilai VIF (< 10) menunjukkan tidak terjadi multikolinearitas antar variabel bebas.Uji Shapiro–Wilk (p = 0.02676) menunjukkan residual tidak sepenuhnya normal, namun masih dapat diterima karena jumlah data besar. Nilai VIF ≈ 1 menandakan tidak ada multikolinearitas, sehingga model layak digunakan.

par(mfrow = c(2,2))
plot(model_DO)

Plot diagnostik digunakan untuk mengecek pola residual, mendeteksi outlier, dan memverifikasi kesesuaian model linier.Secara keseluruhan, model regresi masih layak digunakan meskipun asumsi normalitas sedikit menyimpang.

data$Prediksi_DO <- predict(model_DO)

library(ggplot2)
ggplot(data, aes(x = DO, y = Prediksi_DO)) +
  geom_point(color = "black", size = 2) +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
  labs(title = "Perbandingan DO Aktual vs Prediksi",
       x = "DO Aktual", y = "DO Prediksi") +
  theme_minimal()

Plot ini membandingkan nilai aktual DO dengan hasil prediksi model. Semakin mendekati garis merah (45°), semakin baik kemampuan model dalam memperkirakan nilai DO.

if (!require(Metrics)) install.packages("Metrics"); library(Metrics)
## Loading required package: Metrics
## Warning: package 'Metrics' was built under R version 4.5.1
## 
## Attaching package: 'Metrics'
## The following objects are masked from 'package:caret':
## 
##     precision, recall
rmse(data$DO, data$Prediksi_DO)
## [1] 0.9004047

Nilai RMSE (Root Mean Square Error) menunjukkan tingkat kesalahan prediksi model. Semakin kecil nilainya, semakin baik model regresi.

Hasil Akhir Model regresi menunjukkan bahwa variabel pH, BOD, TSS, dan Suhu tidak berpengaruh signifikan terhadap DO (p-value > 0,05) dengan nilai R² yang rendah. Artinya, perubahan kadar oksigen terlarut lebih dipengaruhi oleh faktor lain di luar empat variabel tersebut. Meskipun demikian, model masih memberikan gambaran umum hubungan arah pengaruh antarparameter kualitas air.

Prediksi 75 Data Terakhir & Tabel Gabungan

# Ambil 75 baris terakhir dari dataset
test75 <- tail(data, 75)

Langkah ini mengambil sebagian data paling akhir untuk dijadikan sampel prediksi, agar model diuji pada data yang belum dievaluasi sebelumnya.

# Prediksi kadar DO menggunakan model regresi
test75$Pred_DO <- predict(model_DO, test75)

# Prediksi status kualitas air menggunakan model Random Forest dari nomor 2
test75$Pred_Status <- predict(rf_model, test75)

Kolom Pred_DO berisi hasil prediksi kadar oksigen terlarut (DO) dari model regresi linier, sedangkan Pred_Status berisi hasil klasifikasi kualitas air dari model Random Forest.

if (!require(knitr)) install.packages("knitr"); library(knitr)
## Loading required package: knitr
kable(
  test75[, c("pH", "BOD", "TSS", "Suhu", "DO", "Pred_DO", "Status", "Pred_Status")],
  caption = "Tabel Prediksi Kadar DO dan Status Kualitas Air (75 Data Terakhir)"
)
Tabel Prediksi Kadar DO dan Status Kualitas Air (75 Data Terakhir)
pH BOD TSS Suhu DO Pred_DO Status Pred_Status
6.91300 3.4263 45.95360 26.4897 5.7515 5.984035 Tercemar ringan Tercemar ringan
7.25780 3.5757 48.54230 27.0170 6.4223 5.999496 Tercemar ringan Tercemar ringan
6.88280 3.4481 57.19470 28.2945 5.9909 6.017212 Tercemar ringan Tercemar ringan
6.67070 2.9855 45.08910 32.7130 6.8356 5.863449 Baik Baik
7.62510 3.0661 54.75960 26.1587 5.3395 6.053640 Tercemar ringan Tercemar ringan
6.86410 3.6086 55.73150 23.6863 7.5641 6.088195 Tercemar ringan Tercemar ringan
7.47400 2.6870 39.81140 27.7592 4.3770 5.933718 Tercemar ringan Tercemar ringan
6.39920 3.8079 49.38130 26.3215 6.8639 5.997870 Tercemar ringan Tercemar ringan
6.76690 2.4984 44.61690 23.6551 5.4884 6.012383 Tercemar ringan Tercemar ringan
6.86530 1.6923 49.52205 26.2514 5.9909 5.991143 Tercemar ringan Tercemar ringan
6.80450 3.9962 34.91720 27.2528 4.1342 5.910637 Tercemar ringan Tercemar ringan
7.67440 3.1451 65.39170 25.7956 6.2452 6.122594 Tercemar ringan Tercemar ringan
6.98860 3.0661 63.46020 29.4313 8.2235 6.032707 Tercemar berat Baik
7.12210 3.7325 41.57140 29.8153 6.2734 5.910278 Tercemar ringan Tercemar ringan
6.52880 3.8388 43.30130 27.3504 7.1308 5.948949 Tercemar ringan Tercemar ringan
6.63540 3.6111 40.91510 25.0368 6.8387 5.975003 Tercemar ringan Tercemar ringan
7.49900 2.5173 48.63460 29.1883 5.3454 5.958956 Tercemar ringan Tercemar ringan
7.62920 2.7037 44.98960 26.2903 6.9540 5.992172 Baik Baik
7.62440 3.0661 51.12640 26.3402 5.9909 6.029738 Tercemar ringan Tercemar ringan
6.30970 3.8441 31.24780 28.2982 6.2066 5.858673 Tercemar ringan Tercemar ringan
8.02500 3.4664 51.58550 26.0477 7.0011 6.050613 Tercemar ringan Tercemar ringan
7.50840 2.2107 50.15560 27.4106 6.7475 5.995223 Baik Baik
6.98660 4.3477 33.99400 26.4581 5.3734 5.926478 Tercemar ringan Tercemar ringan
7.35180 1.9065 52.75970 28.7846 6.3952 5.980212 Baik Baik
6.51430 2.6534 49.52205 26.3074 5.1078 5.990842 Tercemar ringan Tercemar ringan
6.45190 4.8600 47.69430 26.3125 6.6308 5.999470 Tercemar ringan Tercemar ringan
7.02450 3.4193 49.86780 26.0762 5.5673 6.016057 Tercemar ringan Tercemar ringan
6.40080 3.7766 53.84950 28.0221 6.4521 5.994300 Tercemar ringan Tercemar ringan
7.09500 3.3016 53.46720 30.4299 6.3680 5.963262 Tercemar ringan Tercemar ringan
7.64890 2.2033 45.68390 26.2222 5.7296 5.993087 Tercemar ringan Tercemar ringan
6.48310 2.5220 61.50180 28.4362 6.4655 6.021285 Baik Baik
6.63080 3.1322 49.63470 26.6878 6.5744 5.992278 Tercemar ringan Tercemar ringan
7.02330 3.0661 48.64200 25.8230 5.9909 6.010006 Tercemar ringan Tercemar ringan
6.49120 2.3217 59.44620 30.1823 5.9909 5.978164 Baik Baik
6.80840 3.6389 59.92040 26.8615 7.3927 6.057184 Tercemar ringan Tercemar ringan
7.43640 2.7612 42.99330 31.2416 5.3380 5.892563 Tercemar ringan Tercemar ringan
7.48480 2.7731 40.08940 26.6260 5.2226 5.955628 Tercemar ringan Tercemar ringan
7.19190 3.6956 42.94380 29.3140 6.5135 5.927968 Tercemar ringan Tercemar ringan
6.07420 2.5645 38.24220 30.2214 5.0867 5.848456 Tercemar ringan Tercemar ringan
6.97300 3.5030 32.18800 32.2814 5.5506 5.808956 Tercemar ringan Tercemar ringan
7.53240 1.8621 49.52205 26.3381 6.8029 6.007126 Baik Baik
7.40660 2.0180 48.54890 28.1528 5.4265 5.969187 Tercemar ringan Tercemar ringan
6.90460 1.6607 56.42550 29.8716 4.0719 5.969792 Tercemar ringan Tercemar ringan
6.99235 3.0675 49.98340 26.9967 6.6644 5.997019 Tercemar ringan Tercemar ringan
7.03050 2.8351 54.09160 29.1897 4.3975 5.982008 Tercemar ringan Tercemar ringan
7.28690 4.1535 45.03940 27.1387 4.6454 5.983475 Tercemar ringan Tercemar ringan
7.02290 3.0661 40.39890 25.7333 5.9909 5.964326 Tercemar berat Tercemar berat
7.07870 4.0830 57.78130 23.5257 6.8312 6.112206 Tercemar ringan Tercemar ringan
7.21580 4.5562 57.61120 29.0293 6.2511 6.025408 Tercemar ringan Tercemar ringan
6.80170 2.6072 32.66290 27.3162 6.4623 5.883580 Baik Baik
7.65500 3.3108 58.77290 31.4358 6.8448 5.989952 Tercemar ringan Tercemar ringan
7.23520 2.3241 32.26630 25.7672 5.9909 5.915290 Tercemar ringan Tercemar ringan
6.37870 3.5904 49.54310 27.0230 4.8944 5.984352 Tercemar ringan Tercemar ringan
7.69080 2.1362 46.05130 29.6147 6.5638 5.937914 Baik Baik
7.60220 2.1788 48.71940 27.4780 5.9909 5.987787 Baik Tercemar ringan
7.41200 3.2310 60.96240 28.9761 4.4998 6.037756 Tercemar ringan Tercemar ringan
6.16870 3.0726 37.44780 30.7056 5.3930 5.842685 Tercemar ringan Tercemar ringan
6.71530 3.2101 47.34520 32.3536 5.7078 5.885637 Tercemar ringan Tercemar ringan
7.31780 3.0555 49.52205 28.1028 4.7103 5.983211 Tercemar ringan Tercemar ringan
7.02190 2.5771 49.52205 26.3238 6.6941 6.001922 Baik Baik
7.17400 2.8958 43.73410 25.8308 5.4008 5.983764 Tercemar ringan Tercemar ringan
8.22980 4.2961 49.58950 26.9069 7.2569 6.037227 Tercemar ringan Tercemar ringan
6.59080 3.0661 51.99950 27.1470 6.0535 5.996444 Baik Baik
5.94340 1.9452 49.52205 25.5780 6.7281 5.983026 Baik Baik
7.13680 2.3244 47.33970 29.1956 7.5611 5.940997 Baik Baik
6.65620 2.1185 60.84150 30.8478 6.2656 5.976866 Baik Baik
7.22300 2.2799 48.19250 27.1134 7.0767 5.982894 Baik Baik
6.59380 1.9911 52.51560 25.9193 6.2107 6.010265 Baik Baik
8.10600 0.8993 53.69540 25.4522 4.4883 6.050700 Tercemar ringan Tercemar ringan
6.93810 3.5353 54.07030 29.5403 6.0224 5.980287 Tercemar ringan Tercemar ringan
6.76130 3.5280 54.92510 28.0630 6.7181 6.006010 Tercemar ringan Tercemar ringan
6.91690 2.7995 49.52205 24.1903 6.4895 6.037760 Baik Baik
7.43130 2.4210 42.91330 27.7525 5.8261 5.948085 Tercemar ringan Tercemar ringan
7.04870 2.3503 49.41730 28.0007 4.7823 5.971343 Tercemar ringan Tercemar ringan
6.18720 3.3190 40.36580 26.3124 6.6464 5.936788 Tercemar ringan Tercemar ringan

Tabel ini menampilkan perbandingan antara nilai aktual dan hasil prediksi untuk kadar DO dan status air.Jika nilai prediksi mendekati nilai aktual, berarti model bekerja dengan baik dalam memperkirakan kondisi kualitas air.

library(ggplot2)

ggplot(test75, aes(x = DO, y = Pred_DO)) +
  geom_point(color = "skyblue", size = 2) +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
  labs(
    title = "Perbandingan DO Aktual dan Hasil Prediksi (75 Data Terakhir)",
    x = "DO Aktual",
    y = "DO Prediksi"
  ) +
  theme_minimal()

Sebagian besar titik berada di sekitar garis diagonal, menandakan hasil prediksi cukup mendekati nilai aktual. Hal ini menunjukkan bahwa model regresi masih dapat digunakan untuk estimasi kadar DO, meskipun tidak sepenuhnya presisi.

Prediksi

# Ambil 75 data terakhir dari dataset asli
test75 <- tail(data, 75)
# Prediksi SVM
svm_pred75 <- predict(svm_model, test75)

# Prediksi Decision Tree
tree_pred75 <- predict(tree_model, test75, type = "class")

# Prediksi Random Forest
rf_pred75 <- predict(rf_model, test75)
# Gabungkan hasil prediksi ke masing-masing data frame
svm_result75 <- data.frame(
  pH = test75$pH,
  BOD = test75$BOD,
  TSS = test75$TSS,
  Suhu = test75$Suhu,
  DO = test75$DO,
  Status_Aktual = test75$Status,
  Prediksi_SVM = svm_pred75
)

tree_result75 <- data.frame(
  pH = test75$pH,
  BOD = test75$BOD,
  TSS = test75$TSS,
  Suhu = test75$Suhu,
  DO = test75$DO,
  Status_Aktual = test75$Status,
  Prediksi_Tree = tree_pred75
)

rf_result75 <- data.frame(
  pH = test75$pH,
  BOD = test75$BOD,
  TSS = test75$TSS,
  Suhu = test75$Suhu,
  DO = test75$DO,
  Status_Aktual = test75$Status,
  Prediksi_RF = rf_pred75
)
if (!require(openxlsx)) install.packages("openxlsx"); library(openxlsx)
## Loading required package: openxlsx
## Warning: package 'openxlsx' was built under R version 4.5.1
write.xlsx(svm_result75, "Prediksi75_SVM.xlsx")
write.xlsx(tree_result75, "Prediksi75_DecisionTree.xlsx")
write.xlsx(rf_result75, "Prediksi75_RandomForest.xlsx")

Hasil Akhir Berdasarkan 75 data terakhir, hasil prediksi menunjukkan bahwa nilai DO hasil model regresi cenderung mendekati nilai aktual, dengan rata-rata selisih sekitar 0.9. Sementara hasil klasifikasi model Random Forest mampu mengidentifikasi status air dengan tingkat kecocokan tinggi, di mana sebagian besar prediksi sesuai dengan status sebenarnya. Hal ini menegaskan bahwa kombinasi model regresi linier untuk DO dan Random Forest untuk status kualitas air dapat digunakan sebagai dasar pemantauan kondisi perairan secara cukup akurat.