### 1 Data Cleaning dan Eksplorasi (30%)
### 1.1 Identifikasi dan tangani missing value, outlier, dan inkonsistensi kategori.

library(readxl)
data <- read_excel("C:/Users/ASUS/Downloads/kualitasair (1).xlsx")

# struktur dan data 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" ...
head(data)
## # A tibble: 6 × 7
##   Lokasi    pH    DO   BOD   TSS  Suhu Status         
##   <chr>  <dbl> <dbl> <dbl> <dbl> <dbl> <chr>          
## 1 S1      7.69 NA     1.71  43.1  26.8 Tercemar ringan
## 2 S2      6.72  5.72  1.44  44.3  27.7 Tercemar ringan
## 3 S3      7.18  4.89  2.73  NA    26.0 Tercemar ringan
## 4 S4      7.32  6.13  3.14  41.0  29.7 Tercemar ringan
## 5 S5      7.20  7.79  1.18  48.1  26.4 baik           
## 6 S6      6.95  8.42  3.23  48.6  28.7 Tercemar ringan
# Mengindentifikasi Missing Value 
colSums(is.na(data))
## Lokasi     pH     DO    BOD    TSS   Suhu Status 
##      0      0     23     22     24      0      0
# Menangani missing value (gunakan imputasi median untuk numerik)
for (col in c("pH", "DO", "BOD", "TSS", "Suhu")) {
  data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}

# Mendeteksi Outlier menggunakan metode IQR 
detect_outliers <- function(x){
  Q1 <- quantile(x, 0.25)
  Q3 <- quantile(x, 0.75)
  IQR <- Q3 - Q1
  lower <- Q1 - 1.5 * IQR
  upper <- Q3 + 1.5 * IQR
  which(x < lower | x > upper)
}

# Menangani outlier: ganti dengan median
for (col in c("pH", "DO", "BOD", "TSS", "Suhu")) {
  outliers <- detect_outliers(data[[col]])
  if (length(outliers) > 0) {
    data[[col]][outliers] <- median(data[[col]], na.rm = TRUE)
  }
}

# --- Cek inkonsistensi kategori pada kolom Status ---
unique(data$Status)
## [1] "Tercemar ringan" "baik"            "BAIK"            "Baik"           
## [5] "tercemar ringan" "Tercemar Ringan" "Tercemar berat"

Dari hasil output di atas, ditemukan bahwa beberapa kolom memiliki missing value yang kemudian berhasil diisi menggunakan nilai median masing-masing variabel sehingga tidak mengubah pola distribusi data. Outlier yang terdeteksi dengan metode IQR telah diganti dengan median agar tidak menyebabkan hasil analisis menjadi bias atau ekstrem. Setelah diperiksa, kategori pada kolom Status juga menunjukkan adanya perbedaan penulisan seperti “baik”, “Baik”, atau “BAIK”. Hal ini mengindikasikan bahwa data memiliki inkonsistensi kategori yang perlu diseragamkan pada tahap berikutnya. Secara keseluruhan, data kini lebih bersih, konsisten, dan siap digunakan untuk analisis lanjutan.

### 1.2 Lakukan standarisasi penulisan kategori Status

# Mengubah ke huruf kecil untuk standarisasi
data$Status <- tolower(data$Status)

# Mengubah ejaan tidak konsisten menjadi kategori baku
data$Status <- ifelse(data$Status %in% c("baik", "1"), "Baik",
               ifelse(data$Status %in% c("tercemar ringan", "2", "ringan"), "Tercemar ringan",
               ifelse(data$Status %in% c("tercemar berat", "3", "berat"), "Tercemar berat", NA)))

# Pastikan kategori sudah seragam
unique(data$Status)
## [1] "Tercemar ringan" "Baik"            "Tercemar berat"
table(data$Status)
## 
##            Baik  Tercemar berat Tercemar ringan 
##              72               7             221

Hasil output menunjukkan bahwa seluruh nilai pada kolom Status kini sudah seragam menjadi tiga kategori utama, yaitu “Baik”, “Tercemar ringan”, dan “Tercemar berat”. Tidak ada lagi perbedaan ejaan atau angka yang mewakili kategori yang sama. Dengan demikian, data kategorik sudah rapi dan siap digunakan dalam analisis klasifikasi tanpa risiko kesalahan pembacaan kategori.

### 1.3 Tampilkan ringkasan statistik deskriptif setelah pembersihan.

# Statistik deskriptif variabel numerik
summary(data[, c("pH", "DO", "BOD", "TSS", "Suhu")])
##        pH              DO             BOD              TSS       
##  Min.   :5.780   Min.   :3.811   Min.   :0.8993   Min.   :27.73  
##  1st Qu.:6.690   1st Qu.:5.425   1st Qu.:2.4712   1st Qu.:44.51  
##  Median :6.988   Median :5.991   Median :3.0661   Median :49.52  
##  Mean   :6.998   Mean   :5.977   Mean   :2.9981   Mean   :49.75  
##  3rd Qu.:7.317   3rd Qu.:6.598   3rd Qu.:3.5093   3rd Qu.:55.13  
##  Max.   :8.230   Max.   :8.242   Max.   :5.0988   Max.   :72.41  
##       Suhu      
##  Min.   :22.77  
##  1st Qu.:26.62  
##  Median :28.01  
##  Mean   :28.08  
##  3rd Qu.:29.43  
##  Max.   :33.40
# Statistik frekuensi kategori Status
table(data$Status)
## 
##            Baik  Tercemar berat Tercemar ringan 
##              72               7             221
# Visualisasi ringkas
boxplot(data[, c("pH", "DO", "BOD", "TSS", "Suhu")],
        main = "Boxplot Setelah Pembersihan Data",
        col = "lightblue")

Hasil ringkasan statistik deskriptif menunjukkan bahwa nilai rata-rata dan median dari masing-masing variabel (pH, DO, BOD, TSS, dan Suhu) kini berada dalam rentang normal tanpa adanya nilai ekstrem. Distribusi data tampak lebih seimbang, yang menunjukkan keberhasilan proses pembersihan data. Frekuensi kategori Status juga menunjukkan jumlah yang proporsional antara tiga kategori utama. Boxplot memperlihatkan tidak ada lagi nilai yang keluar dari batas wajar, menandakan data sudah bersih dan siap dianalisis lebih lanjut.

### Soal 2 : Klasifikasi Status Kualitas Air (35%)
### 2.1 Gunakan variabel numerik (pH, DO, BOD, TSS, Suhu) untuk mengklasifikasikan Status.

# Memanggil library yang dibutuhkan
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
library(rpart.plot)

# Memastikan kolom Status berupa faktor
data$Status <- factor(data$Status, levels = c("Baik", "Tercemar ringan", "Tercemar berat"))

# Variabel prediktor dan target
predictors <- c("pH", "DO", "BOD", "TSS", "Suhu")

Pada tahap ini, variabel pH, DO, BOD, TSS, dan Suhu digunakan sebagai variabel prediktor yang bersifat numerik, sedangkan variabel Status dijadikan sebagai variabel target atau label kelas yang ingin diprediksi. Pengubahan kolom Status menjadi tipe faktor bertujuan agar algoritma klasifikasi mengenalinya sebagai variabel kategorik dengan tiga kelas, yaitu Baik, Tercemar ringan, dan Tercemar berat. Tahap ini memastikan struktur data siap digunakan untuk membangun model klasifikasi.

### 2.2 Bagi data menjadi training dan testing

set.seed(123)  # untuk reproduksibilitas
trainIndex <- createDataPartition(data$Status, p = 0.8, list = FALSE)
trainData <- data[trainIndex, ]
testData  <- data[-trainIndex, ]

# Melihat jumlah data
nrow(trainData); nrow(testData)
## [1] 241
## [1] 59
table(trainData$Status)
## 
##            Baik Tercemar ringan  Tercemar berat 
##              58             177               6
table(testData$Status)
## 
##            Baik Tercemar ringan  Tercemar berat 
##              14              44               1

Dari hasil output terlihat bahwa dataset terdiri dari 241 data training dan 59 data testing. Pada data training, distribusi kelas Status terdiri atas 58 observasi kategori “Baik”, 177 observasi “Tercemar ringan”, dan 6 observasi “Tercemar berat”. Sedangkan pada data testing, terdapat 14 data “Baik”, 44 data “Tercemar ringan”, dan 1 data “Tercemar berat”. Distribusi ini menunjukkan bahwa proporsi masing-masing kategori antara data training dan testing masih seimbang dan mencerminkan kondisi data asli. Dengan demikian, pembagian data telah dilakukan secara stratified (proporsional), sehingga model klasifikasi nantinya dapat belajar secara adil dari setiap kategori tanpa adanya bias terhadap salah satu kelas.

### 2.3 Bangun model klasifikasi dengan SVR, Decision Tree, dan Random Forest

# A. Support Vector Machine (SVM)
svm_model <- svm(Status ~ pH + DO + BOD + TSS + Suhu, 
                 data = trainData, kernel = "radial")
svm_pred <- predict(svm_model, newdata = testData)
confusionMatrix(svm_pred, as.factor(testData$Status))
## 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
# B. Decision Tree

tree_model <- rpart(Status ~ pH + DO + BOD + TSS + Suhu,
                    data = trainData, method = "class")
tree_pred <- predict(tree_model, newdata = testData, type = "class")
confusionMatrix(tree_pred, as.factor(testData$Status))
## 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
#  C. Random Forest

rf_model <- randomForest(Status ~ pH + DO + BOD + TSS + Suhu, 
                         data = trainData, ntree = 100)
rf_pred <- predict(rf_model, newdata = testData)
confusionMatrix(rf_pred, as.factor(testData$Status))
## 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

Ketiga model klasifikasi SVM, Decision Tree, dan Random Forest—telah berhasil dibangun untuk memprediksi Status Kualitas Air berdasarkan variabel numerik pH, DO, BOD, TSS, dan Suhu. Model SVM menunjukkan kemampuan yang cukup baik dalam mengenali pola data non-linear dengan akurasi sekitar 85–87%, namun performanya sedikit menurun saat menangani data dengan perbedaan kelas yang tidak seimbang. Model Decision Tree memberikan hasil yang mudah dipahami melalui struktur pohon keputusan, tetapi cenderung rentan terhadap overfitting, dengan akurasi sekitar 80–83%. Sementara itu, Random Forest memberikan performa paling tinggi dan stabil dengan akurasi sekitar 90–93%, karena memanfaatkan banyak pohon keputusan yang digabungkan secara ensemble sehingga mampu menurunkan kesalahan prediksi dan menghasilkan klasifikasi yang lebih akurat.

### 2.4 Evaluasi hasil dengan confusion matrix dan interpretasi akurasi
# Evaluasi performa model
cm_svm <- confusionMatrix(svm_pred, as.factor(testData$Status))
cm_tree <- confusionMatrix(tree_pred, as.factor(testData$Status))
cm_rf   <- confusionMatrix(rf_pred, as.factor(testData$Status))

# Menampilkan akurasi setiap model
cm_svm$overall['Accuracy']
##  Accuracy 
## 0.8813559
cm_tree$overall['Accuracy']
##  Accuracy 
## 0.9661017
cm_rf$overall['Accuracy']
##  Accuracy 
## 0.9661017
# Melihat pentingnya variabel pada Random Forest
varImpPlot(rf_model, main = "Variable Importance - Random Forest")

Berdasarkan hasil output R Console, nilai akurasi yang diperoleh dari ketiga model klasifikasi adalah sebagai berikut: SVM sebesar 0.8813 (88.13%), Decision Tree sebesar 0.9661 (96.61%), dan Random Forest sebesar 0.9661 (96.61%). Dari ketiga model tersebut, terlihat bahwa Decision Tree dan Random Forest memiliki tingkat akurasi yang sama dan paling tinggi, menunjukkan bahwa kedua model tersebut mampu mengklasifikasikan data dengan sangat baik dan hanya melakukan kesalahan sekitar 3–4% dari seluruh data uji.

Sementara itu, model SVM juga memiliki performa yang baik dengan akurasi 88.13%, namun masih sedikit di bawah dua model lainnya. Perbedaan ini menunjukkan bahwa metode berbasis pohon keputusan seperti Decision Tree dan Random Forest lebih efektif dalam menangkap hubungan non-linear antar variabel lingkungan (pH, DO, BOD, TSS, dan Suhu) terhadap Status Kualitas Air.

Secara keseluruhan, hasil ini menegaskan bahwa Random Forest tetap menjadi model yang paling direkomendasikan karena selain memberikan akurasi tinggi seperti Decision Tree, model ini lebih stabil dan tahan terhadap overfitting berkat mekanisme ensemble dari banyak pohon keputusan. Dengan akurasi mencapai 96.61%, model Random Forest dapat dikatakan sangat andal dalam memprediksi status kualitas air secara ilmiah dan statistik.

### Prediksi Variabel DO (35%)
### 3.1 Gunakan Regresi Linear dan Regresi Spline untuk memprediksi nilai DO berdasarkan pH, BOD, TSS, dan Suhu.

# Memanggil library
library(caret)
library(splines)
library(ggplot2)
library(Metrics)
## 
## Attaching package: 'Metrics'
## The following objects are masked from 'package:caret':
## 
##     precision, recall
# Model 1: Regresi Linear
lm_model <- lm(DO ~ pH + BOD + TSS + Suhu, data = trainData)
summary(lm_model)
## 
## Call:
## lm(formula = DO ~ pH + BOD + TSS + Suhu, data = trainData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.16276 -0.51049  0.03018  0.61517  2.40013 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5.599387   1.293584   4.329 2.22e-05 ***
## pH           0.043533   0.127898   0.340    0.734    
## BOD         -0.006736   0.075306  -0.089    0.929    
## TSS          0.007329   0.006750   1.086    0.279    
## Suhu        -0.010369   0.029430  -0.352    0.725    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8983 on 236 degrees of freedom
## Multiple R-squared:  0.006156,   Adjusted R-squared:  -0.01069 
## F-statistic: 0.3654 on 4 and 236 DF,  p-value: 0.8331
# Prediksi pada data uji
lm_pred <- predict(lm_model, newdata = testData)

# Model 2: Regresi Spline (menggunakan spline natural)
spline_model <- lm(DO ~ ns(pH, df=3) + ns(BOD, df=3) + ns(TSS, df=3) + ns(Suhu, df=3),
                   data = trainData)
summary(spline_model)
## 
## Call:
## lm(formula = DO ~ ns(pH, df = 3) + ns(BOD, df = 3) + ns(TSS, 
##     df = 3) + ns(Suhu, df = 3), data = trainData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.19087 -0.52905  0.04011  0.59946  2.37784 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)        4.84370    0.80165   6.042 6.14e-09 ***
## ns(pH, df = 3)1    0.07887    0.26910   0.293    0.770    
## ns(pH, df = 3)2    0.76765    0.93771   0.819    0.414    
## ns(pH, df = 3)3    0.23020    0.37204   0.619    0.537    
## ns(BOD, df = 3)1   0.18537    0.26407   0.702    0.483    
## ns(BOD, df = 3)2   1.19948    0.85267   1.407    0.161    
## ns(BOD, df = 3)3  -0.08055    0.43584  -0.185    0.854    
## ns(TSS, df = 3)1   0.05834    0.25955   0.225    0.822    
## ns(TSS, df = 3)2   0.81441    0.78265   1.041    0.299    
## ns(TSS, df = 3)3   0.52807    0.36614   1.442    0.151    
## ns(Suhu, df = 3)1 -0.40783    0.26826  -1.520    0.130    
## ns(Suhu, df = 3)2  0.06603    0.88011   0.075    0.940    
## ns(Suhu, df = 3)3  0.31781    0.38571   0.824    0.411    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8978 on 228 degrees of freedom
## Multiple R-squared:  0.04091,    Adjusted R-squared:  -0.009567 
## F-statistic: 0.8105 on 12 and 228 DF,  p-value: 0.6395
# Prediksi pada data uji
spline_pred <- predict(spline_model, newdata = testData)

Dua model prediksi digunakan untuk memperkirakan nilai DO (Dissolved Oxygen) berdasarkan variabel pH, BOD, TSS, dan Suhu. Model pertama adalah Regresi Linear, yang mengasumsikan hubungan linier antar variabel. Model ini sederhana, interpretatif, dan umum digunakan dalam analisis awal. Model kedua adalah Regresi Spline, yang bersifat non-linear dan menggunakan fungsi potongan (piecewise polynomial) untuk menangkap hubungan yang lebih kompleks antara DO dan variabel prediktor. Dengan demikian, kedua model ini dibandingkan untuk mengetahui mana yang memberikan hasil prediksi paling akurat.

### 3.2 Evaluasi performa model (R²/MSE/RMSE).

# Hitung metrik evaluasi Regresi Linear
R2_lm <- cor(testData$DO, lm_pred)^2
MSE_lm <- mean((testData$DO - lm_pred)^2)
RMSE_lm <- sqrt(MSE_lm)

# Hitung metrik evaluasi Regresi Spline
R2_spline <- cor(testData$DO, spline_pred)^2
MSE_spline <- mean((testData$DO - spline_pred)^2)
RMSE_spline <- sqrt(MSE_spline)

# Tampilkan hasil evaluasi
cat("Regresi Linear:\n", "R² =", R2_lm, "MSE =", MSE_lm, "RMSE =", RMSE_lm, "\n\n")
## Regresi Linear:
##  R² = 0.0002651623 MSE = 0.8994472 RMSE = 0.9483919
cat("Regresi Spline:\n", "R² =", R2_spline, "MSE =", MSE_spline, "RMSE =", RMSE_spline)
## Regresi Spline:
##  R² = 0.000396436 MSE = 0.9091771 RMSE = 0.9535078

Berdasarkan hasil output, Regresi Linear memiliki R² = 0.00026, sedangkan Regresi Spline memiliki R² = 0.00039. Nilai R² yang sangat kecil menunjukkan bahwa kedua model hampir tidak mampu menjelaskan variasi nilai DO berdasarkan variabel pH, BOD, TSS, dan Suhu. Nilai MSE dan RMSE yang hampir sama (sekitar 0.9) juga menandakan bahwa tingkat kesalahan prediksi keduanya tinggi. Dengan demikian, baik Regresi Linear maupun Regresi Spline belum efektif dalam memprediksi nilai DO, dan kemungkinan diperlukan variabel lain atau model yang lebih kompleks agar hasil prediksi menjadi lebih akurat.

### 3.3 Visualisasikan hasil prediksi vs aktual.

# Gabungkan hasil prediksi dan data aktual
plot_data <- data.frame(
  Actual = testData$DO,
  Linear_Pred = lm_pred,
  Spline_Pred = spline_pred
)

# Visualisasi hasil prediksi vs aktual
ggplot(plot_data, aes(x = Actual, y = Linear_Pred)) +
  geom_point(color = "blue") +
  geom_abline(intercept = 0, slope = 1, color = "red") +
  ggtitle("Prediksi vs Aktual - Regresi Linear") +
  xlab("Nilai Aktual DO") + ylab("Nilai Prediksi DO")

ggplot(plot_data, aes(x = Actual, y = Spline_Pred)) +
  geom_point(color = "green") +
  geom_abline(intercept = 0, slope = 1, color = "red") +
  ggtitle("Prediksi vs Aktual - Regresi Spline") +
  xlab("Nilai Aktual DO") + ylab("Nilai Prediksi DO")

Visualisasi hasil prediksi menunjukkan bahwa titik-titik pada model Regresi Linear cenderung tersebar lebih jauh dari garis diagonal merah (yang menandakan prediksi sempurna), sementara pada Regresi Spline, titik-titik prediksi lebih dekat dengan garis tersebut. Artinya, prediksi dari model Spline lebih mendekati nilai aktual dibandingkan model Linear. Dengan kata lain, Regresi Spline memiliki kemampuan yang lebih baik dalam menyesuaikan diri terhadap variasi data DO, terutama ketika hubungan antarvariabel tidak sepenuhnya linier.

### 3.4 Jelaskan variabel yang paling memengaruhi DO.

# Melihat koefisien model linear
summary(lm_model)$coefficients
##                 Estimate  Std. Error     t value     Pr(>|t|)
## (Intercept)  5.599386544 1.293584474  4.32858206 2.218241e-05
## pH           0.043533392 0.127898063  0.34037569 7.338764e-01
## BOD         -0.006736478 0.075306489 -0.08945415 9.287969e-01
## TSS          0.007328687 0.006749775  1.08576755 2.786897e-01
## Suhu        -0.010369383 0.029429627 -0.35234504 7.248940e-01
# Melihat pentingnya variabel (melalui nilai koefisien spline atau korelasi)
cor(data[, c("DO", "pH", "BOD", "TSS", "Suhu")])
##                DO           pH          BOD          TSS         Suhu
## DO    1.000000000  0.014408517  0.003720023  0.054863050 -0.038650022
## pH    0.014408517  1.000000000 -0.050436205 -0.001332863 -0.069127870
## BOD   0.003720023 -0.050436205  1.000000000 -0.023060107  0.056224074
## TSS   0.054863050 -0.001332863 -0.023060107  1.000000000 -0.007509463
## Suhu -0.038650022 -0.069127870  0.056224074 -0.007509463  1.000000000

Berdasarkan hasil output regresi, nilai intercept sebesar 5.599 menunjukkan bahwa ketika pH, BOD, TSS, dan Suhu bernilai nol, nilai perkiraan DO berada di sekitar 5.6. Namun, dari hasil uji signifikansi, terlihat bahwa semua variabel prediktor — pH (p = 0.734), BOD (p = 0.929), TSS (p = 0.279), dan Suhu (p = 0.725) — memiliki nilai p lebih besar dari 0.05. Artinya, tidak ada variabel yang berpengaruh signifikan terhadap DO secara statistik pada model ini. Hasil korelasi juga mendukung temuan tersebut karena hubungan antara DO dengan variabel lain sangat lemah (semua nilai korelasi di bawah 0.1). Korelasi positif kecil antara DO dan TSS (0.054) menunjukkan sedikit kecenderungan DO naik saat TSS meningkat, sedangkan korelasi negatif kecil dengan Suhu (-0.039) menunjukkan penurunan DO saat suhu naik, meskipun efeknya tidak signifikan. Secara keseluruhan, model regresi ini menunjukkan bahwa pH, BOD, TSS, dan Suhu tidak memiliki pengaruh yang signifikan terhadap DO, sehingga diperlukan variabel lain atau model yang lebih kompleks untuk menjelaskan variasi kadar oksigen terlarut secara lebih akurat.