Latar Belakang

Kualitas wine merupakan salah satu faktor penting yang menentukan nilai jual dan daya saing produk wine di pasar. Produsen wine berupaya menghasilkan produk dengan kualitas yang konsisten agar dapat memenuhi ekspektasi konsumen serta meningkatkan kepercayaan terhadap merek yang mereka tawarkan. Di sisi lain, konsumen juga membutuhkan informasi yang akurat mengenai kualitas wine untuk membantu dalam proses pengambilan keputusan pembelian.

Penilaian kualitas wine secara tradisional biasanya dilakukan melalui evaluasi sensorik oleh para ahli. Proses ini memerlukan pengalaman, waktu, serta biaya yang tidak sedikit. Oleh karena itu, pendekatan berbasis data dan metode machine learning dapat menjadi alternatif yang lebih efisien untuk memprediksi kualitas wine berdasarkan karakteristik kimia yang dimilikinya.

Dataset Wine Quality menyediakan berbagai informasi mengenai komposisi kimia wine yang dapat digunakan sebagai fitur prediktor untuk menentukan kualitas wine. Dengan memanfaatkan algoritma klasifikasi seperti Decision Tree dan Random Forest, diharapkan dapat dibangun model yang mampu mengklasifikasikan kualitas wine secara lebih cepat dan objektif.

Tujuan

Penelitian ini bertujuan untuk:

  1. Mendeskripsikan karakteristik dataset Wine Quality yang digunakan dalam proses klasifikasi.

  2. Membangun model klasifikasi kualitas wine menggunakan metode Decision Tree dan Random Forest.

  3. Membandingkan performa kedua model berdasarkan metrik evaluasi klasifikasi.

  4. Menentukan model terbaik dalam mengklasifikasikan kualitas wine ke dalam kategori Good dan Bad.

Data dan Variabel

Dataset yang digunakan dalam penelitian ini adalah Red Wine Quality Dataset yang tersedia pada UCI Machine Learning Repository dan juga dapat diakses melalui platform Kaggle. Dataset ini berisi informasi mengenai berbagai karakteristik kimia dari red wine yang digunakan untuk memprediksi tingkat kualitas wine. Dataset tersebut terdiri dari 1599 observasi dengan 12 variabel, dimana 11 variabel merupakan fitur prediktor dan 1 variabel merupakan fitur target.

Berikut merupakan penjelasan dari masing-masing variabel yang terdapat pada dataset:

  1. Fixed Acidity: Konsentrasi asam tetap dalam wine, khususnya asam tartarat, yang diukur dalam gram per liter.

  2. Volatile Acidity: Jumlah asam volatil dalam wine, terutama asam asetat, yang dapat mempengaruhi aroma dan rasa wine.

  3. Citric Acid: Kandungan asam sitrat dalam wine yang berperan dalam memberikan keseimbangan rasa.

  4. Residual Sugar: Jumlah gula yang masih tersisa setelah proses fermentasi selesai.

  5. Chlorides: Konsentrasi garam dalam wine yang dapat mempengaruhi karakteristik rasa.

  6. Free Sulfur Dioxide: Jumlah sulfur dioksida bebas yang berfungsi sebagai antioksidan dan pengawet dalam wine.

  7. Total Sulfur Dioxide: Jumlah keseluruhan sulfur dioksida yang terdiri dari bentuk bebas dan bentuk terikat.

  8. Density: Massa jenis wine yang dipengaruhi oleh kandungan alkohol dan gula.

  9. pH: Ukuran tingkat keasaman wine yang menunjukkan keseimbangan kimia dalam produk.

  10. Sulphates: Konsentrasi garam sulfat yang berperan dalam stabilitas wine.

  11. Alcohol: Persentase kandungan alkohol yang terdapat dalam wine.

  12. Quality: Tingkat kualitas red wine dengan nilai dalam rentang 0 hingga 10, dimana nilai yang lebih tinggi menunjukkan kualitas wine yang lebih baik.

Dalam penelitian ini, variabel quality akan digunakan sebagai dasar dalam proses klasifikasi kualitas wine menggunakan metode Decision Tree dan Random Forest.

Import Data

data_wine <- read_excel("wine quality.xls")
head(data_wine)
## # A tibble: 6 × 12
##   `fixed acidity` `volatile acidity` `citric acid` `residual sugar` chlorides
##             <dbl>              <dbl>         <dbl>            <dbl>     <dbl>
## 1             7.4               0.7           0                 1.9     0.076
## 2             7.8               0.88          0                 2.6     0.098
## 3             7.8               0.76          0.04              2.3     0.092
## 4            11.2               0.28          0.56              1.9     0.075
## 5             7.4               0.7           0                 1.9     0.076
## 6             7.4               0.66          0                 1.8     0.075
## # ℹ 7 more variables: `free sulfur dioxide` <dbl>,
## #   `total sulfur dioxide` <dbl>, density <dbl>, pH <dbl>, sulphates <dbl>,
## #   alcohol <dbl>, quality <dbl>

Exploratory Data Analysis

Ringkasan Data

summary(data_wine)
##  fixed acidity   volatile acidity  citric acid    residual sugar  
##  Min.   : 4.60   Min.   :0.1200   Min.   :0.000   Min.   : 0.900  
##  1st Qu.: 7.10   1st Qu.:0.3900   1st Qu.:0.090   1st Qu.: 1.900  
##  Median : 7.90   Median :0.5200   Median :0.260   Median : 2.200  
##  Mean   : 8.32   Mean   :0.5278   Mean   :0.271   Mean   : 2.539  
##  3rd Qu.: 9.20   3rd Qu.:0.6400   3rd Qu.:0.420   3rd Qu.: 2.600  
##  Max.   :15.90   Max.   :1.5800   Max.   :1.000   Max.   :15.500  
##    chlorides       free sulfur dioxide total sulfur dioxide    density      
##  Min.   :0.01200   Min.   : 1.00       Min.   :  6.00       Min.   :0.9901  
##  1st Qu.:0.07000   1st Qu.: 7.00       1st Qu.: 22.00       1st Qu.:0.9956  
##  Median :0.07900   Median :14.00       Median : 38.00       Median :0.9968  
##  Mean   :0.08747   Mean   :15.87       Mean   : 46.47       Mean   :0.9967  
##  3rd Qu.:0.09000   3rd Qu.:21.00       3rd Qu.: 62.00       3rd Qu.:0.9978  
##  Max.   :0.61100   Max.   :72.00       Max.   :289.00       Max.   :1.0037  
##        pH          sulphates         alcohol         quality     
##  Min.   :2.740   Min.   :0.3300   Min.   : 8.40   Min.   :3.000  
##  1st Qu.:3.210   1st Qu.:0.5500   1st Qu.: 9.50   1st Qu.:5.000  
##  Median :3.310   Median :0.6200   Median :10.20   Median :6.000  
##  Mean   :3.311   Mean   :0.6581   Mean   :10.42   Mean   :5.636  
##  3rd Qu.:3.400   3rd Qu.:0.7300   3rd Qu.:11.10   3rd Qu.:6.000  
##  Max.   :4.010   Max.   :2.0000   Max.   :14.90   Max.   :8.000

Berdasarkan ringkasan data di atas, dataset berisi sejumlah variabel numerik yang merepresentasikan karakteristik kimia wine, serta satu variabel target yaitu quality.

Struktur Data

str(data_wine)
## tibble [1,599 × 12] (S3: tbl_df/tbl/data.frame)
##  $ fixed acidity       : num [1:1599] 7.4 7.8 7.8 11.2 7.4 7.4 7.9 7.3 7.8 7.5 ...
##  $ volatile acidity    : num [1:1599] 0.7 0.88 0.76 0.28 0.7 0.66 0.6 0.65 0.58 0.5 ...
##  $ citric acid         : num [1:1599] 0 0 0.04 0.56 0 0 0.06 0 0.02 0.36 ...
##  $ residual sugar      : num [1:1599] 1.9 2.6 2.3 1.9 1.9 1.8 1.6 1.2 2 6.1 ...
##  $ chlorides           : num [1:1599] 0.076 0.098 0.092 0.075 0.076 0.075 0.069 0.065 0.073 0.071 ...
##  $ free sulfur dioxide : num [1:1599] 11 25 15 17 11 13 15 15 9 17 ...
##  $ total sulfur dioxide: num [1:1599] 34 67 54 60 34 40 59 21 18 102 ...
##  $ density             : num [1:1599] 0.998 0.997 0.997 0.998 0.998 ...
##  $ pH                  : num [1:1599] 3.51 3.2 3.26 3.16 3.51 3.51 3.3 3.39 3.36 3.35 ...
##  $ sulphates           : num [1:1599] 0.56 0.68 0.65 0.58 0.56 0.56 0.46 0.47 0.57 0.8 ...
##  $ alcohol             : num [1:1599] 9.4 9.8 9.8 9.8 9.4 9.4 9.4 10 9.5 10.5 ...
##  $ quality             : num [1:1599] 5 5 5 6 5 5 5 7 7 5 ...

Berdasarkan hasil identifikasi struktur data, seluruh variabel dalam dataset bertipe numerik. Variabel-variabel tersebut terdiri atas sejumlah variabel prediktor serta variabel quality sebagai target. Karena penelitian ini bertujuan untuk melakukan klasifikasi, maka variabel quality selanjutnya ditransformasikan ke dalam bentuk kategorikal, yaitu Good dan Bad, agar sesuai dengan kebutuhan pemodelan klasifikasi.

Checking Missing Value

colSums(is.na(data_wine))
##        fixed acidity     volatile acidity          citric acid 
##                    0                    0                    0 
##       residual sugar            chlorides  free sulfur dioxide 
##                    0                    0                    0 
## total sulfur dioxide              density                   pH 
##                    0                    0                    0 
##            sulphates              alcohol              quality 
##                    0                    0                    0

Berdasarkan hasil pemeriksaan, seluruh variabel pada dataset tidak ditemukan missing value. Dengan demikian, dataset dinilai telah memenuhi syarat untuk digunakan pada tahap pemodelan tanpa memerlukan proses imputasi data.

Checking Outlier

boxplot(data_wine[, 1:11],
        las = 2,
        col = "lightblue",
        main = "Boxplot Variabel Prediktor Wine Quality")

Berdasarkan visualisasi boxplot pada seluruh variabel prediktor, teridentifikasi adanya sejumlah observasi yang berada di luar batas whisker, sehingga dapat dikategorikan sebagai outlier. Keberadaan outlier tampak cukup menonjol pada beberapa variabel, khususnya total sulfur dioxide, free sulfur dioxide, dan residual sugar. Hal ini menunjukkan bahwa terdapat sejumlah sampel wine yang memiliki karakteristik kimia yang relatif lebih ekstrem dibandingkan mayoritas observasi dalam dataset.

Dalam penelitian ini, outlier tersebut tidak dieliminasi dari dataset. Keputusan ini diambil dengan mempertimbangkan karakteristik metode yang digunakan, yaitu Decision Tree dan Random Forest, yang secara umum memiliki tingkat ketahanan (robustness) yang lebih baik terhadap keberadaan nilai ekstrem dibandingkan metode lain yang lebih sensitif terhadap skala maupun jarak antarobservasi.

Korelasi Antar Variabel

cor_matrix <- cor(data_wine[, 1:11])
cor_matrix
##                      fixed acidity volatile acidity citric acid residual sugar
## fixed acidity           1.00000000     -0.256130895  0.67170343    0.114776724
## volatile acidity       -0.25613089      1.000000000 -0.55249568    0.001917882
## citric acid             0.67170343     -0.552495685  1.00000000    0.143577162
## residual sugar          0.11477672      0.001917882  0.14357716    1.000000000
## chlorides               0.09370519      0.061297772  0.20382291    0.055609535
## free sulfur dioxide    -0.15379419     -0.010503827 -0.06097813    0.187048995
## total sulfur dioxide   -0.11318144      0.076470005  0.03553302    0.203027882
## density                 0.66804729      0.022026232  0.36494718    0.355283371
## pH                     -0.68297819      0.234937294 -0.54190414   -0.085652422
## sulphates               0.18300566     -0.260986685  0.31277004    0.005527121
## alcohol                -0.06166827     -0.202288027  0.10990325    0.042075437
##                         chlorides free sulfur dioxide total sulfur dioxide
## fixed acidity         0.093705186        -0.153794193          -0.11318144
## volatile acidity      0.061297772        -0.010503827           0.07647000
## citric acid           0.203822914        -0.060978129           0.03553302
## residual sugar        0.055609535         0.187048995           0.20302788
## chlorides             1.000000000         0.005562147           0.04740047
## free sulfur dioxide   0.005562147         1.000000000           0.66766645
## total sulfur dioxide  0.047400468         0.667666450           1.00000000
## density               0.200632327        -0.021945831           0.07126948
## pH                   -0.265026131         0.070377499          -0.06649456
## sulphates             0.371260481         0.051657572           0.04294684
## alcohol              -0.221140545        -0.069408354          -0.20565394
##                          density          pH    sulphates     alcohol
## fixed acidity         0.66804729 -0.68297819  0.183005664 -0.06166827
## volatile acidity      0.02202623  0.23493729 -0.260986685 -0.20228803
## citric acid           0.36494718 -0.54190414  0.312770044  0.10990325
## residual sugar        0.35528337 -0.08565242  0.005527121  0.04207544
## chlorides             0.20063233 -0.26502613  0.371260481 -0.22114054
## free sulfur dioxide  -0.02194583  0.07037750  0.051657572 -0.06940835
## total sulfur dioxide  0.07126948 -0.06649456  0.042946836 -0.20565394
## density               1.00000000 -0.34169933  0.148506412 -0.49617977
## pH                   -0.34169933  1.00000000 -0.196647602  0.20563251
## sulphates             0.14850641 -0.19664760  1.000000000  0.09359475
## alcohol              -0.49617977  0.20563251  0.093594750  1.00000000
corrplot(cor_matrix, method = "color", tl.cex = 0.7)

Gambar di atas menunjukkan visualisasi korelasi antar variabel prediktor pada dataset Wine Quality. Analisis korelasi dilakukan untuk mengetahui arah dan kekuatan hubungan linear antarvariabel sebelum proses pemodelan dilakukan. Nilai korelasi berkisar antara -1 hingga 1, dimana nilai yang mendekati 1 menunjukkan hubungan positif yang kuat, nilai yang mendekati -1 menunjukkan hubungan negatif yang kuat, sedangkan nilai yang mendekati 0 menunjukkan hubungan linear yang lemah.

Berdasarkan hasil visualisasi, beberapa pasangan variabel menunjukkan hubungan yang relatif lebih kuat dibandingkan variabel lainnya. Sebagai contoh, free sulfur dioxide memiliki hubungan positif yang cukup kuat dengan total sulfur dioxide, sedangkan density menunjukkan hubungan negatif dengan alcohol. Selain itu, fixed acidity juga tampak memiliki hubungan negatif dengan pH.

Secara umum, hasil analisis korelasi memberikan gambaran bahwa terdapat beberapa variabel yang saling berkaitan secara linear. Namun demikian, kondisi ini tidak menjadi permasalahan utama dalam penelitian ini, karena metode yang digunakan, yaitu Decision Tree dan Random Forest, relatif lebih robust terhadap korelasi antarvariabel dibandingkan model berbasis regresi linear.

Membentuk Variabel Klasifikasi

data_wine$class <- ifelse(data_wine$quality >= 7, "Good", "Bad")
data_wine$class <- as.factor(data_wine$class)

table(data_wine$class)
## 
##  Bad Good 
## 1382  217
prop.table(table(data_wine$class))
## 
##       Bad      Good 
## 0.8642902 0.1357098

Variabel target klasifikasi dibentuk dari variabel quality, dimana nilai quality ≥ 7 dikategorikan sebagai Good, sedangkan quality < 7 dikategorikan sebagai Bad.

Visualisasi Distribusi Kelas

ggplot(data_wine, aes(x = class, fill = class)) +
  geom_bar() +
  labs(
    title = "Distribusi Kelas Kualitas Wine",
    x = "Kelas",
    y = "Frekuensi"
  ) +
  theme_minimal()

Berdasarkan visualisasi distribusi kelas, terlihat bahwa jumlah observasi pada kategori Bad jauh lebih besar dibandingkan kategori Good. Kondisi ini menunjukkan bahwa dataset memiliki distribusi kelas yang tidak seimbang (class imbalance). Ketidakseimbangan kelas ini perlu diperhatikan karena dapat memengaruhi performa model klasifikasi, khususnya dalam mendeteksi kelas minoritas, yaitu Good.

Pembagian Data Training dan Testing

set.seed(123)

index <- createDataPartition(data_wine$class, p = 0.8, list = FALSE)

data_wine.train <- data_wine[index, ]
data_wine.test  <- data_wine[-index, ]

dim(data_wine.train)
## [1] 1280   13
dim(data_wine.test)
## [1] 319  13
prop.table(table(data_wine.train$class))
## 
##       Bad      Good 
## 0.8640625 0.1359375
prop.table(table(data_wine.test$class))
## 
##       Bad      Good 
## 0.8652038 0.1347962

Dataset dibagi ke dalam dua subset, yaitu 80% sebagai data training dan 20% sebagai data testing. Proses pembagian dilakukan dengan pendekatan stratified sampling untuk memastikan bahwa proporsi kelas Good dan Bad pada data training maupun data testing tetap representatif terhadap distribusi kelas pada dataset asli.

Penanganan Ketidakseimbangan Kelas (DownSampling)

train_balanced <- downSample(
  x = data_wine.train %>% select(-quality, -class),
  y = data_wine.train$class,
  yname = "model_class"
)

table(train_balanced$model_class)
## 
##  Bad Good 
##  174  174
prop.table(table(train_balanced$model_class))
## 
##  Bad Good 
##  0.5  0.5

Berdasarkan hasil eksplorasi data, distribusi kelas pada variabel target menunjukkan kondisi yang tidak seimbang, dimana jumlah observasi pada kategori Bad lebih besar dibandingkan kategori Good. Untuk mengurangi pengaruh ketidakseimbangan kelas tersebut, pada penelitian ini dilakukan penanganan menggunakan teknik downsampling pada data training. Teknik downsampling dilakukan dengan mengurangi jumlah observasi pada kelas mayoritas hingga sama dengan jumlah observasi pada kelas minoritas, sehingga distribusi kelas menjadi seimbang.

Menyiapkan Data untuk Pemodelan

data_wine.test$model_class <- data_wine.test$class
data_wine.test <- data_wine.test %>% select(-quality, -class)

colnames(train_balanced)
##  [1] "fixed acidity"        "volatile acidity"     "citric acid"         
##  [4] "residual sugar"       "chlorides"            "free sulfur dioxide" 
##  [7] "total sulfur dioxide" "density"              "pH"                  
## [10] "sulphates"            "alcohol"              "model_class"
colnames(data_wine.test)
##  [1] "fixed acidity"        "volatile acidity"     "citric acid"         
##  [4] "residual sugar"       "chlorides"            "free sulfur dioxide" 
##  [7] "total sulfur dioxide" "density"              "pH"                  
## [10] "sulphates"            "alcohol"              "model_class"

Setelah tahap persiapan data dilakukan, variabel model_class ditetapkan sebagai variabel target dalam proses klasifikasi, sedangkan variabel quality dan class dikeluarkan dari dataset pemodelan untuk menghindari terjadinya information leakage yang dapat memengaruhi hasil evaluasi model.

Pemodelan Klasifikasi

Decision Tree

Decision Tree merupakan salah satu metode klasifikasi berbasis struktur pohon keputusan yang digunakan untuk memetakan hubungan antara variabel prediktor dan variabel target. Metode ini bekerja dengan membagi data secara bertahap ke dalam sejumlah cabang berdasarkan variabel prediktor yang memiliki kemampuan terbaik dalam memisahkan kelas target.

Pada setiap tahap pemisahan, algoritma akan memilih variabel dan titik batas yang menghasilkan tingkat kemurnian node terbaik. Dengan demikian, data yang semula berada dalam satu kelompok besar akan dipisahkan menjadi kelompok-kelompok yang semakin homogen. Proses ini terus dilakukan hingga mencapai kondisi tertentu, misalnya jumlah data pada node sudah terlalu sedikit atau kedalaman pohon telah mencapai batas yang ditentukan.

Salah satu keunggulan utama Decision Tree adalah tingkat interpretabilitasnya yang tinggi. Hasil pemodelan dapat divisualisasikan dalam bentuk pohon keputusan, sehingga memudahkan peneliti dalam memahami aturan klasifikasi yang terbentuk. Oleh karena itu, metode ini sering digunakan pada analisis klasifikasi yang tidak hanya menekankan akurasi, tetapi juga kemudahan interpretasi hasil.

Dalam klasifikasi, salah satu ukuran impurity yang umum digunakan untuk menentukan pemisahan node adalah Gini Index. Nilai Gini digunakan untuk mengukur tingkat ketidakmurnian suatu node. Semakin kecil nilai Gini, maka node tersebut semakin homogen.

Rumus Gini Index dapat dituliskan sebagai berikut:

\[ Gini(t) = 1 - \sum_{i=1}^{c} p_i^2 \]

dengan:

  • \(t\) adalah node yang sedang dievaluasi

  • \(c\) adalah jumlah kelas

  • \(p_i\) adalah proporsi observasi pada kelas ke-\(i\) dalam node tersebut

Selain itu, kualitas pemisahan juga dapat dinilai dari penurunan impurity setelah suatu node dibagi menjadi beberapa cabang. Secara umum, pemisahan terbaik adalah pemisahan yang menghasilkan penurunan impurity paling besar.

Rumus penurunan impurity dapat dituliskan sebagai berikut:

\[ \Delta Gini = Gini(t) - \left( \frac{n_{left}}{n_t} Gini(t_{left}) + \frac{n_{right}}{n_t} Gini(t_{right}) \right) \]

dengan:

  • \(n_t\) adalah jumlah observasi pada node induk

  • \(n_{left}\) adalah jumlah observasi pada cabang kiri

  • \(n_{right}\) adalah jumlah observasi pada cabang kanan

  • \(Gini(t_{left})\) adalah nilai Gini pada cabang kiri

  • \(Gini(t_{right})\) adalah nilai Gini pada cabang kanan

Pada penelitian ini, model Decision Tree digunakan untuk mengklasifikasikan kualitas wine ke dalam dua kategori, yaitu Good dan Bad, berdasarkan karakteristik kimia yang dimiliki oleh masing-masing sampel wine.

set.seed(123)

dt_model <- rpart(
  model_class ~ .,
  data = train_balanced,
  method = "class",
  control = rpart.control(cp = 0.01, minsplit = 20, maxdepth = 5)
)

rpart.plot(dt_model, type = 2, extra = 104, fallen.leaves = TRUE)

Gambar di atas menunjukkan struktur model Decision Tree yang digunakan untuk mengklasifikasikan kualitas wine ke dalam kategori Good dan Bad. Variabel yang muncul pada bagian atas pohon, seperti alcohol, volatile acidity, citric acid, residual sugar, dan sulphates, menunjukkan bahwa variabel-variabel tersebut memiliki peran penting dalam proses klasifikasi. Setiap simpul merepresentasikan aturan pemisahan berdasarkan nilai ambang tertentu, sedangkan daun pohon menunjukkan hasil akhir klasifikasi. Dengan demikian, plot ini memberikan gambaran mengenai aturan keputusan yang terbentuk dalam model Decision Tree.

Evaluasi Model Decision Tree

pred_dt <- predict(dt_model, data_wine.test, type = "class")

cm_dt <- confusionMatrix(pred_dt, data_wine.test$model_class, positive = "Good")
cm_dt
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Bad Good
##       Bad  217   11
##       Good  59   32
##                                           
##                Accuracy : 0.7806          
##                  95% CI : (0.7311, 0.8248)
##     No Information Rate : 0.8652          
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.3605          
##                                           
##  Mcnemar's Test P-Value : 1.937e-08       
##                                           
##             Sensitivity : 0.7442          
##             Specificity : 0.7862          
##          Pos Pred Value : 0.3516          
##          Neg Pred Value : 0.9518          
##              Prevalence : 0.1348          
##          Detection Rate : 0.1003          
##    Detection Prevalence : 0.2853          
##       Balanced Accuracy : 0.7652          
##                                           
##        'Positive' Class : Good            
## 
cm_dt_table <- as.data.frame(table(
  Predicted = pred_dt,
  Actual = data_wine.test$model_class
))

ggplot(cm_dt_table, aes(x = Actual, y = Predicted, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), size = 5) +
  scale_fill_gradient(low = "lightblue", high = "steelblue") +
  labs(
    title = "Confusion Matrix Decision Tree",
    x = "Actual Class",
    y = "Predicted Class",
    fill = "Count"
  ) +
  theme_minimal()

dt_results <- data.frame(
  Model = "Decision Tree",
  Accuracy = as.numeric(cm_dt$overall["Accuracy"]),
  Precision = as.numeric(cm_dt$byClass["Pos Pred Value"]),
  Recall = as.numeric(cm_dt$byClass["Sensitivity"]),
  F1_Score = as.numeric(cm_dt$byClass["F1"]),
  Specificity = as.numeric(cm_dt$byClass["Specificity"]),
  Balanced_Accuracy = as.numeric(cm_dt$byClass["Balanced Accuracy"])
)

dt_results
##           Model  Accuracy Precision   Recall  F1_Score Specificity
## 1 Decision Tree 0.7805643 0.3516484 0.744186 0.4776119   0.7862319
##   Balanced_Accuracy
## 1          0.765209

Berdasarkan hasil confusion matrix dan metrik evaluasi, diperoleh beberapa temuan sebagai berikut:

  • Model berhasil mengklasifikasikan 217 observasi yang termasuk kelas Bad secara benar.

  • Model juga berhasil mengklasifikasikan 32 observasi yang termasuk kelas Good secara benar.

  • Terdapat 11 observasi yang sebenarnya termasuk kelas Good, tetapi diprediksi sebagai Bad.

  • Terdapat 59 observasi yang sebenarnya termasuk kelas Bad, tetapi diprediksi sebagai Good.

Berdasarkan metrik evaluasi yang diperoleh:

  • Accuracy sebesar 78,06% menunjukkan bahwa model mampu mengklasifikasikan sekitar 78,06% data uji dengan benar.

  • Precision sebesar 35,16% menunjukkan bahwa dari seluruh observasi yang diprediksi sebagai Good, hanya sekitar 35,16% yang benar-benar termasuk kategori Good.

  • Recall sebesar 74,42% menunjukkan bahwa model mampu mendeteksi sekitar 74,42% dari seluruh wine yang benar-benar berkategori Good.

  • F1-score sebesar 47,76% menunjukkan bahwa keseimbangan antara precision dan recall pada model masih berada pada tingkat sedang.

  • Specificity sebesar 78,62% menunjukkan bahwa model mampu mengenali sekitar 78,62% wine yang termasuk kategori Bad.

  • Balanced Accuracy sebesar 76,52% menunjukkan bahwa performa model dalam mengenali kedua kelas relatif cukup seimbang.

Secara umum, model Decision Tree menunjukkan performa klasifikasi yang cukup baik. Model ini memiliki kemampuan yang relatif baik dalam mendeteksi kelas Good, namun nilai precision yang masih rendah menunjukkan bahwa prediksi terhadap kelas Good belum sepenuhnya akurat. Oleh karena itu, hasil ini masih perlu dibandingkan dengan model Random Forest untuk menentukan model terbaik.

ROC dan AUC Decision Tree

pred_dt_prob <- predict(dt_model, data_wine.test, type = "prob")[, "Good"]

roc_dt <- roc(
  response = data_wine.test$model_class,
  predictor = pred_dt_prob,
  levels = c("Bad", "Good")
)

plot(roc_dt, col = "blue", main = "ROC Curve - Decision Tree")

auc(roc_dt)
## Area under the curve: 0.8182

Berdasarkan hasil evaluasi menggunakan kurva ROC, model Decision Tree menunjukkan kemampuan yang cukup baik dalam membedakan dua kelas, yaitu Good dan Bad. Kurva ROC menggambarkan hubungan antara sensitivity (true positive rate) dan specificity pada berbagai nilai ambang klasifikasi. Semakin dekat posisi kurva ke sudut kiri atas, maka semakin baik kemampuan model dalam melakukan klasifikasi.

Nilai AUC yang diperoleh untuk model Decision Tree adalah sebesar 0,8182. Nilai tersebut menunjukkan bahwa model memiliki kemampuan klasifikasi yang baik, karena nilai AUC berada di atas 0,8. Hal ini berarti bahwa model Decision Tree cukup mampu membedakan observasi yang termasuk kategori Good dan Bad secara keseluruhan.

Dengan demikian, berdasarkan kurva ROC dan nilai AUC yang diperoleh, model Decision Tree dapat dikatakan memiliki performa diskriminatif yang cukup baik dalam klasifikasi kualitas wine. Hasil ini juga menunjukkan bahwa model tidak hanya memiliki kemampuan klasifikasi berdasarkan satu nilai ambang tertentu, tetapi juga mempertahankan performa yang cukup stabil pada berbagai kemungkinan threshold.

Random Forest

Random Forest merupakan metode ensemble learning yang membangun sejumlah pohon keputusan dari sampel bootstrap data training, kemudian menentukan hasil klasifikasi akhir berdasarkan voting mayoritas. Metode ini umumnya lebih stabil dibandingkan pohon keputusan tunggal karena mampu mengurangi variansi model dan meningkatkan kemampuan generalisasi.

Dalam proses pembentukannya, setiap pohon pada Random Forest dibangun dari sampel acak yang diambil dari data training. Selain itu, pada setiap proses pemisahan node, hanya sebagian variabel prediktor yang dipilih secara acak untuk dipertimbangkan. Pendekatan ini bertujuan untuk mengurangi korelasi antar pohon, sehingga hasil klasifikasi menjadi lebih robust dan tidak terlalu bergantung pada satu struktur pohon tertentu.

Secara matematis, prediksi akhir Random Forest dapat dituliskan sebagai berikut:

\[ \hat{Y} = \text{mode}\{h_1(x), h_2(x), \dots, h_B(x)\} \]

dengan:

  • \(\hat{Y}\) adalah hasil prediksi akhir model

  • \(h_1(x), h_2(x), \dots, h_B(x)\) adalah prediksi dari masing-masing pohon keputusan

  • \(B\) adalah jumlah pohon yang dibangun

  • \(\text{mode}\) menyatakan kelas yang paling banyak dipilih oleh seluruh pohon.

Dengan demikian, hasil akhir klasifikasi pada Random Forest ditentukan berdasarkan kelas yang memperoleh suara terbanyak dari seluruh pohon keputusan yang terbentuk.

Pada penelitian ini, model Random Forest digunakan untuk mengklasifikasikan kualitas wine berdasarkan kombinasi berbagai variabel prediktor kimia, dengan harapan mampu menghasilkan performa klasifikasi yang lebih stabil dan akurat dibandingkan model pohon keputusan tunggal.

names(train_balanced) <- make.names(names(train_balanced))
colnames(train_balanced)
##  [1] "fixed.acidity"        "volatile.acidity"     "citric.acid"         
##  [4] "residual.sugar"       "chlorides"            "free.sulfur.dioxide" 
##  [7] "total.sulfur.dioxide" "density"              "pH"                  
## [10] "sulphates"            "alcohol"              "model_class"
names(data_wine.test) <- make.names(names(data_wine.test))
colnames(data_wine.test)
##  [1] "fixed.acidity"        "volatile.acidity"     "citric.acid"         
##  [4] "residual.sugar"       "chlorides"            "free.sulfur.dioxide" 
##  [7] "total.sulfur.dioxide" "density"              "pH"                  
## [10] "sulphates"            "alcohol"              "model_class"
set.seed(123)

rf_model <- randomForest(
  model_class ~ .,
  data = train_balanced,
  ntree = 500,
  mtry = 3,
  importance = TRUE
)

rf_model
## 
## Call:
##  randomForest(formula = model_class ~ ., data = train_balanced,      ntree = 500, mtry = 3, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 15.23%
## Confusion matrix:
##      Bad Good class.error
## Bad  140   34   0.1954023
## Good  19  155   0.1091954

Berdasarkan hasil pemodelan, model Random Forest berhasil dibangun sebagai model klasifikasi dengan 500 pohon keputusan dan 3 variabel yang dipertimbangkan pada setiap pemisahan node. Hasil ini menunjukkan bahwa model menggunakan pendekatan ensemble untuk meningkatkan stabilitas dan mengurangi variansi model.

Nilai OOB (Out-of-Bag) error rate sebesar 15,23% menunjukkan bahwa tingkat kesalahan internal model pada data training hasil downsampling relatif rendah. Dengan demikian, model memiliki akurasi internal sekitar 84,77%.

Confusion matrix pada output model menunjukkan bahwa model mampu mengklasifikasikan 140 observasi kelas Bad dan 155 observasi kelas Good secara benar. Sementara itu, masih terdapat sejumlah observasi yang salah diklasifikasikan pada masing-masing kelas.

Secara umum, hasil ini menunjukkan bahwa model Random Forest memiliki performa internal yang cukup baik pada data training. Namun demikian, penilaian utama terhadap performa model tetap dilakukan menggunakan data testing, agar kemampuan generalisasi model dapat dinilai secara lebih objektif.

Importance Variabel pada Random Forest

importance(rf_model)
##                            Bad     Good MeanDecreaseAccuracy MeanDecreaseGini
## fixed.acidity         4.784695 12.65809            14.083403        11.943679
## volatile.acidity     10.564760 22.80822            24.185854        23.276629
## citric.acid           5.146842 18.64804            18.875278        18.354590
## residual.sugar        2.515797 15.53778            12.976603        10.501521
## chlorides             9.479437 11.04418            14.224620        12.422855
## free.sulfur.dioxide   5.139778 10.07454            10.926871         7.680101
## total.sulfur.dioxide  5.143744 15.28309            14.832485        10.680065
## density               7.448519 13.30066            15.315356        12.883225
## pH                    1.508366 10.44301             9.313289         9.498784
## sulphates             9.709136 24.11308            24.845382        20.338686
## alcohol              23.369174 35.51528            39.258940        35.981265
varImpPlot(rf_model)

Berdasarkan hasil importance(rf_model), variabel alcohol memiliki tingkat kepentingan tertinggi dalam model Random Forest, baik berdasarkan Mean Decrease Accuracy maupun Mean Decrease Gini. Hal ini menunjukkan bahwa kandungan alkohol merupakan variabel yang paling berpengaruh dalam membedakan kualitas wine. Selain itu, variabel sulphates, volatile acidity, dan citric acid juga memiliki kontribusi yang cukup besar dalam proses klasifikasi. Secara umum, hasil ini menunjukkan bahwa model Random Forest memanfaatkan beberapa karakteristik kimia utama sebagai faktor penting dalam menentukan kualitas wine.

Evaluasi Model Random Forest

pred_rf <- predict(rf_model, data_wine.test)

cm_rf <- confusionMatrix(pred_rf, data_wine.test$model_class, positive = "Good")
cm_rf
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Bad Good
##       Bad  221    7
##       Good  55   36
##                                           
##                Accuracy : 0.8056          
##                  95% CI : (0.7579, 0.8476)
##     No Information Rate : 0.8652          
##     P-Value [Acc > NIR] : 0.9988          
##                                           
##                   Kappa : 0.4336          
##                                           
##  Mcnemar's Test P-Value : 2.387e-09       
##                                           
##             Sensitivity : 0.8372          
##             Specificity : 0.8007          
##          Pos Pred Value : 0.3956          
##          Neg Pred Value : 0.9693          
##              Prevalence : 0.1348          
##          Detection Rate : 0.1129          
##    Detection Prevalence : 0.2853          
##       Balanced Accuracy : 0.8190          
##                                           
##        'Positive' Class : Good            
## 
cm_rf_table <- as.data.frame(table(
  Predicted = pred_rf,
  Actual = data_wine.test$model_class
))

ggplot(cm_rf_table, aes(x = Actual, y = Predicted, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), size = 5) +
  scale_fill_gradient(low = "lightblue", high = "steelblue") +
  labs(
    title = "Confusion Matrix Random Forest",
    x = "Actual Class",
    y = "Predicted Class",
    fill = "Count"
  ) +
  theme_minimal()

rf_results <- data.frame(
  Model = "Random Forest",
  Accuracy = as.numeric(cm_rf$overall["Accuracy"]),
  Precision = as.numeric(cm_rf$byClass["Pos Pred Value"]),
  Recall = as.numeric(cm_rf$byClass["Sensitivity"]),
  F1_Score = as.numeric(cm_rf$byClass["F1"]),
  Specificity = as.numeric(cm_rf$byClass["Specificity"]),
  Balanced_Accuracy = as.numeric(cm_rf$byClass["Balanced Accuracy"])
)

rf_results
##           Model  Accuracy Precision    Recall  F1_Score Specificity
## 1 Random Forest 0.8056426 0.3956044 0.8372093 0.5373134   0.8007246
##   Balanced_Accuracy
## 1          0.818967

ROC dan AUC Random Forest

pred_rf_prob <- predict(rf_model, data_wine.test, type = "prob")[, "Good"]

roc_rf <- roc(
  response = data_wine.test$model_class,
  predictor = pred_rf_prob,
  levels = c("Bad", "Good")
)

plot(roc_rf, col = "red", main = "ROC Curve - Random Forest")

auc(roc_rf)
## Area under the curve: 0.9003

Berdasarkan hasil confusion matrix dan metrik evaluasi, diperoleh beberapa temuan sebagai berikut:

  • Model berhasil mengklasifikasikan 221 observasi yang termasuk kelas Bad secara benar.

  • Model juga berhasil mengklasifikasikan 36 observasi yang termasuk kelas Good secara benar.

  • Terdapat 7 observasi yang sebenarnya termasuk kelas Good, tetapi diprediksi sebagai Bad.

  • Terdapat 55 observasi yang sebenarnya termasuk kelas Bad, tetapi diprediksi sebagai Good.

Berdasarkan metrik evaluasi yang diperoleh:

  • Accuracy sebesar 80,56% menunjukkan bahwa model mampu mengklasifikasikan sekitar 80,56% data uji dengan benar.

  • Precision sebesar 39,56% menunjukkan bahwa dari seluruh observasi yang diprediksi sebagai Good, sekitar 39,56% benar-benar termasuk kategori Good.

  • Recall sebesar 83,72% menunjukkan bahwa model mampu mendeteksi sekitar 83,72% dari seluruh wine yang benar-benar berkategori Good.

  • F1-score sebesar 53,73% menunjukkan bahwa keseimbangan antara precision dan recall pada model berada pada tingkat yang cukup baik.

  • Specificity sebesar 80,07% menunjukkan bahwa model mampu mengenali sekitar 80,07% wine yang termasuk kategori Bad.

  • Balanced Accuracy sebesar 81,90% menunjukkan bahwa performa model dalam mengenali kedua kelas relatif cukup seimbang.

Secara umum, model Random Forest menunjukkan performa klasifikasi yang cukup baik. Model ini memiliki kemampuan yang lebih tinggi dalam mendeteksi kelas Good, sebagaimana ditunjukkan oleh nilai recall yang relatif besar. Selain itu, nilai balanced accuracy yang cukup tinggi menunjukkan bahwa model memiliki kemampuan klasifikasi yang seimbang pada kedua kelas. Oleh karena itu, hasil ini dapat dibandingkan dengan model Decision Tree untuk menentukan model yang paling optimal dalam klasifikasi kualitas wine.

Perbandingan Model dan Penentuan Model Terbaik

comparison <- rbind(dt_results, rf_results)
comparison
##           Model  Accuracy Precision    Recall  F1_Score Specificity
## 1 Decision Tree 0.7805643 0.3516484 0.7441860 0.4776119   0.7862319
## 2 Random Forest 0.8056426 0.3956044 0.8372093 0.5373134   0.8007246
##   Balanced_Accuracy
## 1          0.765209
## 2          0.818967
comparison$AUC <- c(as.numeric(auc(roc_dt)), as.numeric(auc(roc_rf)))
comparison
##           Model  Accuracy Precision    Recall  F1_Score Specificity
## 1 Decision Tree 0.7805643 0.3516484 0.7441860 0.4776119   0.7862319
## 2 Random Forest 0.8056426 0.3956044 0.8372093 0.5373134   0.8007246
##   Balanced_Accuracy       AUC
## 1          0.765209 0.8181665
## 2          0.818967 0.9002781
ggplot(comparison, aes(x = Model, y = Accuracy, fill = Model)) +
  geom_col() +
  ylim(0, 1) +
  labs(
    title = "Perbandingan Accuracy Model",
    x = "Model",
    y = "Accuracy"
  ) +
  theme_minimal()

plot(roc_dt, col = "blue", main = "Perbandingan ROC Curve")
plot(roc_rf, col = "red", add = TRUE)
legend("bottomright",
       legend = c("Decision Tree", "Random Forest"),
       col = c("blue", "red"),
       lwd = 2)

comparison[which.max(comparison$Accuracy), ]
##           Model  Accuracy Precision    Recall  F1_Score Specificity
## 2 Random Forest 0.8056426 0.3956044 0.8372093 0.5373134   0.8007246
##   Balanced_Accuracy       AUC
## 2          0.818967 0.9002781
comparison[which.max(comparison$AUC), ]
##           Model  Accuracy Precision    Recall  F1_Score Specificity
## 2 Random Forest 0.8056426 0.3956044 0.8372093 0.5373134   0.8007246
##   Balanced_Accuracy       AUC
## 2          0.818967 0.9002781

Berdasarkan hasil evaluasi, model Decision Tree dan Random Forest sama-sama mampu digunakan untuk mengklasifikasikan kualitas wine ke dalam kategori Good dan Bad. Namun demikian, kedua model menunjukkan performa yang berbeda pada beberapa metrik evaluasi.

Model Decision Tree memiliki nilai accuracy sebesar 78,06%, precision sebesar 35,16%, recall sebesar 74,42%, dan F1-score sebesar 47,76%. Sementara itu, model Random Forest memiliki nilai accuracy sebesar 80,56%, precision sebesar 39,56%, recall sebesar 83,72%, dan F1-score sebesar 53,73%.

Berdasarkan grafik perbandingan accuracy, terlihat bahwa model Random Forest memiliki nilai accuracy yang lebih tinggi dibandingkan model Decision Tree. Selain itu, berdasarkan perbandingan kurva ROC, model Random Forest juga menunjukkan kemampuan diskriminatif yang lebih baik dalam membedakan kelas Good dan Bad, yang ditunjukkan oleh kurva ROC yang lebih baik serta nilai AUC yang lebih tinggi dibandingkan model Decision Tree.

Hasil seleksi model terbaik berdasarkan nilai accuracy dan AUC juga menunjukkan bahwa model Random Forest menempati posisi terbaik. Selain memiliki accuracy yang lebih tinggi, model ini juga menunjukkan nilai recall dan F1-score yang lebih baik, sehingga lebih mampu mendeteksi kelas Good dan memberikan performa klasifikasi yang lebih seimbang.

Dengan demikian, dapat disimpulkan bahwa model Random Forest merupakan model terbaik dalam penelitian ini. Model ini dinilai lebih optimal untuk digunakan dalam klasifikasi kualitas wine karena memiliki performa yang lebih unggul secara keseluruhan dibandingkan model Decision Tree.

Kesimpulan

Berdasarkan hasil analisis klasifikasi pada dataset Wine Quality, metode Decision Tree dan Random Forest berhasil diterapkan untuk mengklasifikasikan kualitas wine ke dalam kategori Good dan Bad. Proses analisis dilakukan melalui tahapan eksplorasi data, pemeriksaan kualitas data, pembentukan variabel target klasifikasi, pembagian data training dan testing, penanganan ketidakseimbangan kelas dengan downsampling, pembangunan model, serta evaluasi performa model.

Hasil evaluasi menunjukkan bahwa kedua model memiliki kemampuan klasifikasi yang cukup baik. Model Decision Tree menghasilkan nilai accuracy sebesar 78,06%, precision sebesar 35,16%, recall sebesar 74,42%, dan F1-score sebesar 47,76%. Sementara itu, model Random Forest menghasilkan nilai accuracy sebesar 80,56%, precision sebesar 39,56%, recall sebesar 83,72%, dan F1-score sebesar 53,73%. Selain itu, berdasarkan perbandingan kurva ROC dan nilai AUC, model Random Forest juga menunjukkan kemampuan diskriminatif yang lebih baik dibandingkan model Decision Tree.

Dengan demikian, dapat disimpulkan bahwa model Random Forest merupakan model terbaik dalam penelitian ini. Model tersebut dinilai lebih optimal untuk digunakan dalam klasifikasi kualitas wine karena memiliki performa yang lebih unggul secara keseluruhan, baik dari segi accuracy, recall, F1-score, maupun kemampuan membedakan kedua kelas.