options(repos = c(CRAN = "https://cloud.r-project.org"))
library(readxl)
## Warning: package 'readxl' was built under R version 4.4.3
library(readxl)
data <- read_excel("C:/Users/Cahya Alam/OneDrive/Semester 5/Statistika Lingkungan/kualitasair.xlsx")
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
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
colSums(is.na(data))
## Lokasi pH DO BOD TSS Suhu Status
## 0 0 23 22 24 0 0
# Imputasi missing value dengan median
num_vars <- c("pH", "DO", "BOD", "TSS", "Suhu")
for (v in num_vars) {
med <- median(data[[v]], na.rm = TRUE)
data[[v]][is.na(data[[v]])] <- med
}
# Cek ulang
colSums(is.na(data))
## Lokasi pH DO BOD TSS Suhu Status
## 0 0 0 0 0 0 0
install.packages(c("readxl", "dplyr", "ggplot2", "caret",
"e1071", "rpart", "rpart.plot", "randomForest",
"splines", "writexl"))
## Warning: package 'readxl' is in use and will not be installed
## Installing packages into 'C:/Users/Cahya Alam/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## Warning: package 'splines' is a base package, and should not be updated
## package 'dplyr' successfully unpacked and MD5 sums checked
## package 'ggplot2' successfully unpacked and MD5 sums checked
## package 'caret' successfully unpacked and MD5 sums checked
## package 'e1071' successfully unpacked and MD5 sums checked
## package 'rpart' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'rpart'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\Cahya
## Alam\AppData\Local\R\win-library\4.4\00LOCK\rpart\libs\x64\rpart.dll to
## C:\Users\Cahya Alam\AppData\Local\R\win-library\4.4\rpart\libs\x64\rpart.dll:
## Permission denied
## Warning: restored 'rpart'
## package 'rpart.plot' successfully unpacked and MD5 sums checked
## package 'randomForest' successfully unpacked and MD5 sums checked
## package 'writexl' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\Cahya Alam\AppData\Local\Temp\RtmpOK90Fu\downloaded_packages
library(readxl)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
##
## 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(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(caret)
## Warning: package 'caret' was built under R version 4.4.3
## Loading required package: lattice
library(e1071)
## Warning: package 'e1071' was built under R version 4.4.3
##
## Attaching package: 'e1071'
## The following object is masked from 'package:ggplot2':
##
## element
library(rpart)
## Warning: package 'rpart' was built under R version 4.4.3
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 4.4.3
library(randomForest)
## Warning: package 'randomForest' was built under R version 4.4.3
## 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(writexl)
## Warning: package 'writexl' was built under R version 4.4.3
data <- read_excel("C:/Users/Cahya Alam/OneDrive/Semester 5/Statistika Lingkungan/kualitasair.xlsx")
num_vars <- c("pH","DO","BOD","TSS","Suhu")
for (v in num_vars) {
med <- median(data[[v]], na.rm = TRUE)
data[[v]][is.na(data[[v]])] <- med
}
set.seed(123)
index <- createDataPartition(data$DO, p = 0.7, list = FALSE)
train_reg <- data[index, ]
test_reg <- data[-index, ]
nrow(train_reg)
## [1] 212
nrow(test_reg)
## [1] 88
SOAL 1 — Data Cleaning & Eksplorasi (30%)
colSums(is.na(data))
## Lokasi pH DO BOD TSS Suhu Status
## 0 0 0 0 0 0 0
Menstandarkan Kategori “Status”
data$Status <- as.character(data$Status)
data$Status <- trimws(data$Status)
data$Status <- case_when(
data$Status %in% c("1", "Baik", "baik", "BAIK") ~ "Baik",
data$Status %in% c("2", "Tercemar ringan", "tercemar ringan") ~ "Tercemar_ringan",
data$Status %in% c("3", "Tercemar berat", "tercemar berat") ~ "Tercemar_berat",
TRUE ~ data$Status
)
data$Status <- factor(data$Status, levels = c("Baik","Tercemar_ringan","Tercemar_berat"))
table(data$Status)
##
## Baik Tercemar_ringan Tercemar_berat
## 72 220 7
Kategori pada kolom Status diseragamkan menjadi tiga kelompok utama.
Deteksi Outlier
par(mfrow = c(2,3))
for(v in num_vars){
boxplot(data[[v]], main = v, col = "skyblue4")
}
par(mfrow = c(1,1))
Boxplot menunjukkan distribusi data dan kemungkinan adanya nilai ekstrem
pada variabel seperti BOD atau TSS.
Statistik Deskriptif
summary(data[num_vars])
## pH DO BOD TSS
## Min. :5.503 Min. :2.982 Min. :0.3026 Min. :24.65
## 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.989 Mean :5.977 Mean :3.0053 Mean :49.68
## 3rd Qu.:7.318 3rd Qu.:6.611 3rd Qu.:3.5323 3rd Qu.:55.62
## Max. :8.351 Max. :9.229 Max. :5.7962 Max. :76.34
## Suhu
## Min. :22.77
## 1st Qu.:26.62
## Median :28.01
## Mean :28.31
## 3rd Qu.:29.46
## Max. :90.00
Ringkasan statistik membantu memahami sebaran nilai tiap variabel setelah proses pembersihan data.
Soal 2 — Klasifikasi Status Kualitas Air (35%)
2.1 Split Data (70% Training, 30% Testing)
set.seed(123)
train_index <- createDataPartition(data$Status, p = 0.7, list = FALSE)
train <- data[train_index, ]
test <- data[-train_index, ]
Dataset dibagi agar model dapat dilatih pada data berbeda dari data yang digunakan untuk evaluasi.
2.2 Model Support Vector Machine (SVM)
svm_model <- svm(Status ~ pH + DO + BOD + TSS + Suhu, data = train, kernel = "radial")
svm_pred <- predict(svm_model, test)
confusionMatrix(svm_pred, test$Status)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar_ringan Tercemar_berat
## Baik 11 0 0
## Tercemar_ringan 10 66 2
## Tercemar_berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.8652
## 95% CI : (0.7763, 0.9283)
## No Information Rate : 0.7416
## P-Value [Acc > NIR] : 0.003614
##
## Kappa : 0.5799
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar_ringan Class: Tercemar_berat
## Sensitivity 0.5238 1.0000 0.00000
## Specificity 1.0000 0.4783 1.00000
## Pos Pred Value 1.0000 0.8462 NaN
## Neg Pred Value 0.8718 1.0000 0.97753
## Prevalence 0.2360 0.7416 0.02247
## Detection Rate 0.1236 0.7416 0.00000
## Detection Prevalence 0.1236 0.8764 0.00000
## Balanced Accuracy 0.7619 0.7391 0.50000
Hasil confusion matrix menunjukkan akurasi model SVM dalam mengklasifikasikan status air.
2.3 Model Decision Tree
tree_model <- rpart(Status ~ pH + DO + BOD + TSS + Suhu, data = train, method = "class")
rpart.plot(tree_model)
tree_pred <- predict(tree_model, test, type = "class")
confusionMatrix(tree_pred, test$Status)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar_ringan Tercemar_berat
## Baik 18 2 0
## Tercemar_ringan 3 64 2
## Tercemar_berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9213
## 95% CI : (0.8446, 0.9678)
## No Information Rate : 0.7416
## P-Value [Acc > NIR] : 1.554e-05
##
## Kappa : 0.7886
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar_ringan Class: Tercemar_berat
## Sensitivity 0.8571 0.9697 0.00000
## Specificity 0.9706 0.7826 1.00000
## Pos Pred Value 0.9000 0.9275 NaN
## Neg Pred Value 0.9565 0.9000 0.97753
## Prevalence 0.2360 0.7416 0.02247
## Detection Rate 0.2022 0.7191 0.00000
## Detection Prevalence 0.2247 0.7753 0.00000
## Balanced Accuracy 0.9139 0.8762 0.50000
Decision Tree mempermudah interpretasi karena menampilkan struktur aturan keputusan yang sederhana.
2.4 Model Random Forest
train <- train %>% filter(complete.cases(.))
test <- test %>% filter(complete.cases(.))
set.seed(123)
rf_model <- randomForest(Status ~ pH + DO + BOD + TSS + Suhu, data = train, ntree = 500, importance = TRUE)
rf_pred <- predict(rf_model, test)
confusionMatrix(rf_pred, test$Status)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Baik Tercemar_ringan Tercemar_berat
## Baik 18 2 0
## Tercemar_ringan 3 64 2
## Tercemar_berat 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9213
## 95% CI : (0.8446, 0.9678)
## No Information Rate : 0.7416
## P-Value [Acc > NIR] : 1.554e-05
##
## Kappa : 0.7886
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Baik Class: Tercemar_ringan Class: Tercemar_berat
## Sensitivity 0.8571 0.9697 0.00000
## Specificity 0.9706 0.7826 1.00000
## Pos Pred Value 0.9000 0.9275 NaN
## Neg Pred Value 0.9565 0.9000 0.97753
## Prevalence 0.2360 0.7416 0.02247
## Detection Rate 0.2022 0.7191 0.00000
## Detection Prevalence 0.2247 0.7753 0.00000
## Balanced Accuracy 0.9139 0.8762 0.50000
varImpPlot(rf_model)
Random Forest umumnya memiliki akurasi tertinggi karena menggabungkan
banyak pohon keputusan dan mengurangi overfitting.
Soal 3 — Prediksi Variabel DO (35%)
3.1 Split Data untuk Regresi
set.seed(123)
index <- createDataPartition(data$DO, p = 0.7, list = FALSE)
train_reg <- data[index, ]
test_reg <- data[-index, ]
Regresi Linear
lm_model <- lm(DO ~ pH + BOD + TSS + Suhu, data = train_reg)
summary(lm_model)
##
## Call:
## lm(formula = DO ~ pH + BOD + TSS + Suhu, data = train_reg)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.01836 -0.52678 0.01004 0.67867 2.98760
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.601603 1.123864 4.984 1.31e-06 ***
## pH 0.030197 0.134200 0.225 0.822
## BOD 0.113544 0.080293 1.414 0.159
## TSS -0.002318 0.007107 -0.326 0.745
## Suhu -0.002632 0.014032 -0.188 0.851
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9672 on 207 degrees of freedom
## Multiple R-squared: 0.01056, Adjusted R-squared: -0.008559
## F-statistic: 0.5524 on 4 and 207 DF, p-value: 0.6975
lm_pred <- predict(lm_model, test_reg)
lm_R2 <- cor(test_reg$DO, lm_pred)^2
lm_MSE <- mean((test_reg$DO - lm_pred)^2)
lm_RMSE <- sqrt(lm_MSE)
data.frame(R2 = lm_R2, MSE = lm_MSE, RMSE = lm_RMSE)
## R2 MSE RMSE
## 1 4.109829e-05 0.9184745 0.9583707
Regresi linear digunakan untuk melihat hubungan langsung antara DO dan variabel prediktor lain.
Regresi Spline
spline_model <- lm(DO ~ bs(pH, df=4) + bs(BOD, df=4) + bs(TSS, df=4) + bs(Suhu, df=4),
data = train_reg)
summary(spline_model)
##
## Call:
## lm(formula = DO ~ bs(pH, df = 4) + bs(BOD, df = 4) + bs(TSS,
## df = 4) + bs(Suhu, df = 4), data = train_reg)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.93317 -0.53864 0.05054 0.57224 2.49048
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.122428 1.178196 5.196 5.1e-07 ***
## bs(pH, df = 4)1 0.365184 1.041616 0.351 0.726272
## bs(pH, df = 4)2 -0.210406 0.724565 -0.290 0.771827
## bs(pH, df = 4)3 0.226620 0.940485 0.241 0.809838
## bs(pH, df = 4)4 0.557867 0.898888 0.621 0.535575
## bs(BOD, df = 4)1 0.998118 1.004977 0.993 0.321856
## bs(BOD, df = 4)2 0.708918 0.710980 0.997 0.319952
## bs(BOD, df = 4)3 0.457214 0.983944 0.465 0.642683
## bs(BOD, df = 4)4 3.218174 0.945943 3.402 0.000811 ***
## bs(TSS, df = 4)1 -0.908867 0.871441 -1.043 0.298264
## bs(TSS, df = 4)2 -0.334627 0.604428 -0.554 0.580469
## bs(TSS, df = 4)3 -0.634775 0.815674 -0.778 0.437381
## bs(TSS, df = 4)4 -1.290065 0.791708 -1.629 0.104828
## bs(Suhu, df = 4)1 -0.277965 0.610200 -0.456 0.649234
## bs(Suhu, df = 4)2 -2.231997 2.714172 -0.822 0.411883
## bs(Suhu, df = 4)3 11.308761 22.296081 0.507 0.612582
## bs(Suhu, df = 4)4 -0.008051 1.048338 -0.008 0.993880
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9556 on 195 degrees of freedom
## Multiple R-squared: 0.09006, Adjusted R-squared: 0.01539
## F-statistic: 1.206 on 16 and 195 DF, p-value: 0.2659
spline_pred <- predict(spline_model, test_reg)
## Warning in bs(BOD, degree = 3L, knots = 3.0661, Boundary.knots = c(0.6572, :
## some 'x' values beyond boundary knots may cause ill-conditioned bases
## Warning in bs(Suhu, degree = 3L, knots = 28.00415, Boundary.knots = c(23.2896,
## : some 'x' values beyond boundary knots may cause ill-conditioned bases
sp_R2 <- cor(test_reg$DO, spline_pred)^2
sp_MSE <- mean((test_reg$DO - spline_pred)^2)
sp_RMSE <- sqrt(sp_MSE)
data.frame(R2 = sp_R2, MSE = sp_MSE, RMSE = sp_RMSE)
## R2 MSE RMSE
## 1 1.223294e-05 0.9537181 0.9765849
Regresi spline memberikan fleksibilitas lebih besar untuk menangkap hubungan non-linear antar variabel.
Visualisasi Prediksi vs Aktual
ggplot(data.frame(Aktual = test_reg$DO, Prediksi = lm_pred),
aes(x = Aktual, y = Prediksi)) +
geom_point(color = "steelblue") +
geom_abline(intercept = 0, slope = 1, color = "darkred", linetype = "dashed") +
labs(title = "Linear Regression: DO Aktual vs Prediksi", x = "Aktual", y = "Prediksi") +
theme_minimal()
ggplot(data.frame(Aktual = test_reg$DO, Prediksi = spline_pred),
aes(x = Aktual, y = Prediksi)) +
geom_point(color = "darkgreen") +
geom_abline(intercept = 0, slope = 1, color = "darkred", linetype = "dashed") +
labs(title = "Spline Regression: DO Aktual vs Prediksi", x = "Aktual", y = "Prediksi") +
theme_minimal()
Titik-titik yang dekat dengan garis diagonal menunjukkan hasil prediksi mendekati nilai aktual.
# Simpan hasil prediksi ke spreadsheet (Excel)
hasil_prediksi <- data.frame(
Aktual_DO = test_reg$DO,
Prediksi_Linear = lm_pred,
Prediksi_Spline = spline_pred
)
write_xlsx(hasil_prediksi, "hasil_prediksi_DO_75baris.xlsx")
hasil_status <- data.frame(
Lokasi = test$Lokasi,
Status_Aktual = test$Status,
Status_Prediksi = rf_pred
)
write_xlsx(hasil_status, "hasil_prediksi_status.xlsx")
View(hasil_prediksi)
library(writexl)
hasil_prediksi_status <- data.frame(Status_Prediksi = rf_pred)
write_xlsx(hasil_prediksi_status, "Status_Prediksi_75baris.xlsx")
View(hasil_prediksi_status)
write_xlsx(hasil_status, "Hasil_Prediksi_Status_Kualitas_Air.xlsx")
library(writexl)
# Tampilkan hasil prediksi status langsung di RStudio
hasil_status <- data.frame(Status_Prediksi = rf_pred)
View(hasil_status) # buka tabel di RStudio
# Print di console juga biar bisa copy dari sana
print(hasil_status)
## Status_Prediksi
## 1 Tercemar_ringan
## 2 Tercemar_ringan
## 3 Baik
## 4 Tercemar_ringan
## 5 Tercemar_ringan
## 6 Tercemar_ringan
## 7 Tercemar_ringan
## 8 Tercemar_ringan
## 9 Baik
## 10 Tercemar_ringan
## 11 Tercemar_ringan
## 12 Tercemar_ringan
## 13 Tercemar_ringan
## 14 Tercemar_ringan
## 15 Tercemar_ringan
## 16 Baik
## 17 Tercemar_ringan
## 18 Baik
## 19 Baik
## 20 Tercemar_ringan
## 21 Tercemar_ringan
## 22 Tercemar_ringan
## 23 Baik
## 24 Tercemar_ringan
## 25 Tercemar_ringan
## 26 Tercemar_ringan
## 27 Tercemar_ringan
## 28 Baik
## 29 Tercemar_ringan
## 30 Baik
## 31 Tercemar_ringan
## 32 Tercemar_ringan
## 33 Baik
## 34 Tercemar_ringan
## 35 Tercemar_ringan
## 36 Tercemar_ringan
## 37 Tercemar_ringan
## 38 Tercemar_ringan
## 39 Tercemar_ringan
## 40 Tercemar_ringan
## 41 Tercemar_ringan
## 42 Tercemar_ringan
## 43 Baik
## 44 Tercemar_ringan
## 45 Tercemar_ringan
## 46 Tercemar_ringan
## 47 Baik
## 48 Tercemar_ringan
## 49 Baik
## 50 Tercemar_ringan
## 51 Baik
## 52 Tercemar_ringan
## 53 Tercemar_ringan
## 54 Baik
## 55 Baik
## 56 Tercemar_ringan
## 57 Tercemar_ringan
## 58 Baik
## 59 Tercemar_ringan
## 60 Tercemar_ringan
## 61 Tercemar_ringan
## 62 Tercemar_ringan
## 63 Tercemar_ringan
## 64 Tercemar_ringan
## 65 Tercemar_ringan
## 66 Baik
## 67 Tercemar_ringan
## 68 Tercemar_ringan
## 69 Tercemar_ringan
## 70 Tercemar_ringan
## 71 Tercemar_ringan
## 72 Baik
## 73 Tercemar_ringan
## 74 Tercemar_ringan
## 75 Tercemar_ringan
## 76 Tercemar_ringan
## 77 Tercemar_ringan
## 78 Baik
## 79 Tercemar_ringan
## 80 Tercemar_ringan
## 81 Tercemar_ringan
## 82 Tercemar_ringan
## 83 Tercemar_ringan
## 84 Tercemar_ringan
## 85 Tercemar_ringan
## 86 Tercemar_ringan
## 87 Baik
## 88 Tercemar_ringan
## 89 Tercemar_ringan
# ===============================
# 🔮 HASIL PREDIKSI STATUS (75 BARIS)
hasil_status <- data.frame(Status_Prediksi = rf_pred)
hasil_status <- head(hasil_status, 75)
View(hasil_status)
cat(paste(hasil_status$Status_Prediksi, collapse = "\n"))
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Baik
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
## Baik
## Tercemar_ringan
## Tercemar_ringan
## Tercemar_ringan
cat("\n\nJumlah baris yang ditampilkan:", nrow(hasil_status), "\n")
##
##
## Jumlah baris yang ditampilkan: 75
```