BAB I PENDAHULUAN

1.1 Latar Belakang Masalah

Machine learning merupakan cabang kecerdasan buatan yang memungkinkan komputer mempelajari pola dari data untuk melakukan prediksi tanpa diprogram secara eksplisit untuk setiap kasus. Dalam statistika, pendekatan ini banyak dipakai untuk klasifikasi yang responsnya berupa kategori dan regresi yang responsnya berupa nilai numerik. Salah satu model yang populer adalah decision tree karena mudah diinterpretasikan, tetapi satu pohon keputusan cenderung tidak stabil: perubahan kecil pada data latih dapat menghasilkan struktur pohon yang jauh berbeda sehingga variance-nya tinggi dan mudah mengalami overfitting.

Untuk mengatasi masalah tersebut, dikembangkan ensemble learning, yaitu pendekatan yang menggabungkan banyak model dasar agar prediksi menjadi lebih akurat dan stabil (Fransiska, 2026). Dua kelompok metode ensemble yang banyak dibahas adalah bagging yang membangun model secara paralel dari sampel bootstrap, dan boosting yang membangun model secara berurutan untuk memperbaiki kesalahan model sebelumnya. Random Forest merupakan pengembangan dari bagging: selain memakai sampel bootstrap, metode ini mengacak prediktor yang dipertimbangkan pada setiap node sehingga pohon-pohonnya tidak terlalu berkorelasi dan penurunan variance menjadi lebih efektif (James dkk., 2021; Biau & Scornet, 2016).

Meskipun efektif, Random Forest masih dapat mengalami underfitting karena setiap pohon hanya dibentuk dari satu sampel bootstrap yang memuat sekitar 63,2% observasi unik. Han, Kim, dan Lee (2020) memperkenalkan Double Random Forest (DRF) yang membangun setiap pohon dari seluruh data latih, sedangkan bootstrap digunakan pada setiap node untuk mencari aturan pemisahan, sehingga pohon yang dihasilkan cenderung lebih besar dan beragam. Pada kedua metode ini, performa model perlu dievaluasi dengan ukuran yang sesuai, misalnya accuracy, precision, recall, specificity, dan F1-score pada klasifikasi, serta Out-of-Bag (OOB) error sebagai evaluasi internal tanpa harus menyisihkan data khusus.

1.2 Rumusan Masalah

Berdasarkan latar belakang di atas, rumusan masalah yang dapat disimpulkan yaitu:

  1. Bagaimana memahami Random Forest dan Double Random Forest sebagai metode ensemble berbasis decision tree?
  2. Bagaimana mengimplementasikan Random Forest untuk klasifikasi dan regresi, serta Double Random Forest untuk klasifikasi dan regresi?
  3. Bagaimana mengevaluasi performa model, memahami Out-of-Bag (OOB) error, dan membandingkan Random Forest dengan Double Random Forest?

1.3 Tujuan

Berdasarkan rumusan masalah di atas, tujuan yang dapat disimpulkan yaitu:

  1. Praktikan dapat memahami Random Forest dan Double Random Forest sebagai metode ensemble berbasis decision tree.
  2. Praktikan dapat mengimplementasikan Random Forest untuk klasifikasi dan regresi, serta Double Random Forest untuk klasifikasi dan regresi.
  3. Praktikan dapat mengevaluasi performa model, memahami Out-of-Bag (OOB) error, dan membandingkan Random Forest dengan Double Random Forest.

1.4 Manfaat Penelitian

Adapun manfaat dari praktikum ini yaitu:

  1. Bagi penulis, yaitu dapat memberikan informasi dan pengetahuan serta menerapkan ilmu pengetahuan mengenai metode Random Forest dengan R.
  2. Bagi pembaca, yaitu dapat menambah ilmu pengetahuan dan pemahaman mengenai Random Forest, Double Random Forest, dan evaluasi performanya dengan R.

1.5 Batasan Masalah

Adapun batasan masalah dari praktikum ini, yaitu lakukan analisis Random Forest secara lengkap mulai dari persiapan data, pembentukan model, prediksi, evaluasi, visualisasi, dan interpretasi dengan set.seed(234) agar hasil dapat direproduksi. Lakukan Random Forest classification pada dataset BreastCancer, kemudian bandingkan mtry = 2, 3, dan 5 dengan num.trees = 200, 500, dan 1000. Bandingkan performa pada data testing berdasarkan confusion matrix, accuracy, precision, recall, specificity, F1-score, dan variable importance, serta tampilkan pula OOB error sebagai evaluasi internal model. Selanjutnya, lakukan Double Random Forest classification dengan sintaks dari modul pada data dan pembagian data yang sama, lalu bandingkan hasilnya dengan Random Forest.

BAB II TINJAUAN PUSTAKA

2.1 Random Forest

Random Forest merupakan metode ensemble learning berbasis pohon keputusan (decision tree) yang menggabungkan prediksi dari sejumlah pohon untuk menghasilkan prediksi akhir. Metode ini mengombinasikan bootstrap sampling dan pemilihan prediktor secara acak pada setiap node untuk membangun pohon-pohon yang beragam. Penggabungan prediksi tersebut bertujuan mengurangi variansi dan meningkatkan kemampuan generalisasi model (Fransiska, 2026; Wright & Ziegler, 2017).

Menurut Fransiska (2026), proses pembentukan Random Forest melibatkan empat tahapan utama. Pertama, bootstrap sampling, yaitu pengambilan sampel secara acak dengan pengembalian dari data latih. Kedua, pembangunan sejumlah pohon keputusan berdasarkan sampel yang diperoleh. Ketiga, random feature selection, yaitu pemilihan sebagian prediktor secara acak sebagai kandidat pemisahan pada setiap node. Keempat, agregasi prediksi seluruh pohon untuk memperoleh hasil akhir.

Pada setiap pohon, pemilihan prediktor secara acak membantu mengurangi kemiripan antarpohon. Dengan demikian, penggabungan prediksi diharapkan menghasilkan model yang lebih stabil dibandingkan penggunaan satu pohon keputusan saja (James dkk., 2021; Wright & Ziegler, 2017).

Misalkan tersedia data latih

\[ D=\{(x_i,y_i)\}_{i=1}^{n}, \]

dengan \(x_i\) merupakan vektor prediktor, \(y_i\) merupakan variabel respons, dan \(n\) adalah jumlah observasi. Jika dibangun sebanyak \(B\) pohon keputusan, setiap pohon dinyatakan sebagai \(T_b(x)\) untuk \(b=1,2,\ldots,B\).

Berdasarkan jenis variabel respons, Random Forest dapat digunakan untuk dua tujuan utama, yaitu klasifikasi dan regresi.

2.1.1 Random Forest Klasifikasi

Random Forest klasifikasi digunakan ketika variabel respons berbentuk kategorik, misalnya status kelulusan mahasiswa, kategori tingkat kesejahteraan, atau status penyakit. Setiap pohon keputusan menghasilkan prediksi kelas berdasarkan karakteristik prediktor yang dimiliki suatu observasi. Prediksi akhir diperoleh melalui majority voting, yaitu pemilihan kelas yang memperoleh suara terbanyak dari seluruh pohon (Fransiska, 2026).

Secara matematis, prediksi kelas pada Random Forest klasifikasi dirumuskan sebagai berikut.

\[ \hat{G}_{RF}(x) = \operatorname{mode} \{T_1(x),T_2(x),\ldots,T_B(x)\}. \]

Keterangan:

  • \(\hat{G}_{RF}(x)\) adalah kelas hasil prediksi Random Forest.
  • \(T_b(x)\) adalah kelas hasil prediksi pohon ke-\(b\).
  • \(B\) adalah jumlah pohon keputusan yang dibangun.
  • \(\operatorname{mode}\) menunjukkan kelas yang paling sering diprediksi.

Sebagai contoh, apabila 100 pohon menghasilkan 65 prediksi kelas Lulus dan 35 prediksi kelas Tidak Lulus, prediksi akhir observasi tersebut adalah Lulus.

Kinerja model klasifikasi dapat dievaluasi menggunakan confusion matrix dan beberapa ukuran evaluasi, seperti accuracy, precision, recall, specificity, dan F1-score. Penggunaan ukuran evaluasi yang sesuai diperlukan agar kinerja model dapat dinilai secara lebih menyeluruh, terutama ketika distribusi kelas tidak seimbang.

2.1.2 Random Forest Regresi

Random Forest regresi digunakan ketika variabel respons berbentuk numerik kontinu, misalnya jumlah pengeluaran, harga rumah, atau nilai produksi. Berbeda dari klasifikasi yang menentukan kelas melalui majority voting, regresi menggabungkan prediksi numerik dari seluruh pohon menggunakan rata-rata (Fransiska, 2026; Wright & Ziegler, 2017).

Secara matematis, prediksi Random Forest regresi dirumuskan sebagai berikut.

\[ \hat{f}_{RF}(x) = \frac{1}{B} \sum_{b=1}^{B}T_b(x). \]

Keterangan:

  • \(\hat{f}_{RF}(x)\) adalah nilai prediksi variabel respons.
  • \(T_b(x)\) adalah nilai prediksi dari pohon ke-\(b\).
  • \(B\) adalah jumlah pohon keputusan yang dibangun.

Sebagai contoh, apabila tiga pohon menghasilkan prediksi sebesar 10, 12, dan 14, prediksi akhir Random Forest regresi adalah

\[ \hat{f}_{RF}(x) = \frac{10+12+14}{3} = 12. \]

Evaluasi model regresi dapat dilakukan menggunakan Mean Squared Error (MSE), Root Mean Squared Error (RMSE), Mean Absolute Error (MAE), dan koefisien determinasi (\(R^2\)). Nilai MSE, RMSE, dan MAE yang lebih kecil menunjukkan kesalahan prediksi yang lebih rendah, sedangkan nilai \(R^2\) yang lebih besar umumnya menunjukkan kemampuan model yang lebih baik dalam menjelaskan variasi respons pada data evaluasi (Fransiska, 2026).

2.1.3 Proses Pembentukan Random Forest

Secara umum, tahapan pembentukan Random Forest dapat dijelaskan sebagai berikut (Fransiska, 2026; Wright & Ziegler, 2017).

  1. Menentukan data latih yang terdiri atas prediktor dan variabel respons.
  2. Menentukan jumlah pohon keputusan yang akan dibangun.
  3. Mengambil sampel bootstrap dari data latih untuk setiap pohon.
  4. Memilih sejumlah prediktor secara acak pada setiap node sebagai kandidat pemisahan.
  5. Menentukan pemisahan terbaik berdasarkan kriteria yang sesuai dengan tujuan analisis.
  6. Mengulangi proses tersebut hingga diperoleh seluruh pohon keputusan.
  7. Menggabungkan hasil prediksi seluruh pohon menggunakan majority voting untuk klasifikasi atau rata-rata untuk regresi.

Perbedaan mendasar antara Random Forest klasifikasi dan regresi terletak pada jenis respons, kriteria pemisahan, dan mekanisme agregasi prediksi.

Aspek Random Forest Klasifikasi Random Forest Regresi
Variabel respons Kategorik Numerik
Tujuan Memprediksi kelas Memprediksi nilai numerik
Kriteria pemisahan Misalnya indeks Gini Misalnya penurunan variansi
Agregasi prediksi Majority voting Rata-rata prediksi
Evaluasi Accuracy, precision, recall, F1-score MSE, RMSE, MAE, \(R^2\)

2.2 Out-of-Bag Error

Out-of-Bag (OOB) merupakan mekanisme evaluasi internal pada Random Forest yang memanfaatkan observasi yang tidak terpilih dalam sampel bootstrap untuk membangun pohon tertentu. Mekanisme ini memungkinkan evaluasi prediksi tanpa harus menggunakan observasi yang sama untuk membentuk dan mengevaluasi pohon tersebut (Fransiska, 2026; Wright & Ziegler, 2017).

Pada pengambilan sampel bootstrap berukuran \(n\) dari \(n\) observasi dengan pengembalian, peluang suatu observasi tidak terpilih dalam satu pengambilan adalah \(1-\frac{1}{n}\). Oleh karena itu, peluang observasi tersebut tidak terpilih dalam seluruh pengambilan adalah

\[ P(\text{OOB}) = \left(1-\frac{1}{n}\right)^n. \]

Ketika \(n\) semakin besar, peluang tersebut mendekati

\[ \lim_{n\to\infty} \left(1-\frac{1}{n}\right)^n = e^{-1} \approx 0{,}368. \]

Dengan demikian, sekitar 36,8% observasi tidak masuk ke dalam sampel bootstrap suatu pohon, sedangkan sekitar 63,2% observasi unik masuk ke dalam sampel tersebut secara rata-rata.

Prediksi OOB untuk setiap observasi dihitung menggunakan pohon-pohon yang tidak menggunakan observasi tersebut dalam proses pembentukannya. Selanjutnya, prediksi OOB digunakan untuk menghitung kesalahan prediksi model.

Pada klasifikasi, kesalahan OOB dapat dinyatakan sebagai proporsi observasi yang salah diklasifikasikan. Pada regresi, kesalahan OOB dapat dihitung menggunakan MSE antara respons aktual dan prediksi OOB.

Penggunaan OOB dapat membantu mengevaluasi model secara internal. Namun, OOB tidak sepenuhnya menggantikan kebutuhan evaluasi pada data uji terpisah, khususnya ketika penelitian memerlukan penilaian kemampuan generalisasi pada data yang belum pernah digunakan selama pengembangan model (Wright & Ziegler, 2017).

2.3 Double Random Forest

Double Random Forest (DRF) merupakan pengembangan metode ensemble yang diperkenalkan oleh Han, Kim, dan Lee (2020). Metode ini dirancang untuk mengatasi kondisi ketika Random Forest belum menghasilkan pohon yang cukup besar untuk memperoleh kinerja prediksi yang optimal. Gagasan utamanya adalah menerapkan bootstrap sampling pada setiap node selama pembentukan pohon, bukan hanya sekali pada akar pohon sebagaimana mekanisme Random Forest standar (Han dkk., 2020; Fransiska, 2026).

Pada Random Forest standar, setiap pohon dibangun menggunakan satu sampel bootstrap yang diambil dari keseluruhan data latih. Sebaliknya, DRF menggunakan seluruh data latih sebagai data awal setiap pohon, kemudian melakukan pengambilan sampel bootstrap secara lokal pada node yang sedang diproses untuk menentukan aturan pemisahan.

Secara umum, tahapan DRF adalah sebagai berikut.

  1. Memulai pembentukan setiap pohon menggunakan seluruh data latih.
  2. Mengidentifikasi observasi yang mencapai node yang sedang diproses.
  3. Mengambil sampel bootstrap dari observasi pada node tersebut.
  4. Memilih sebagian prediktor secara acak sebagai kandidat pemisahan.
  5. Menentukan aturan pemisahan terbaik menggunakan sampel bootstrap pada node.
  6. Menerapkan aturan pemisahan yang diperoleh pada observasi asli yang mencapai node tersebut.
  7. Meneruskan observasi asli ke child nodes sesuai dengan aturan pemisahan.
  8. Mengulangi proses hingga memenuhi kriteria penghentian, kemudian mengulangi pembentukan pohon sampai jumlah pohon yang ditentukan tercapai.

Penerapan bootstrap pada setiap node memungkinkan DRF membentuk pohon yang lebih besar dibandingkan Random Forest standar pada kondisi tertentu. Namun, pohon yang lebih besar tidak selalu menjamin peningkatan akurasi. Kinerja DRF tetap perlu dibandingkan secara empiris dengan Random Forest pada data dan skenario evaluasi yang sama (Han dkk., 2020).

2.3.1 Double Random Forest Klasifikasi

DRF klasifikasi digunakan untuk memprediksi variabel respons kategorik. Dalam metode yang diperkenalkan oleh Han dkk. (2020), setiap pohon dibangun menggunakan mekanisme bootstrap pada setiap node untuk memperoleh aturan pemisahan. Prediksi akhir ditentukan berdasarkan kelas yang memperoleh suara terbanyak dari seluruh pohon.

Secara matematis, prediksi DRF klasifikasi dapat dituliskan sebagai

\[ \hat{G}_{DRF}(x) = \operatorname{mode} \{T_1^{DRF}(x),T_2^{DRF}(x),\ldots,T_B^{DRF}(x)\}. \]

Keterangan:

  • \(\hat{G}_{DRF}(x)\) adalah kelas hasil prediksi DRF.
  • \(T_b^{DRF}(x)\) adalah prediksi kelas dari pohon ke-\(b\) pada DRF.
  • \(B\) adalah jumlah pohon dalam ensemble.

Kinerja DRF klasifikasi dapat dievaluasi menggunakan accuracy, precision, recall, specificity, dan F1-score. Pemilihan metrik perlu mempertimbangkan karakteristik data, terutama apabila terdapat ketidakseimbangan jumlah observasi antarkelas.

Han dkk. (2020) mengembangkan dan mengevaluasi DRF dalam konteks klasifikasi. Oleh karena itu, temuan penelitian tersebut menjadi landasan utama untuk menjelaskan mekanisme DRF klasifikasi.

2.3.2 Double Random Forest Regresi

DRF regresi secara konseptual ditujukan untuk memprediksi variabel respons numerik dengan memanfaatkan mekanisme pembentukan pohon DRF. Jika mekanisme DRF diterapkan pada pohon regresi, prediksi akhir dapat dirumuskan menggunakan rata-rata prediksi seluruh pohon, sebagaimana agregasi pada metode ensemble regresi.

Secara matematis, bentuk agregasi tersebut dapat dituliskan sebagai

\[ \hat{f}_{DRF}(x) = \frac{1}{B} \sum_{b=1}^{B}T_b^{DRF}(x). \]

Keterangan:

  • \(\hat{f}_{DRF}(x)\) adalah nilai prediksi DRF regresi.
  • \(T_b^{DRF}(x)\) adalah prediksi numerik pohon ke-\(b\).
  • \(B\) adalah jumlah pohon dalam ensemble.

Persamaan tersebut menjelaskan bentuk agregasi prediksi apabila DRF diterapkan untuk regresi. Namun, persamaan ini saja tidak membuktikan bahwa algoritma DRF regresi telah didefinisikan atau divalidasi dalam suatu implementasi tertentu.

Artikel asli Han dkk. (2020) berfokus pada DRF klasifikasi. Dengan demikian, penerapan DRF regresi perlu disertai sumber metodologis dan implementasi yang secara eksplisit mendukung pembentukan pohon regresi menggunakan mekanisme bootstrap pada setiap node. Kriteria pemisahan, aturan penghentian pohon, serta prosedur evaluasinya juga perlu dijelaskan agar metode tersebut dapat direplikasi.

Apabila implementasi DRF regresi yang digunakan telah tervalidasi, evaluasi dapat dilakukan menggunakan MSE, RMSE, MAE, dan \(R^2\). Nilai kesalahan prediksi yang lebih rendah menunjukkan hasil yang lebih baik, sedangkan nilai \(R^2\) yang lebih tinggi umumnya menunjukkan kemampuan model yang lebih baik dalam menjelaskan variasi respons pada data evaluasi.

2.3.3 Perbedaan Random Forest dan Double Random Forest

Perbedaan utama Random Forest dan DRF terletak pada mekanisme pengambilan sampel selama pembentukan pohon. Pada Random Forest, sampel bootstrap diambil sekali untuk setiap pohon. Pada DRF, sampel bootstrap diambil pada setiap node yang diproses. Perbedaan tersebut memengaruhi observasi yang digunakan untuk menentukan aturan pemisahan dan memungkinkan pembentukan pohon yang lebih besar pada kondisi tertentu (Han dkk., 2020).

Aspek Random Forest Double Random Forest
Data awal setiap pohon Sampel bootstrap dari data latih Seluruh data latih
Pengambilan sampel Satu kali untuk setiap pohon Pada setiap node
Pemilihan prediktor Acak pada setiap node Acak pada setiap node
Ukuran pohon Bergantung pada parameter dan aturan penghentian Berpotensi lebih besar pada kondisi tertentu
Agregasi klasifikasi Majority voting Majority voting
Agregasi regresi Rata-rata prediksi Rata-rata jika metode diterapkan pada pohon regresi
Pertimbangan komputasi Bergantung pada jumlah dan kompleksitas pohon Berpotensi memerlukan komputasi lebih besar karena bootstrap berulang
Landasan metode Klasifikasi dan regresi Artikel asli berfokus pada klasifikasi; regresi memerlukan dukungan metodologis tambahan

Berdasarkan perbedaan tersebut, DRF dapat dipertimbangkan sebagai alternatif Random Forest, khususnya ketika ukuran pohon menjadi perhatian dalam proses pemodelan. Meskipun demikian, pemilihan metode sebaiknya didasarkan pada hasil evaluasi empiris, bukan hanya pada ukuran pohon yang dihasilkan.

2.4 Parameter Penting dalam Random Forest dan Double Random Forest

Kinerja model berbasis pohon dipengaruhi oleh parameter yang mengatur jumlah pohon, pemilihan prediktor, kompleksitas pohon, dan mekanisme pemisahan. Pengaturan parameter perlu disesuaikan dengan tujuan pemodelan dan karakteristik data (Fransiska, 2026; Wright & Ziegler, 2017).

Beberapa parameter penting dalam Random Forest adalah sebagai berikut.

Parameter Penjelasan
num.trees Jumlah pohon keputusan yang dibangun dalam ensemble.
mtry Jumlah prediktor yang dipertimbangkan secara acak pada setiap node.
min.node.size Ukuran minimum node yang mengatur pembentukan dan kompleksitas pohon.
replace Menentukan apakah pengambilan sampel dilakukan dengan pengembalian.
sample.fraction Proporsi observasi yang digunakan dalam pengambilan sampel.
splitrule Menentukan kriteria pemisahan, misalnya Gini untuk klasifikasi atau variansi untuk regresi pada implementasi yang mendukungnya.
importance Menentukan metode perhitungan kepentingan prediktor.

Parameter yang tersedia dapat berbeda menurut perangkat lunak dan implementasi algoritma yang digunakan. Oleh karena itu, definisi parameter perlu disesuaikan dengan dokumentasi perangkat lunak terkait.

Pada DRF, selain parameter umum seperti jumlah pohon dan pemilihan prediktor, mekanisme pengambilan sampel pada setiap node menjadi karakteristik penting. Parameter khusus DRF perlu mengikuti implementasi yang digunakan karena tidak semua perangkat lunak menyediakan algoritma tersebut secara langsung.

Penentuan parameter yang tepat dapat dilakukan melalui validasi silang atau pendekatan evaluasi lain yang sesuai. Data uji sebaiknya tidak digunakan berulang kali untuk memilih parameter karena dapat menyebabkan penilaian kinerja model menjadi terlalu optimistis.

2.5 Evaluasi Model

Evaluasi model dilakukan untuk mengetahui kemampuan metode dalam menghasilkan prediksi yang mendekati nilai aktual. Metrik evaluasi harus disesuaikan dengan jenis variabel respons, sehingga evaluasi klasifikasi tidak disamakan dengan evaluasi regresi.

2.5.1 Evaluasi Model Klasifikasi

Evaluasi klasifikasi dapat menggunakan confusion matrix yang memuat empat komponen utama, yaitu True Positive (TP), True Negative (TN), False Positive (FP), dan False Negative (FN).

Ukuran evaluasi klasifikasi meliputi:

  1. ** Accuracy **

    Mengukur proporsi seluruh observasi yang diklasifikasikan dengan benar.

    \[ Accuracy = \frac{TP+TN}{TP+TN+FP+FN}. \]

  2. ** Precision **

    Mengukur proporsi prediksi positif yang benar-benar positif.

    \[ Precision = \frac{TP}{TP+FP}. \]

  3. ** Recall atau Sensitivity **

    Mengukur proporsi observasi positif aktual yang berhasil dikenali model.

    \[ Recall = \frac{TP}{TP+FN}. \]

  4. ** Specificity **

    Mengukur proporsi observasi negatif aktual yang berhasil dikenali model.

    \[ Specificity = \frac{TN}{TN+FP}. \]

  5. F1-score

    Merupakan rata-rata harmonik antara precision dan recall.

    \[ F1 = 2\frac{Precision\times Recall} {Precision+Recall}. \]

Nilai accuracy yang tinggi belum tentu menunjukkan model yang baik apabila kelas dalam data tidak seimbang. Oleh karena itu, precision, recall, dan F1-score perlu dipertimbangkan sesuai tujuan analisis.

2.5.2 Evaluasi Model Regresi

Evaluasi regresi dilakukan dengan membandingkan nilai prediksi dan nilai aktual. Misalkan \(y_i\) adalah nilai aktual, \(\hat{y}_i\) adalah nilai prediksi, dan \(n\) adalah jumlah observasi yang dievaluasi.

  1. ** Mean Squared Error (MSE)**

    MSE merupakan rata-rata kuadrat selisih antara nilai aktual dan nilai prediksi.

    \[ MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i-\hat{y}_i)^2. \]

  2. ** Root Mean Squared Error (RMSE)**

    RMSE merupakan akar kuadrat MSE dan memiliki satuan yang sama dengan variabel respons.

    \[ RMSE = \sqrt{ \frac{1}{n} \sum_{i=1}^{n} (y_i-\hat{y}_i)^2 }. \]

  3. ** Mean Absolute Error (MAE)**

    MAE merupakan rata-rata nilai absolut selisih antara nilai aktual dan nilai prediksi.

    \[ MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i-\hat{y}_i|. \]

  4. Koefisien Determinasi (\(R^2\))

    Koefisien determinasi mengukur perbandingan variasi yang dapat dijelaskan oleh prediksi model terhadap variasi total data aktual. Salah satu bentuk perhitungannya adalah

    \[ R^2 = 1- \frac{ \sum_{i=1}^{n}(y_i-\hat{y}_i)^2 }{ \sum_{i=1}^{n}(y_i-\bar{y})^2 }, \]

    dengan \(\bar{y}\) merupakan rata-rata nilai aktual.

Nilai MSE, RMSE, dan MAE yang lebih kecil menunjukkan kesalahan prediksi yang lebih rendah. Nilai \(R^2\) yang lebih tinggi menunjukkan hasil yang lebih baik menurut ukuran tersebut, tetapi interpretasinya perlu mempertimbangkan data yang digunakan untuk evaluasi dan kesesuaian model.

BAB III METODE PENELITIAN

3.1 Sumber Data

Jenis data yang digunakan pada penelitian ini adalah data kategorik dengan variabel respon berupa kelas diagnosis tumor. Data yang digunakan merupakan dataset BreastCancer dari package mlbench pada R. Sumber data yang digunakan dalam penelitian ini yaitu data sekunder yang tersedia secara terbuka melalui package tersebut, terdiri atas 699 observasi dan 11 variabel. Setelah observasi yang mengandung missing value dihapus, data yang dianalisis berjumlah 683 observasi.

3.2 Variabel Penelitian

Penelitian ini menggunakan sepuluh variabel, yaitu satu variabel respon dan sembilan variabel prediktor. Variabel Class adalah variabel respon yang menunjukkan jenis tumor, yaitu benign (jinak) dan malignant (ganas), dengan malignant ditetapkan sebagai kelas positif. Sembilan variabel prediktor berupa hasil penilaian karakteristik sel pada skala 1 sampai 10, yaitu Cl.thickness (ketebalan gumpalan sel), Cell.size (keseragaman ukuran sel), Cell.shape (keseragaman bentuk sel), Marg.adhesion (perlekatan tepi), Epith.c.size (ukuran sel epitel tunggal), Bare.nuclei (inti sel telanjang), Bl.cromatin (kromatin yang halus), Normal.nucleoli (nukleolus normal), dan Mitoses (jumlah mitosis). Variabel Id tidak digunakan dalam pemodelan karena hanya berfungsi sebagai pengenal pasien.

3.3 Langkah-langkah Analisis

Berikut adalah langkah-langkah yang dilakukan dalam analisis:

  1. Memanggil package yang diperlukan, yaitu ranger, mlbench, caret, randomForest, dan pkgbuild.
  2. Membaca dan memeriksa struktur dataset BreastCancer.
  3. Menyiapkan data dengan menghapus variabel Id dan observasi yang mengandung missing value, serta memastikan respons berupa factor.
  4. Membagi data menjadi 70% data training dan 30% data testing dengan set.seed(234).
  5. Membentuk model Random Forest dasar (num.trees = 500, mtry = 3), lalu melihat OOB error, prediksi data testing, confusion matrix, ukuran evaluasi, dan variable importance.
  6. Membentuk sembilan model dari kombinasi mtry = 2, 3, 5 dan num.trees = 200, 500, 1000.
  7. Menghitung OOB error, confusion matrix, accuracy, precision, recall, specificity, dan F1-score pada data testing untuk setiap model.
  8. Membandingkan hasil kesembilan model Random Forest beserta variable importance-nya.
  9. Menyiapkan mesin Double Random Forest dengan mengunduh dan mengompilasi kode sumber dari modul, lalu membentuk model DRF (ntree = 500, mtry = 3) pada data training dan testing yang sama.
  10. Menghitung confusion matrix, ukuran evaluasi, dan variable importance DRF, lalu mengulangi untuk sembilan kombinasi mtry dan ntree yang sama dengan Random Forest.
  11. Membandingkan Random Forest dan Double Random Forest (ukuran evaluasi, OOB error, ukuran pohon, dan waktu komputasi), lalu menentukan model terbaik dan menginterpretasikan setiap output.

BAB IV HASIL DAN PEMBAHASAN

4.1 Persiapan Data

4.1.1 Eksplorasi Data

library(ranger)
library(mlbench)
library(caret)

# Memuat data
data(BreastCancer, package = "mlbench")
bc <- BreastCancer

# Melihat struktur dan ringkasan data
str(bc)
## 'data.frame':    699 obs. of  11 variables:
##  $ Id             : chr  "1000025" "1002945" "1015425" "1016277" ...
##  $ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 5 3 6 4 8 1 2 2 4 ...
##  $ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 1 1 2 ...
##  $ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 2 1 1 ...
##  $ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 5 1 1 3 8 1 1 1 1 ...
##  $ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 7 2 3 2 7 2 2 2 2 ...
##  $ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 10 2 4 1 10 10 1 1 1 ...
##  $ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 3 3 3 3 3 9 3 3 1 2 ...
##  $ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 1 7 1 7 1 1 1 1 ...
##  $ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ...
##  $ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
summary(bc)
##          Id       Cl.thickness   Cell.size     Cell.shape  Marg.adhesion
##  Length   :699   1      :145   1      :384   1      :353   1      :407  
##  N.unique :645   5      :130   10     : 67   2      : 59   2      : 58  
##  N.blank  :  0   3      :108   3      : 52   10     : 58   3      : 58  
##  Min.nchar:  5   4      : 80   2      : 45   3      : 56   10     : 55  
##  Max.nchar:  8   10     : 69   4      : 40   4      : 44   4      : 33  
##                  2      : 50   5      : 30   5      : 34   8      : 25  
##                  (Other):117   (Other): 81   (Other): 95   (Other): 63  
##   Epith.c.size  Bare.nuclei   Bl.cromatin  Normal.nucleoli    Mitoses   
##  2      :386   1      :402   2      :166   1      :443     1      :579  
##  3      : 72   10     :132   3      :165   10     : 61     2      : 35  
##  4      : 48   2      : 30   1      :152   3      : 44     3      : 33  
##  1      : 47   5      : 30   7      : 73   2      : 36     10     : 14  
##  6      : 41   3      : 28   4      : 40   8      : 24     4      : 12  
##  5      : 39   (Other): 61   5      : 34   6      : 22     7      :  9  
##  (Other): 66   NAs    : 16   (Other): 69   (Other): 69     (Other): 17  
##        Class    
##  benign   :458  
##  malignant:241  
##                 
##                 
##                 
##                 
## 
# Memeriksa missing value
sapply(bc, function(x) sum(is.na(x)))
##              Id    Cl.thickness       Cell.size      Cell.shape   Marg.adhesion 
##               0               0               0               0               0 
##    Epith.c.size     Bare.nuclei     Bl.cromatin Normal.nucleoli         Mitoses 
##               0              16               0               0               0 
##           Class 
##               0

Berdasarkan hasil pemeriksaan, dataset BreastCancer terdiri atas 699 observasi dan 11 variabel. Variabel Id berisi nomor pengenal pasien, sembilan variabel prediktor berupa penilaian karakteristik sel berskala 1 sampai 10 yang tersimpan sebagai factor, dan variabel Class menjadi variabel respon. Pemeriksaan missing value menunjukkan bahwa hanya variabel Bare.nuclei yang memiliki data hilang, yaitu sebanyak 16 pengamatan. Karena jumlahnya kecil dibandingkan keseluruhan data, pengamatan tersebut dihapus pada tahap persiapan berikutnya.

4.1.2 Persiapan Data

# Menghapus Id karena hanya merupakan identifier
bc$Id <- NULL
# Menghapus observasi yang mengandung missing value
bc <- na.omit(bc)
# Memastikan respons berupa factor
bc$Class <- droplevels(as.factor(bc$Class))

levels(bc$Class)
## [1] "benign"    "malignant"
table(bc$Class)
## 
##    benign malignant 
##       444       239
prop.table(table(bc$Class))
## 
##    benign malignant 
## 0.6500732 0.3499268

Setelah variabel Id dihapus dan 16 observasi yang mengandung missing value dibuang, data yang tersisa berjumlah 683 observasi dengan 10 variabel. Variabel Class memiliki dua kategori, yaitu benign dan malignant, dengan 444 pengamatan benign (65,01%) dan 239 pengamatan malignant (34,99%). Kelas benign memang lebih banyak, tetapi perbedaannya belum terlalu ekstrem sehingga data masih dapat langsung dimodelkan tanpa penanganan khusus.

4.1.3 Pembagian Data Training dan Testing

# Membagi data training dan testing (70% training dan 30% testing)
set.seed(234)
idx.cls <- createDataPartition(bc$Class, p = 0.70, list = FALSE)
bc.train <- bc[idx.cls, ]
bc.test  <- bc[-idx.cls, ]

dim(bc.train)
## [1] 479  10
dim(bc.test)
## [1] 204  10
table(bc.train$Class)
## 
##    benign malignant 
##       311       168
table(bc.test$Class)
## 
##    benign malignant 
##       133        71
prop.table(table(bc.train$Class))
## 
##    benign malignant 
## 0.6492693 0.3507307
prop.table(table(bc.test$Class))
## 
##    benign malignant 
## 0.6519608 0.3480392

Data dibagi menjadi 479 observasi sebagai data training dan 204 observasi sebagai data testing (sekitar 70% dan 30%). Data training berisi 311 pengamatan benign dan 168 pengamatan malignant, sedangkan data testing berisi 133 pengamatan benign dan 71 pengamatan malignant. Proporsi kelas pada kedua data hampir sama dengan data awal (sekitar 65% benign dan 35% malignant) karena createDataPartition() membagi data secara stratified berdasarkan kelas, sehingga data testing cukup mewakili kondisi data keseluruhan.

4.2 Model Random Forest Dasar (num.trees = 500 dan mtry = 3)

4.2.1 Pembentukan Model dan OOB Error

# Membentuk Random Forest
set.seed(234)
rf.cls <- ranger(
  Class ~ ., data = bc.train,
  num.trees = 500,
  mtry = 3,
  min.node.size = 1,
  replace = TRUE,
  sample.fraction = 1,
  importance = "permutation",
  seed = 234
)
rf.cls
## Ranger result
## 
## Call:
##  ranger(Class ~ ., data = bc.train, num.trees = 500, mtry = 3,      min.node.size = 1, replace = TRUE, sample.fraction = 1, importance = "permutation",      seed = 234) 
## 
## Type:                             Classification 
## Number of trees:                  500 
## Sample size:                      479 
## Number of independent variables:  9 
## Mtry:                             3 
## Target node size:                 1 
## Variable importance mode:         permutation 
## Splitrule:                        gini 
## OOB prediction error:             2.92 %
# Evaluasi OOB
rf.cls$prediction.error
## [1] 0.02922756
rf.cls$confusion.matrix
##            predicted
## true        benign malignant
##   benign       303         8
##   malignant      6       162
head(rf.cls$predictions)
## [1] benign    benign    malignant malignant benign    benign   
## Levels: benign malignant

Model Random Forest dibentuk dari 500 pohon klasifikasi dengan mtry = 3 dari 9 prediktor, min.node.size = 1 sehingga pohon tumbuh sangat dalam, dan importance = "permutation" supaya variable importance dapat dihitung. Nilai OOB prediction error sebesar 0,0292 berarti sekitar 2,92% observasi data training salah diklasifikasikan ketika diprediksi hanya oleh pohon yang tidak memuat observasi tersebut dalam sampel bootstrap-nya. Confusion matrix OOB menunjukkan 303 pengamatan benign terprediksi benar dan 8 salah, serta 162 pengamatan malignant terprediksi benar dan 6 salah, sehingga total 14 dari 479 observasi yang salah. Karena prediksi OOB tidak memakai pohon yang melihat observasi tersebut, nilai ini lebih jujur dibandingkan error pada data latih biasa.

4.2.2 Prediksi, Confusion Matrix, dan Ukuran Evaluasi

# Prediksi data testing
pred.rf.cls <- predict(rf.cls, data = bc.test)$predictions
pred.rf.cls <- factor(pred.rf.cls, levels = levels(bc.test$Class))
head(pred.rf.cls)
## [1] malignant benign    benign    benign    benign    malignant
## Levels: benign malignant
hasil.rf.cls <- data.frame(Aktual = bc.test$Class, Prediksi = pred.rf.cls)
head(hasil.rf.cls, 10)
##       Aktual  Prediksi
## 1     benign malignant
## 2     benign    benign
## 3     benign    benign
## 4     benign    benign
## 5     benign    benign
## 6  malignant malignant
## 7     benign    benign
## 8     benign    benign
## 9     benign    benign
## 10 malignant malignant
# Confusion Matrix dan ukuran evaluasi
cm.rf <- confusionMatrix(data = pred.rf.cls, reference = bc.test$Class,
                         positive = "malignant")
cm.rf
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  benign malignant
##   benign       130         3
##   malignant      3        68
##                                           
##                Accuracy : 0.9706          
##                  95% CI : (0.9371, 0.9891)
##     No Information Rate : 0.652           
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9352          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9577          
##             Specificity : 0.9774          
##          Pos Pred Value : 0.9577          
##          Neg Pred Value : 0.9774          
##              Prevalence : 0.3480          
##          Detection Rate : 0.3333          
##    Detection Prevalence : 0.3480          
##       Balanced Accuracy : 0.9676          
##                                           
##        'Positive' Class : malignant       
## 
tab.rf <- table(Aktual = bc.test$Class, Prediksi = pred.rf.cls)
tab.rf
##            Prediksi
## Aktual      benign malignant
##   benign       130         3
##   malignant      3        68
TN <- tab.rf["benign", "benign"]
FP <- tab.rf["benign", "malignant"]
FN <- tab.rf["malignant", "benign"]
TP <- tab.rf["malignant", "malignant"]

Accuracy <- (TP + TN) / (TP + TN + FP + FN)
Precision <- TP / (TP + FP)
Recall <- TP / (TP + FN)
Specificity <- TN / (TN + FP)
F1 <- 2 * (Precision * Recall) / (Precision + Recall)

evaluasi.rf <- data.frame(Accuracy, Precision, Recall, Specificity,
                          F1_Score = F1)
round(evaluasi.rf, 4)
##   Accuracy Precision Recall Specificity F1_Score
## 1   0.9706    0.9577 0.9577      0.9774   0.9577

Pada data testing, model dasar memprediksi 130 dari 133 pengamatan benign dengan benar (TN) dan 3 salah (FP), serta 68 dari 71 pengamatan malignant dengan benar (TP) dan 3 salah (FN). Dari angka tersebut diperoleh accuracy sebesar 0,9706, precision sebesar 0,9577, recall sebesar 0,9577, specificity sebesar 0,9774, dan F1-score sebesar 0,9577. Error pada data testing (6 dari 204, yaitu 0,0294) sangat dekat dengan OOB error (0,0292), yang menunjukkan bahwa OOB error memang dapat dipakai sebagai perkiraan kemampuan model pada data baru. Pada model ini jumlah pasien ganas yang terlewat (FN) sama dengan jumlah pasien jinak yang diprediksi ganas (FP), sehingga precision dan recall bernilai sama.

4.2.3 Variable Importance

importance.rf <- sort(rf.cls$variable.importance, decreasing = TRUE)
round(importance.rf, 4)
##     Bare.nuclei       Cell.size      Cell.shape    Cl.thickness   Marg.adhesion 
##          0.0991          0.0853          0.0651          0.0260          0.0193 
##     Bl.cromatin Normal.nucleoli    Epith.c.size         Mitoses 
##          0.0188          0.0148          0.0109          0.0032
barplot(importance.rf, las = 2,
        main = "Variable Importance - Random Forest Classification",
        ylab = "Permutation Importance")

Berdasarkan permutation importance, variabel yang paling berpengaruh adalah Bare.nuclei (0,0991), diikuti Cell.size (0,0853) dan Cell.shape (0,0651), sedangkan Cl.thickness berada jauh di bawahnya (0,0260). Variabel dengan pengaruh paling kecil adalah Mitoses (0,0032). Artinya, jika nilai Bare.nuclei diacak, akurasi prediksi OOB menurun paling besar dibanding variabel lain, sehingga variabel ini paling dibutuhkan model untuk membedakan tumor jinak dan ganas. Sebaliknya, Mitoses hampir tidak berkontribusi pada ketepatan prediksi.

4.3 Perbandingan mtry = 2, 3, 5 dan num.trees = 200, 500, 1000

4.3.1 Pembentukan Sembilan Model

# Kombinasi parameter
grid <- expand.grid(mtry = c(2, 3, 5), num.trees = c(200, 500, 1000))
grid
##   mtry num.trees
## 1    2       200
## 2    3       200
## 3    5       200
## 4    2       500
## 5    3       500
## 6    5       500
## 7    2      1000
## 8    3      1000
## 9    5      1000
model.list <- list()
hasil.grid <- data.frame()

for (i in seq_len(nrow(grid))) {
  mt <- grid$mtry[i]
  nt <- grid$num.trees[i]

  set.seed(234)
  rf <- ranger(Class ~ ., data = bc.train,
               num.trees = nt, mtry = mt, min.node.size = 1,
               replace = TRUE, sample.fraction = 1,
               importance = "permutation", seed = 234)

  pred <- factor(predict(rf, data = bc.test)$predictions,
                 levels = levels(bc.test$Class))
  tab <- table(Aktual = bc.test$Class, Prediksi = pred)

  TN <- tab["benign", "benign"];       FP <- tab["benign", "malignant"]
  FN <- tab["malignant", "benign"];    TP <- tab["malignant", "malignant"]
  Pre <- TP / (TP + FP);  Rec <- TP / (TP + FN)

  hasil.grid <- rbind(hasil.grid, data.frame(
    num.trees = nt, mtry = mt, OOB_Error = rf$prediction.error,
    TN = TN, FP = FP, FN = FN, TP = TP,
    Accuracy = (TP + TN) / sum(tab), Precision = Pre, Recall = Rec,
    Specificity = TN / (TN + FP), F1_Score = 2 * Pre * Rec / (Pre + Rec)))

  model.list[[paste0("nt", nt, "_mt", mt)]] <- rf
}

hasil.grid[, -c(1, 2, 4:7)] <- round(hasil.grid[, -c(1, 2, 4:7)], 4)
hasil.grid
##   num.trees mtry OOB_Error  TN FP FN TP Accuracy Precision Recall Specificity
## 1       200    2    0.0292 130  3  1 70   0.9804    0.9589 0.9859      0.9774
## 2       200    3    0.0271 130  3  3 68   0.9706    0.9577 0.9577      0.9774
## 3       200    5    0.0292 129  4  4 67   0.9608    0.9437 0.9437      0.9699
## 4       500    2    0.0292 129  4  1 70   0.9755    0.9459 0.9859      0.9699
## 5       500    3    0.0292 130  3  3 68   0.9706    0.9577 0.9577      0.9774
## 6       500    5    0.0292 129  4  4 67   0.9608    0.9437 0.9437      0.9699
## 7      1000    2    0.0292 129  4  2 69   0.9706    0.9452 0.9718      0.9699
## 8      1000    3    0.0271 129  4  3 68   0.9657    0.9444 0.9577      0.9699
## 9      1000    5    0.0271 129  4  5 66   0.9559    0.9429 0.9296      0.9699
##   F1_Score
## 1   0.9722
## 2   0.9577
## 3   0.9437
## 4   0.9655
## 5   0.9577
## 6   0.9437
## 7   0.9583
## 8   0.9510
## 9   0.9362

Kesembilan model dibentuk dengan pengaturan lain yang sama (min.node.size = 1, replace = TRUE, sample.fraction = 1) sehingga perbedaan hasil benar-benar berasal dari mtry dan num.trees. Tabel di atas memuat OOB error, empat komponen confusion matrix, dan lima ukuran evaluasi pada data testing untuk setiap kombinasi. Seluruh model menghasilkan accuracy di atas 0,95, dengan kisaran 0,9559 sampai 0,9804, dan OOB error hanya berkisar antara 0,0271 sampai 0,0292. Hal ini menunjukkan bahwa Random Forest sudah bekerja sangat baik pada data BreastCancer untuk semua kombinasi parameter yang dicoba.

4.3.2 Pengaruh mtry dan num.trees terhadap Performa

# Ringkasan rata-rata accuracy menurut mtry dan num.trees
aggregate(Accuracy ~ mtry, data = hasil.grid, FUN = mean)
##   mtry  Accuracy
## 1    2 0.9755000
## 2    3 0.9689667
## 3    5 0.9591667
aggregate(Accuracy ~ num.trees, data = hasil.grid, FUN = mean)
##   num.trees  Accuracy
## 1       200 0.9706000
## 2       500 0.9689667
## 3      1000 0.9640667
# Plot accuracy dan OOB error
warna <- c("blue", "red", "forestgreen")
par(mfrow = c(1, 2))
plot(NULL, xlim = c(200, 1000), ylim = range(hasil.grid$Accuracy),
     xaxt = "n", xlab = "num.trees", ylab = "Accuracy (testing)",
     main = "Accuracy Data Testing")
axis(1, at = c(200, 500, 1000))
for (k in 1:3) {
  d <- subset(hasil.grid, mtry == c(2, 3, 5)[k])
  lines(d$num.trees, d$Accuracy, type = "b", pch = 19, lwd = 2, col = warna[k])
}
legend("topright", legend = paste("mtry =", c(2, 3, 5)), col = warna,
       pch = 19, lwd = 2, cex = 0.8)

plot(NULL, xlim = c(200, 1000), ylim = range(hasil.grid$OOB_Error),
     xaxt = "n", xlab = "num.trees", ylab = "OOB Error", main = "OOB Error")
axis(1, at = c(200, 500, 1000))
for (k in 1:3) {
  d <- subset(hasil.grid, mtry == c(2, 3, 5)[k])
  lines(d$num.trees, d$OOB_Error, type = "b", pch = 19, lwd = 2, col = warna[k])
}

par(mfrow = c(1, 1))

Dari sisi mtry, rata-rata accuracy data testing menurun seiring bertambahnya mtry, yaitu sekitar 0,9755 untuk mtry = 2, 0,9690 untuk mtry = 3, dan 0,9592 untuk mtry = 5. Pola ini sesuai dengan teori: semakin besar mtry, semakin banyak prediktor kandidat di setiap node, sehingga prediktor yang kuat lebih sering terpilih dan pohon-pohon menjadi lebih mirip (korelasi antar-pohon naik). Akibatnya, manfaat pengacakan prediktor berkurang. Pada mtry = 5, jumlah kesalahan testing bahkan mencapai 8 sampai 9 pengamatan, dibanding 4 sampai 6 pengamatan pada mtry = 2.

Dari sisi num.trees, menambah pohon dari 200 menjadi 500 atau 1000 tidak memperbaiki akurasi. Untuk mtry = 2 misalnya, accuracy justru turun dari 0,9804 (200 pohon) menjadi 0,9755 (500 pohon) dan 0,9706 (1000 pohon), sedangkan untuk mtry = 3 dan 5 hasilnya hampir tidak berubah. Perubahan sebesar satu atau dua pengamatan dari 204 data lebih mungkin berasal dari variasi acak pada pohon-pohon yang terbentuk daripada pengaruh sebenarnya dari jumlah pohon. Hal ini wajar karena Random Forest tidak mudah overfitting ketika jumlah pohon ditambah; jumlah pohon yang lebih banyak terutama membuat prediksi lebih stabil, dan pada data sekecil ini kestabilan tersebut sudah tercapai sekitar 200 pohon. Probst dan Boulesteix (2018) juga menunjukkan bahwa error klasifikasi dapat mencapai nilai terendah lalu naik lagi saat pohon ditambah, sehingga turunnya accuracy pada 500 dan 1000 pohon di sini masih dalam kewajaran.

OOB error berada pada 0,0292 untuk sebagian besar model, dan sedikit lebih rendah (0,0271) pada tiga model, yaitu (num.trees = 200, mtry = 3), (1000, 3), dan (1000, 5). Selisih 0,0271 dengan 0,0292 setara dengan hanya satu observasi dari 479 data training, sehingga OOB error tidak cukup membedakan model-model ini. Menariknya, OOB error terendah tidak selalu sejalan dengan accuracy tertinggi pada data testing, misalnya model (1000, 5) memiliki OOB error terendah tetapi accuracy testing terendah (0,9559).

4.3.3 Variable Importance pada Semua Model

# Matriks variable importance seluruh model
nama.var <- names(model.list[[1]]$variable.importance)
imp.mat <- sapply(model.list, function(m) m$variable.importance[nama.var])
round(imp.mat, 4)
##                 nt200_mt2 nt200_mt3 nt200_mt5 nt500_mt2 nt500_mt3 nt500_mt5
## Cl.thickness       0.0245    0.0278    0.0233    0.0267    0.0260    0.0217
## Cell.size          0.0787    0.0917    0.0898    0.0773    0.0853    0.0909
## Cell.shape         0.0610    0.0628    0.0427    0.0630    0.0651    0.0460
## Marg.adhesion      0.0193    0.0191    0.0209    0.0191    0.0193    0.0195
## Epith.c.size       0.0117    0.0099    0.0086    0.0132    0.0109    0.0093
## Bare.nuclei        0.0839    0.0972    0.1259    0.0805    0.0991    0.1243
## Bl.cromatin        0.0215    0.0180    0.0142    0.0219    0.0188    0.0153
## Normal.nucleoli    0.0204    0.0160    0.0217    0.0218    0.0148    0.0203
## Mitoses            0.0038    0.0037    0.0026    0.0031    0.0032    0.0030
##                 nt1000_mt2 nt1000_mt3 nt1000_mt5
## Cl.thickness        0.0263     0.0248     0.0227
## Cell.size           0.0779     0.0855     0.0911
## Cell.shape          0.0626     0.0593     0.0438
## Marg.adhesion       0.0189     0.0195     0.0189
## Epith.c.size        0.0140     0.0119     0.0097
## Bare.nuclei         0.0817     0.1003     0.1214
## Bl.cromatin         0.0205     0.0192     0.0162
## Normal.nucleoli     0.0199     0.0165     0.0208
## Mitoses             0.0033     0.0032     0.0032
# Plot variable importance untuk mtry = 2, 3, 5 pada num.trees = 500
par(mfrow = c(1, 3))
for (k in c("nt500_mt2", "nt500_mt3", "nt500_mt5")) {
  imp <- sort(model.list[[k]]$variable.importance, decreasing = TRUE)
  barplot(imp, las = 2, main = k, ylab = "Permutation Importance",
          cex.names = 0.7)
}

par(mfrow = c(1, 1))

Pada seluruh sembilan model, tiga variabel teratas selalu sama, yaitu Bare.nuclei, Cell.size, dan Cell.shape, disusul Cl.thickness di urutan keempat, sedangkan Mitoses selalu menjadi variabel dengan importance terendah (sekitar 0,0026 sampai 0,0038). Dengan kata lain, kesimpulan tentang variabel penting sangat konsisten dan tidak terpengaruh oleh pilihan mtry maupun num.trees. Meski begitu, besar importance bergeser mengikuti mtry: Bare.nuclei naik dari sekitar 0,081 (mtry = 2) menjadi 0,099 (mtry = 3) dan 0,124 (mtry = 5), sebaliknya Cell.shape turun dari sekitar 0,063 menjadi 0,046. Hal ini masuk akal karena saat mtry besar, variabel terkuat seperti Bare.nuclei lebih sering tersedia di setiap node dan mengambil porsi split yang sebelumnya dibagi ke variabel lain yang berkorelasi dengannya, seperti Cell.shape. Pada mtry kecil, importance menjadi lebih merata di antara variabel-variabel yang saling berkorelasi.

4.4 Double Random Forest (DRF) Klasifikasi

4.4.1 Persiapan Mesin DRF

library(randomForest)
library(pkgbuild)

# Cek compiler
pkgbuild::check_build_tools(debug = FALSE)
cat("R version :", R.version.string, "\n")
## R version : R version 4.6.1 (2026-06-24 ucrt)
cat("make      :", Sys.which("make"), "\n")
## make      : C:\rtools45\usr\bin\make.exe
cat("gcc       :", Sys.which("gcc"), "\n")
## gcc       : C:\rtools45\X86_64~1.POS\bin\gcc.exe
cat("gfortran  :", Sys.which("gfortran"), "\n")
## gfortran  : C:\rtools45\X86_64~1.POS\bin\gfortran.exe
if (Sys.which("make") == "" || Sys.which("gcc") == "" ||
    Sys.which("gfortran") == "") {
  stop("Rtools/compiler belum tersedia. Install Rtools lalu restart RStudio.")
}

# Penyesuaian lintas sistem operasi
os.win  <- .Platform$OS.type == "windows"
nama.lib <- paste0("DRF", .Platform$dynlib.ext)
r.bin   <- file.path(R.home("bin"), if (os.win) "R.exe" else "R")

# Download dan ekstrak source code DRF
drf.extract <- tempfile(pattern = "DRF_extract_"); dir.create(drf.extract, recursive = TRUE)
src.build   <- tempfile(pattern = "DRF_build_");   dir.create(src.build, recursive = TRUE)
drf.zip     <- tempfile(fileext = ".zip")

download.file(url = "https://raw.githubusercontent.com/shan-stat/DRF/master/DRF.zip",
              destfile = drf.zip, mode = "wb")
unzip(drf.zip, exdir = drf.extract)

classTree.file <- list.files(drf.extract, pattern = "^classTree\\.c$",
                             recursive = TRUE, full.names = TRUE)
if (length(classTree.file) == 0) stop("classTree.c tidak ditemukan dalam DRF.zip.")
src.asli <- dirname(classTree.file[1])

hasil.copy <- file.copy(from = list.files(src.asli, full.names = TRUE),
                        to = src.build, overwrite = TRUE)
if (!all(hasil.copy)) stop("Ada file source DRF yang gagal dicopy.")

file.wajib <- c("classTree.c", "regrf.c", "regTree.c", "rf.c", "rf.h",
                "rfsub_drf.f", "rfutils.c")
cek.file <- file.exists(file.path(src.build, file.wajib))
names(cek.file) <- file.wajib
cek.file
## classTree.c     regrf.c   regTree.c        rf.c        rf.h rfsub_drf.f 
##        TRUE        TRUE        TRUE        TRUE        TRUE        TRUE 
##   rfutils.c 
##        TRUE
if (!all(cek.file)) stop("Source code DRF belum lengkap.")
# Patch source DRF untuk R >= 4.5 (Calloc/Realloc/Free -> R_Calloc/R_Realloc/R_Free)
c.files <- list.files(src.build, pattern = "\\.(c|h)$", full.names = TRUE)

if (getRversion() >= "4.5.0") {
  for (f in c.files) {
    txt <- readLines(f, warn = FALSE)
    txt <- gsub("\\bCalloc\\s*\\(",  "R_Calloc(",  txt, perl = TRUE)
    txt <- gsub("\\bRealloc\\s*\\(", "R_Realloc(", txt, perl = TRUE)
    txt <- gsub("\\bFree\\s*\\(",    "R_Free(",    txt, perl = TRUE)
    writeLines(txt, f, useBytes = TRUE)
  }
  cat("Patch kompatibilitas R >= 4.5 selesai.\n")
} else {
  cat("R < 4.5: patch tidak diperlukan.\n")
}
## Patch kompatibilitas R >= 4.5 selesai.
# Compile source code menjadi library DRF
unlink(list.files(src.build, pattern = "\\.(o|dll|so)$", full.names = TRUE), force = TRUE)
wd.awal <- getwd()
setwd(src.build)
src.files <- c("classTree.c", "regrf.c", "regTree.c", "rf.c", "rfsub_drf.f", "rfutils.c")
status <- system2(command = r.bin, args = c("CMD", "SHLIB", "-o", nama.lib, src.files),
                  stdout = FALSE, stderr = FALSE)
setwd(wd.awal)

dll.path <- file.path(src.build, nama.lib)
cat("Status compile =", status, "| library tersedia =", file.exists(dll.path), "\n")
## Status compile = 0 | library tersedia = TRUE
if (status != 0 || !file.exists(dll.path)) stop("Kompilasi DRF gagal.")
# Load native engine dan fungsi R resmi DRF
drf.dll <- dyn.load(dll.path)
DRF_classRF <- getNativeSymbolInfo("classRF", PACKAGE = drf.dll[["name"]])
DRF_regRF   <- getNativeSymbolInfo("regRF",   PACKAGE = drf.dll[["name"]])

func.file <- tempfile(fileext = ".R")
download.file(url = "https://raw.githubusercontent.com/shan-stat/DRF/master/FuncDRF.R",
              destfile = func.file, mode = "wb")
kode <- readLines(func.file, warn = FALSE)

# Ambil hanya Step 1 sampai Step 3
i4 <- grep("^## Step 4\\.", kode)[1]
if (is.na(i4)) stop("Bagian Step 4 tidak ditemukan dalam FuncDRF.R.")
kode.drf <- kode[1:(i4 - 1)]

# Arahkan classRF dan regRF ke library DRF
kode.drf <- gsub('.C("classRF",', '.C(DRF_classRF,', kode.drf, fixed = TRUE)
kode.drf <- gsub('.C("regRF",',   '.C(DRF_regRF,',   kode.drf, fixed = TRUE)

eval(parse(text = paste(kode.drf, collapse = "\n")), envir = .GlobalEnv)

exists("DRF.default")
## [1] TRUE
exists("DRF.formula")
## [1] TRUE

Syntax tersebut memeriksa ketersediaan compiler, mengunduh dan mengekstrak kode sumber DRF, melakukan patch kompatibilitas hanya jika versi R sudah 4.5 atau lebih baru, mengompilasi kode C dan Fortran menjadi satu library, lalu memuatnya dan mengarahkan fungsi classRF dan regRF ke library tersebut. Hasil pemeriksaan TRUE pada DRF.default dan DRF.formula menunjukkan fungsi DRF berhasil tersedia di R, dan status kompilasi bernilai 0 menandakan proses kompilasi berjalan tanpa kegagalan. Fungsi DRF.default() inilah yang dipakai untuk membentuk model pada bagian berikutnya.

4.4.2 Memisahkan Respons dan Prediktor

# Training
x.train <- bc.train[, names(bc.train) != "Class", drop = FALSE]
y.train <- bc.train$Class
# Testing
x.test <- bc.test[, names(bc.test) != "Class", drop = FALSE]
y.test <- bc.test$Class

dim(x.train)
## [1] 479   9
dim(x.test)
## [1] 204   9

Berbeda dengan ranger() yang memakai rumus Class ~ ., fungsi DRF.default() menerima prediktor (x) dan respons (y) secara terpisah. Data training terdiri atas 479 observasi dengan 9 prediktor, dan data testing terdiri atas 204 observasi dengan 9 prediktor. Data yang dipakai identik dengan data pada Random Forest karena berasal dari bc.train dan bc.test yang sama, sehingga perbandingan kedua metode dilakukan pada kondisi yang adil.

4.4.3 Model DRF (ntree = 500 dan mtry = 3)

set.seed(234)
drf.cls <- DRF.default(
  x = x.train, y = y.train,
  xtest = x.test, ytest = y.test,
  ntree = 500, mtry = 3, nodesize = 1,
  importance = TRUE
)
print(drf.cls)
## 
## Call:
##  randomForest(x = x.train, y = y.train, xtest = x.test, ytest = y.test,      ntree = 500, mtry = 3, nodesize = 1, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: NaN%
## Confusion matrix:
##           benign malignant class.error
## benign         0         0         NaN
## malignant      0         0         NaN
##                 Test set error rate: 2.94%
## Confusion matrix:
##           benign malignant class.error
## benign       129         4  0.03007519
## malignant      2        69  0.02816901

Model DRF dibentuk dengan pengaturan yang sama seperti Random Forest dasar, yaitu 500 pohon, mtry = 3, dan nodesize = 1, sehingga perbedaan hasil berasal dari mekanisme pembentukan pohon. Output menunjukkan test set error rate sebesar 2,94%, dengan 129 pengamatan benign dan 69 pengamatan malignant terprediksi benar. Hal yang perlu diperhatikan adalah OOB estimate of error rate bernilai NaN. Hal ini wajar dan bukan kesalahan: karena setiap pohon DRF dibangun dari seluruh data training, tidak ada observasi yang “tertinggal” (out-of-bag) untuk menghitung prediksi OOB. Dengan demikian, OOB error hanya dapat digunakan sebagai evaluasi internal pada Random Forest, sedangkan DRF harus dievaluasi dengan data testing.

4.4.4 Prediksi, Confusion Matrix, dan Ukuran Evaluasi

pred.drf.cls <- factor(drf.cls$test$predicted, levels = levels(y.test))
hasil.drf.cls <- data.frame(Aktual = y.test, Prediksi = pred.drf.cls)
head(hasil.drf.cls, 10)
##       Aktual  Prediksi
## 2     benign malignant
## 5     benign    benign
## 7     benign    benign
## 9     benign    benign
## 12    benign    benign
## 19 malignant malignant
## 27    benign    benign
## 30    benign    benign
## 32    benign    benign
## 37 malignant malignant
cm.drf.cls <- confusionMatrix(data = pred.drf.cls, reference = y.test,
                              positive = "malignant")
cm.drf.cls
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  benign malignant
##   benign       129         2
##   malignant      4        69
##                                           
##                Accuracy : 0.9706          
##                  95% CI : (0.9371, 0.9891)
##     No Information Rate : 0.652           
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9356          
##                                           
##  Mcnemar's Test P-Value : 0.6831          
##                                           
##             Sensitivity : 0.9718          
##             Specificity : 0.9699          
##          Pos Pred Value : 0.9452          
##          Neg Pred Value : 0.9847          
##              Prevalence : 0.3480          
##          Detection Rate : 0.3382          
##    Detection Prevalence : 0.3578          
##       Balanced Accuracy : 0.9709          
##                                           
##        'Positive' Class : malignant       
## 
tab.drf <- table(Aktual = y.test, Prediksi = pred.drf.cls)
tab.drf
##            Prediksi
## Aktual      benign malignant
##   benign       129         4
##   malignant      2        69
TN <- tab.drf["benign", "benign"];       FP <- tab.drf["benign", "malignant"]
FN <- tab.drf["malignant", "benign"];    TP <- tab.drf["malignant", "malignant"]

Accuracy <- (TP + TN) / (TP + TN + FP + FN)
Precision <- TP / (TP + FP)
Recall <- TP / (TP + FN)
Specificity <- TN / (TN + FP)
F1 <- 2 * (Precision * Recall) / (Precision + Recall)

evaluasi.drf.cls <- data.frame(Accuracy, Precision, Recall, Specificity,
                               F1_Score = F1)
round(evaluasi.drf.cls, 4)
##   Accuracy Precision Recall Specificity F1_Score
## 1   0.9706    0.9452 0.9718      0.9699   0.9583

Pada data testing, DRF memprediksi 129 dari 133 pengamatan benign dengan benar (TN) dan 4 salah (FP), serta 69 dari 71 pengamatan malignant dengan benar (TP) dan 2 salah (FN). Dari angka tersebut diperoleh accuracy sebesar 0,9706, precision sebesar 0,9452, recall sebesar 0,9718, specificity sebesar 0,9699, dan F1-score sebesar 0,9583, dengan nilai Kappa sebesar 0,9356. Accuracy ini sama dengan Random Forest dasar (0,9706), tetapi susunan kesalahannya berbeda: DRF lebih jarang melewatkan pasien ganas (FN = 2 dibanding 3) namun lebih sering salah menandai pasien jinak sebagai ganas (FP = 4 dibanding 3).

4.4.5 Variable Importance DRF

importance.drf.cls <- drf.cls$importance
importance.drf.cls
##                 benign malignant MeanDecreaseAccuracy MeanDecreaseGini
## Cl.thickness         0         0                    0         9.075173
## Cell.size            0         0                    0        61.684494
## Cell.shape           0         0                    0        47.177653
## Marg.adhesion        0         0                    0         6.648088
## Epith.c.size         0         0                    0        29.134315
## Bare.nuclei          0         0                    0        37.881146
## Bl.cromatin          0         0                    0        21.929163
## Normal.nucleoli      0         0                    0        19.781594
## Mitoses              0         0                    0         2.188849
# Mean Decrease Accuracy
imp.drf.acc <- sort(importance.drf.cls[, "MeanDecreaseAccuracy"], decreasing = TRUE)
imp.drf.acc
##    Cl.thickness       Cell.size      Cell.shape   Marg.adhesion    Epith.c.size 
##               0               0               0               0               0 
##     Bare.nuclei     Bl.cromatin Normal.nucleoli         Mitoses 
##               0               0               0               0
if (all(abs(imp.drf.acc) < .Machine$double.eps)) {
  message("MeanDecreaseAccuracy bernilai 0 untuk seluruh prediktor; ",
          "ukuran ini tidak informatif pada implementasi DRF yang digunakan.")
} else {
  barplot(imp.drf.acc, las = 2, main = "Variable Importance DRF - Accuracy",
          ylab = "Mean Decrease Accuracy")
}

# Mean Decrease Gini
imp.drf.gini <- sort(importance.drf.cls[, "MeanDecreaseGini"], decreasing = TRUE)
round(imp.drf.gini, 4)
##       Cell.size      Cell.shape     Bare.nuclei    Epith.c.size     Bl.cromatin 
##         61.6845         47.1777         37.8811         29.1343         21.9292 
## Normal.nucleoli    Cl.thickness   Marg.adhesion         Mitoses 
##         19.7816          9.0752          6.6481          2.1888
barplot(imp.drf.gini, las = 2, main = "Variable Importance DRF - Gini",
        ylab = "Mean Decrease Gini")

Nilai Mean Decrease Accuracy bernilai 0 untuk seluruh prediktor, sehingga ukuran ini tidak informatif pada implementasi DRF yang digunakan dan grafiknya tidak ditampilkan (sesuai pengaman pada sintaks). Penyebabnya sejalan dengan temuan sebelumnya: ukuran ini bergantung pada observasi OOB, sedangkan pada DRF tidak ada observasi OOB. Karena itu, variable importance DRF dibaca dari Mean Decrease Gini. Variabel paling berpengaruh adalah Cell.size (61,684), diikuti Cell.shape (47,178) dan Bare.nuclei (37,881), sedangkan Mitoses paling kecil (2,189). Perlu dicatat bahwa Gini importance dan permutation importance pada Random Forest memiliki skala dan cara hitung berbeda sehingga nilainya tidak dibandingkan secara langsung, hanya urutan variabelnya. Meski urutannya sedikit berbeda (Bare.nuclei di peringkat pertama pada Random Forest, Cell.size pada DRF), tiga variabel teratas dan variabel terendah (Mitoses) sama pada kedua metode.

4.5 Perbandingan DRF dengan mtry = 2, 3, 5 dan ntree = 200, 500, 1000

hasil.drf <- data.frame()
imp.drf.list <- list()

for (i in seq_len(nrow(grid))) {
  mt <- grid$mtry[i]
  nt <- grid$num.trees[i]

  set.seed(234)
  waktu <- system.time(
    d <- DRF.default(x = x.train, y = y.train, xtest = x.test, ytest = y.test,
                     ntree = nt, mtry = mt, nodesize = 1, importance = TRUE)
  )["elapsed"]

  pred <- factor(d$test$predicted, levels = levels(y.test))
  tab <- table(Aktual = y.test, Prediksi = pred)
  TN <- tab["benign", "benign"];       FP <- tab["benign", "malignant"]
  FN <- tab["malignant", "benign"];    TP <- tab["malignant", "malignant"]
  Pre <- TP / (TP + FP);  Rec <- TP / (TP + FN)

  hasil.drf <- rbind(hasil.drf, data.frame(
    ntree = nt, mtry = mt,
    TN = TN, FP = FP, FN = FN, TP = TP,
    Accuracy = (TP + TN) / sum(tab), Precision = Pre, Recall = Rec,
    Specificity = TN / (TN + FP), F1_Score = 2 * Pre * Rec / (Pre + Rec),
    Waktu_detik = as.numeric(waktu)))

  imp.drf.list[[paste0("nt", nt, "_mt", mt)]] <- d$importance[, "MeanDecreaseGini"]
}

hasil.drf[, -c(1:6)] <- round(hasil.drf[, -c(1:6)], 4)
hasil.drf
##   ntree mtry  TN FP FN TP Accuracy Precision Recall Specificity F1_Score
## 1   200    2 129  4  1 70   0.9755    0.9459 0.9859      0.9699   0.9655
## 2   200    3 129  4  2 69   0.9706    0.9452 0.9718      0.9699   0.9583
## 3   200    5 129  4  3 68   0.9657    0.9444 0.9577      0.9699   0.9510
## 4   500    2 129  4  0 71   0.9804    0.9467 1.0000      0.9699   0.9726
## 5   500    3 129  4  2 69   0.9706    0.9452 0.9718      0.9699   0.9583
## 6   500    5 129  4  2 69   0.9706    0.9452 0.9718      0.9699   0.9583
## 7  1000    2 129  4  0 71   0.9804    0.9467 1.0000      0.9699   0.9726
## 8  1000    3 129  4  1 70   0.9755    0.9459 0.9859      0.9699   0.9655
## 9  1000    5 129  4  2 69   0.9706    0.9452 0.9718      0.9699   0.9583
##   Waktu_detik
## 1        0.86
## 2        0.75
## 3        1.03
## 4        1.45
## 5        1.83
## 6        2.16
## 7        3.19
## 8        3.00
## 9        3.73
# Rata-rata accuracy DRF menurut mtry dan ntree
aggregate(Accuracy ~ mtry, data = hasil.drf, FUN = mean)
##   mtry  Accuracy
## 1    2 0.9787667
## 2    3 0.9722333
## 3    5 0.9689667
aggregate(Accuracy ~ ntree, data = hasil.drf, FUN = mean)
##   ntree  Accuracy
## 1   200 0.9706000
## 2   500 0.9738667
## 3  1000 0.9755000
# Urutan variabel menurut Gini importance
imp.drf.mat <- sapply(imp.drf.list, function(v) v[nama.var])
round(imp.drf.mat, 2)
##                 nt200_mt2 nt200_mt3 nt200_mt5 nt500_mt2 nt500_mt3 nt500_mt5
## Cl.thickness        13.75      9.62      7.50     12.69      9.08      8.09
## Cell.size           55.76     61.94     87.92     54.94     61.68     90.21
## Cell.shape          39.09     50.15     45.30     39.48     47.18     45.28
## Marg.adhesion       14.15      8.11      4.81     13.92      6.65      4.87
## Epith.c.size        23.01     23.43     23.17     25.10     29.13     25.56
## Bare.nuclei         40.00     37.59     39.55     36.40     37.88     36.22
## Bl.cromatin         24.45     20.34     16.62     23.17     21.93     14.37
## Normal.nucleoli     21.66     21.02     13.39     24.83     19.78     13.65
## Mitoses              2.25      1.93      1.74      2.33      2.19      1.69
##                 nt1000_mt2 nt1000_mt3 nt1000_mt5
## Cl.thickness         13.29       9.30       8.03
## Cell.size            54.84      63.62      90.44
## Cell.shape           38.98      43.65      44.39
## Marg.adhesion        13.13       7.66       5.07
## Epith.c.size         24.86      29.52      26.06
## Bare.nuclei          36.98      38.50      35.31
## Bl.cromatin          24.40      21.44      14.32
## Normal.nucleoli      24.26      20.44      14.41
## Mitoses               2.46       2.04       1.68

Seperti pada Random Forest, kesembilan model DRF menghasilkan kinerja yang sangat baik, yaitu accuracy antara 0,9657 sampai 0,9804. Ada dua pola yang menonjol. Pertama, jumlah false positive selalu 4 pada seluruh model, sehingga specificity konstan di 0,9699; dengan kata lain, empat pasien jinak yang sama selalu diprediksi ganas, kemungkinan karena karakteristik selnya mirip tumor ganas. Perbedaan antarmodel sepenuhnya berasal dari false negative, yang bervariasi dari 0 sampai 3. Kedua, mtry yang lebih kecil lebih baik: rata-rata accuracy sekitar 0,9788 untuk mtry = 2, 0,9722 untuk mtry = 3, dan 0,9690 untuk mtry = 5, dengan recall mencapai 1,0000 pada mtry = 2 saat ntree = 500 dan 1000. Pola ini sama dengan Random Forest.

Pada DRF, penambahan pohon justru sedikit membantu: rata-rata accuracy naik dari sekitar 0,9706 (200 pohon) ke 0,9739 (500 pohon) dan 0,9755 (1000 pohon), berbeda dengan Random Forest yang cenderung datar atau turun. Namun, selisihnya hanya satu atau dua pengamatan dari 204 data testing, sehingga tidak boleh dianggap perbedaan yang pasti. Waktu komputasi meningkat kira-kira sebanding dengan jumlah pohon dan sedikit bertambah pada mtry yang lebih besar. Gini importance pada seluruh model DRF konsisten menempatkan Cell.size, Cell.shape, dan Bare.nuclei sebagai tiga variabel teratas, serta Mitoses paling rendah.

4.6 Perbandingan Random Forest dengan Double Random Forest

4.6.1 Ukuran Evaluasi

# Gabungkan hasil RF dan DRF untuk 9 kombinasi parameter
perbandingan <- data.frame(
  ntree = hasil.grid$num.trees, mtry = hasil.grid$mtry,
  Acc_RF = hasil.grid$Accuracy,  Acc_DRF = hasil.drf$Accuracy,
  Rec_RF = hasil.grid$Recall,    Rec_DRF = hasil.drf$Recall,
  Spe_RF = hasil.grid$Specificity, Spe_DRF = hasil.drf$Specificity,
  F1_RF  = hasil.grid$F1_Score,  F1_DRF = hasil.drf$F1_Score)
perbandingan$Selisih_Acc <- round(perbandingan$Acc_DRF - perbandingan$Acc_RF, 4)
perbandingan
##   ntree mtry Acc_RF Acc_DRF Rec_RF Rec_DRF Spe_RF Spe_DRF  F1_RF F1_DRF
## 1   200    2 0.9804  0.9755 0.9859  0.9859 0.9774  0.9699 0.9722 0.9655
## 2   200    3 0.9706  0.9706 0.9577  0.9718 0.9774  0.9699 0.9577 0.9583
## 3   200    5 0.9608  0.9657 0.9437  0.9577 0.9699  0.9699 0.9437 0.9510
## 4   500    2 0.9755  0.9804 0.9859  1.0000 0.9699  0.9699 0.9655 0.9726
## 5   500    3 0.9706  0.9706 0.9577  0.9718 0.9774  0.9699 0.9577 0.9583
## 6   500    5 0.9608  0.9706 0.9437  0.9718 0.9699  0.9699 0.9437 0.9583
## 7  1000    2 0.9706  0.9804 0.9718  1.0000 0.9699  0.9699 0.9583 0.9726
## 8  1000    3 0.9657  0.9755 0.9577  0.9859 0.9699  0.9699 0.9510 0.9655
## 9  1000    5 0.9559  0.9706 0.9296  0.9718 0.9699  0.9699 0.9362 0.9583
##   Selisih_Acc
## 1     -0.0049
## 2      0.0000
## 3      0.0049
## 4      0.0049
## 5      0.0000
## 6      0.0098
## 7      0.0098
## 8      0.0098
## 9      0.0147
# Ringkasan: berapa kombinasi DRF lebih baik, sama, atau lebih buruk dari RF
table(factor(sign(perbandingan$Selisih_Acc), levels = c(1, 0, -1),
             labels = c("DRF lebih baik", "Sama", "RF lebih baik")))
## 
## DRF lebih baik           Sama  RF lebih baik 
##              6              2              1
# Rata-rata seluruh kombinasi
round(c(RF = mean(hasil.grid$Accuracy), DRF = mean(hasil.drf$Accuracy)), 4)
##     RF    DRF 
## 0.9679 0.9733
# Plot accuracy RF vs DRF
par(mfrow = c(1, 3))
for (mt in c(2, 3, 5)) {
  a <- subset(perbandingan, mtry == mt)
  plot(a$ntree, a$Acc_RF, type = "b", pch = 19, lwd = 2, col = "blue",
       ylim = range(c(perbandingan$Acc_RF, perbandingan$Acc_DRF)),
       xaxt = "n", xlab = "Jumlah pohon", ylab = "Accuracy (testing)",
       main = paste("mtry =", mt))
  axis(1, at = c(200, 500, 1000))
  lines(a$ntree, a$Acc_DRF, type = "b", pch = 17, lwd = 2, col = "red")
  legend("bottomright", legend = c("RF", "DRF"), col = c("blue", "red"),
         pch = c(19, 17), lwd = 2, cex = 0.8)
}

par(mfrow = c(1, 1))

Dari sembilan kombinasi parameter, DRF memiliki accuracy lebih tinggi pada enam kombinasi, sama pada dua kombinasi (mtry = 3 dengan 200 dan 500 pohon), dan lebih rendah hanya pada satu kombinasi (mtry = 2 dengan 200 pohon). Rata-rata accuracy seluruh kombinasi adalah sekitar 0,9733 untuk DRF dan 0,9679 untuk Random Forest. Keunggulan DRF paling jelas pada mtry = 5 dan jumlah pohon besar, misalnya pada 1000 pohon dengan mtry = 5, DRF mencapai 0,9706 sedangkan Random Forest 0,9559. Hal ini sejalan dengan gagasan Han et al. (2020) bahwa pohon yang dibangun dari seluruh data dan bootstrap pada setiap node dapat memberi prediksi lebih akurat ketika pohon Random Forest belum cukup besar. Meski begitu, selisih terbesar hanya sekitar 3 pengamatan dari 204, sehingga keunggulan DRF pada data ini bersifat tipis dan belum tentu bermakna secara statistik.

Dari sisi jenis kesalahan, DRF cenderung lebih baik pada recall (sampai 1,0000 pada mtry = 2), sedangkan Random Forest cenderung lebih baik pada specificity (0,9699 sampai 0,9774, dibanding DRF yang tetap 0,9699). Pada konteks diagnosis, recall yang tinggi lebih diutamakan karena pasien ganas yang terlewat berisiko lebih besar.

4.6.2 Ukuran Pohon, Waktu Komputasi, dan OOB Error

# RF (ranger dan randomForest) vs DRF pada ntree = 500, mtry = 3
set.seed(234)
t.rf <- system.time(
  rf.ranger <- ranger(Class ~ ., data = bc.train, num.trees = 500, mtry = 3,
                      min.node.size = 1, seed = 234))["elapsed"]

set.seed(234)
t.rfo <- system.time(
  rf.pkg <- randomForest::randomForest(x.train, y.train, ntree = 500, mtry = 3,
                                       nodesize = 1, keep.forest = TRUE))["elapsed"]

set.seed(234)
t.drf <- system.time(
  drf.pohon <- DRF.default(x = x.train, y = y.train, ntree = 500, mtry = 3,
                           nodesize = 1, keep.forest = TRUE))["elapsed"]

data.frame(
  Metode = c("RF (ranger)", "RF (randomForest)", "DRF"),
  Waktu_detik = round(c(t.rf, t.rfo, t.drf), 3),
  Rata2_node_per_pohon = c(NA, round(mean(rf.pkg$forest$ndbigtree), 1),
                           round(mean(drf.pohon$forest$ndbigtree), 1)),
  OOB_Error = c(round(rf.ranger$prediction.error, 4),
                round(rf.pkg$err.rate[500, "OOB"], 4), NA))
##              Metode Waktu_detik Rata2_node_per_pohon OOB_Error
## 1       RF (ranger)        0.05                   NA    0.0292
## 2 RF (randomForest)        0.65                 38.6    0.0271
## 3               DRF        1.56                 61.1        NA

Perbandingan ukuran pohon dilakukan dengan randomForest::randomForest() karena memakai mesin yang sama dengan DRF, sehingga selisihnya benar-benar mencerminkan perbedaan algoritma, bukan perbedaan perangkat lunak. Rata-rata jumlah node per pohon pada DRF (sekitar 61) jauh lebih besar dibanding Random Forest (sekitar 39). Temuan ini mendukung teori bahwa DRF menghasilkan pohon yang lebih besar karena setiap pohon tumbuh dari seluruh data training. Dari sisi waktu komputasi, DRF lebih lama dibanding randomForest pada pengaturan yang sama (kira-kira 1,5 sampai 2 kali pada percobaan ini, dengan angka pasti bergantung pada komputer), sedangkan ranger jauh lebih cepat karena diimplementasikan dalam C++ dan bukan karena algoritmanya berbeda. Untuk OOB error, Random Forest menghasilkan nilai sekitar 0,027 sampai 0,029, sedangkan DRF tidak memiliki nilai OOB (NaN) sebagaimana dijelaskan sebelumnya. Jadi, jika evaluasi internal tanpa data testing diperlukan, Random Forest memiliki keunggulan praktis.

4.7 Pemilihan Model Terbaik

# Gabungkan semua model (RF dan DRF) dan urutkan berdasarkan accuracy, lalu F1
semua <- rbind(
  data.frame(Metode = "RF",  ntree = hasil.grid$num.trees, mtry = hasil.grid$mtry,
             FP = hasil.grid$FP, FN = hasil.grid$FN, Accuracy = hasil.grid$Accuracy,
             Precision = hasil.grid$Precision, Recall = hasil.grid$Recall,
             Specificity = hasil.grid$Specificity, F1_Score = hasil.grid$F1_Score),
  data.frame(Metode = "DRF", ntree = hasil.drf$ntree, mtry = hasil.drf$mtry,
             FP = hasil.drf$FP, FN = hasil.drf$FN, Accuracy = hasil.drf$Accuracy,
             Precision = hasil.drf$Precision, Recall = hasil.drf$Recall,
             Specificity = hasil.drf$Specificity, F1_Score = hasil.drf$F1_Score))
semua <- semua[order(-semua$Accuracy, -semua$F1_Score), ]
head(semua, 6)
##    Metode ntree mtry FP FN Accuracy Precision Recall Specificity F1_Score
## 13    DRF   500    2  4  0   0.9804    0.9467 1.0000      0.9699   0.9726
## 16    DRF  1000    2  4  0   0.9804    0.9467 1.0000      0.9699   0.9726
## 1      RF   200    2  3  1   0.9804    0.9589 0.9859      0.9774   0.9722
## 4      RF   500    2  4  1   0.9755    0.9459 0.9859      0.9699   0.9655
## 10    DRF   200    2  4  1   0.9755    0.9459 0.9859      0.9699   0.9655
## 17    DRF  1000    3  4  1   0.9755    0.9459 0.9859      0.9699   0.9655

Dari 18 model yang dibandingkan, tiga model berbagi accuracy tertinggi 0,9804, yaitu Random Forest (num.trees = 200, mtry = 2) serta DRF (ntree = 500, mtry = 2) dan DRF (ntree = 1000, mtry = 2); ketiganya hanya salah pada 4 dari 204 data testing. Perbedaannya ada pada jenis kesalahan. Random Forest (200, 2) memiliki precision 0,9589, recall 0,9859, specificity 0,9774, dan F1-score 0,9722 (3 FP dan 1 FN). DRF (500, 2) memiliki precision 0,9467, recall 1,0000, specificity 0,9699, dan F1-score 0,9726 (4 FP dan 0 FN). Berdasarkan F1-score tertinggi dan recall sempurna, DRF (ntree = 500, mtry = 2) dipilih sebagai model terbaik secara keseluruhan karena tidak ada pasien ganas yang terlewat, sedangkan DRF dengan 1000 pohon memberi hasil sama tetapi lebih lama dihitung.

Namun, pilihan ini perlu dibaca hati-hati. Selisih F1-score antara kedua model hanya 0,0004, atau setara satu pengamatan, sehingga keduanya dapat dianggap setara. Random Forest (200, 2) tetap menarik karena lebih sederhana, jauh lebih cepat, dan memiliki OOB error sebagai evaluasi internal. Selain itu, model dipilih berdasarkan data testing yang sama dengan yang dipakai menilai sehingga hasilnya cenderung sedikit optimis; pemilihan parameter yang lebih ketat sebaiknya memakai cross-validation.

BAB V KESIMPULAN

5.1 Kesimpulan

Random Forest dan Double Random Forest merupakan metode ensemble berbasis decision tree yang menggabungkan banyak pohon untuk menurunkan variance dan meningkatkan kemampuan generalisasi. Random Forest adalah pengembangan dari bagging yang membentuk setiap pohon dari sampel bootstrap dan mengacak mtry prediktor pada setiap node untuk mengurangi korelasi antar-pohon, dengan prediksi akhir berupa majority vote pada klasifikasi dan rata-rata pada regresi. Double Random Forest mengembangkan gagasan tersebut dengan membangun setiap pohon dari seluruh data latih dan memakai bootstrap hanya pada setiap node untuk mencari split, sehingga pohonnya cenderung lebih besar dan beragam tetapi waktu komputasinya lebih lama.

Implementasi Random Forest di R dilakukan dengan fungsi ranger() dari package ranger dengan mengatur parameter seperti num.trees, mtry, min.node.size, replace, sample.fraction, dan importance. Double Random Forest belum tersedia sebagai package sehingga implementasinya dilakukan dengan mengunduh dan mengompilasi kode sumber dari Han et al. (2020) lalu memakai fungsi DRF.default(), yang membutuhkan Rtools atau compiler yang sesuai. Tahapan yang dijalankan meliputi persiapan data, pembagian data training dan testing dengan set.seed(234), pembentukan model, prediksi, evaluasi, dan visualisasi untuk klasifikasi pada dataset BreastCancer, sedangkan regresi dibahas secara konsep karena sintaks yang tersedia hanya untuk klasifikasi.

Performa model dievaluasi dengan confusion matrix untuk menghitung accuracy, precision, recall, specificity, dan F1-score pada data testing, serta OOB error sebagai evaluasi internal yang diperoleh dari observasi yang tidak ikut membentuk pohon tertentu (sekitar 36,8% data). Pada Random Forest dasar, OOB error (0,0292) sangat dekat dengan error data testing (0,0294), sehingga dapat dijadikan perkiraan performa tanpa menyisihkan data khusus. Sebaliknya, DRF tidak memiliki OOB error (bernilai NaN) karena setiap pohonnya memakai seluruh data training, sehingga hanya dapat dievaluasi dengan data testing. Dibandingkan dengan Random Forest, DRF menghasilkan pohon yang lebih besar (sekitar 61 node dibanding 39 per pohon) dengan waktu komputasi lebih lama, dan accuracy yang sedikit lebih tinggi pada sebagian besar kombinasi parameter.

Berdasarkan hasil praktikum pada 683 observasi BreastCancer (479 data training dan 204 data testing), sembilan model Random Forest menghasilkan accuracy antara 0,9559 sampai 0,9804 dengan OOB error antara 0,0271 sampai 0,0292, dan sembilan model DRF menghasilkan accuracy antara 0,9657 sampai 0,9804. mtry yang lebih kecil cenderung lebih baik pada kedua metode, sedangkan jumlah pohon hampir tidak memperbaiki Random Forest tetapi sedikit membantu DRF. DRF unggul pada enam dari sembilan kombinasi (rata-rata accuracy sekitar 0,9733 dibanding 0,9679). Model terbaik adalah DRF dengan ntree = 500 dan mtry = 2 (accuracy 0,9804, precision 0,9467, recall 1,0000, specificity 0,9699, F1-score 0,9726), meskipun Random Forest dengan 200 pohon dan mtry = 2 hampir setara (F1-score 0,9722). Cell.size, Cell.shape, dan Bare.nuclei konsisten menjadi tiga variabel terpenting, sedangkan Mitoses paling kecil pengaruhnya, dengan catatan bahwa selisih antarmodel hanya berkisar satu sampai beberapa pengamatan sehingga perlu divalidasi lebih lanjut.

5.2 Saran

Praktikan diharapkan dapat melanjutkan praktikum dengan menjalankan Random Forest dan Double Random Forest untuk regresi, lalu membandingkan keduanya dari sisi akurasi dan waktu komputasi. Karena selisih performa antarmodel pada praktikum ini hanya satu sampai beberapa pengamatan, pemilihan mtry dan jumlah pohon sebaiknya dilakukan dengan cross-validation atau pembagian data berulang agar tidak terlalu bergantung pada satu pembagian data testing. Praktikan juga disarankan mencoba ukuran lain seperti ROC-AUC, mengatur min.node.size atau nodesize, serta mencoba data lain yang pohon Random Forest-nya lebih kecil agar keunggulan DRF dapat diuji lebih jelas.

Daftar Pustaka

Darwanto, A. R. S., Viarindita, T. L., dan Widyaningsih, Y. (2021). Analisis regresi logistik binomial dan algoritma Random Forest pada proses pengklasifikasian penyakit ginjal kronis. Jurnal Statistika dan Aplikasinya, 5(1), 1–14. doi:10.21009/JSA.05101.

Fransiska, H. (2026). Modul Praktikum Machine Learning dan Model Prediction. Universitas Bengkulu: Bengkulu.

Han, S., Kim, H., dan Lee, Y. S. (2020). Double random forest. Machine Learning, 109, 1569–1586. doi:10.1007/s10994-020-05889-1.

James, G., Witten, D., Hastie, T., dan Tibshirani, R. (2021). An Introduction to Statistical Learning: With Applications in R (edisi ke-2). Springer.

Wright, M. N., dan Ziegler, A. (2017). ranger: A fast implementation of random forests for high dimensional data in C++ and R. Journal of Statistical Software, 77(1), 1–17. doi:10.18637/jss.v077i01.