Variabel - Y: Harga rumah (juta rupiah). - X1: Luas rumah (m²). - X2: Jarak ke pusat kota (km).

Rumah baru yang akan diprediksi memiliki luas 85 m² dan berjarak 7,5 km dari pusat kota.

1. Persiapan

Jika package belum terpasang, jalankan install.packages() satu kali di Console.

# install.packages(c("rpart", "rpart.plot", "randomForest", "ggplot2", "tidyr"))

library(rpart)
library(rpart.plot)
library(randomForest)
library(ggplot2)
library(tidyr)

2. Data Harga Rumah

data_rumah <- data.frame(
  x1 = c(40, 55, 60, 70, 80, 90, 100),
  x2 = c(14, 12, 10, 9, 8, 7, 6),
  y  = c(320, 360, 400, 420, 460, 510, 540)
)

data_rumah
##    x1 x2   y
## 1  40 14 320
## 2  55 12 360
## 3  60 10 400
## 4  70  9 420
## 5  80  8 460
## 6  90  7 510
## 7 100  6 540
# Rumah yang akan diprediksi
rumah_baru <- data.frame(
  x1 = 85,
  x2 = 7.5
)

rumah_baru
##   x1  x2
## 1 85 7.5

3. Decision Tree Regression

model_tree <- rpart(
  y ~ x1 + x2,
  data = data_rumah,
  method = "anova",
  control = rpart.control(
    cp = 0,
    minsplit = 2,
    minbucket = 1,
    maxdepth = 5,
    xval = 0
  )
)

model_tree
## n= 7 
## 
## node), split, n, deviance, yval
##       * denotes terminal node
## 
##  1) root 7 37400.000 430.0000  
##    2) x1< 75 4  5900.000 375.0000  
##      4) x1< 57.5 2   800.000 340.0000  
##        8) x1< 47.5 1     0.000 320.0000 *
##        9) x1>=47.5 1     0.000 360.0000 *
##      5) x1>=57.5 2   200.000 410.0000  
##       10) x1< 65 1     0.000 400.0000 *
##       11) x1>=65 1     0.000 420.0000 *
##    3) x1>=75 3  3266.667 503.3333  
##      6) x2>=7.5 1     0.000 460.0000 *
##      7) x2< 7.5 2   450.000 525.0000  
##       14) x1< 95 1     0.000 510.0000 *
##       15) x1>=95 1     0.000 540.0000 *
# Prediksi rumah baru
pred_tree <- predict(model_tree, newdata = rumah_baru)

cat(
  "\nPrediksi Decision Tree:",
  round(pred_tree, 2), "juta rupiah\n"
)
## 
## Prediksi Decision Tree: 460 juta rupiah

3.1 Visualisasi Pohon Decision Tree

rpart.plot(
  model_tree,
  type = 2,
  extra = 101,
  fallen.leaves = TRUE,
  box.palette = "Blues",
  shadow.col = "gray",
  nn = TRUE,
  main = "Decision Tree Regression Harga Rumah"
)

3.2 Grafik Aktual dan Prediksi Decision Tree

prediksi_semua_tree <- predict(model_tree, newdata = data_rumah)

data_grafik_tree <- data.frame(
  index = seq_len(nrow(data_rumah)),
  Aktual = data_rumah$y,
  Prediksi = as.numeric(prediksi_semua_tree)
)

data_grafik_tree_long <- tidyr::pivot_longer(
  data_grafik_tree,
  cols = c(Aktual, Prediksi),
  names_to = "Keterangan",
  values_to = "Harga"
)

ggplot(
  data_grafik_tree_long,
  aes(
    x = index,
    y = Harga,
    color = Keterangan,
    group = Keterangan
  )
) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 2.5, color = "black") +
  scale_color_manual(
    values = c(
      "Aktual" = "#FF6666",
      "Prediksi" = "#00BFC4"
    )
  ) +
  scale_x_continuous(breaks = seq_len(nrow(data_rumah))) +
  labs(
    title = "Decision Tree Regression",
    x = "Index",
    y = "Harga Rumah (juta rupiah)",
    color = "Keterangan"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(hjust = 0, face = "bold"),
    legend.position = "right"
  )

4. Random Forest Regression dengan Dua Pohon

set.seed(123)

model_rf <- randomForest(
  y ~ x1 + x2,
  data = data_rumah,
  ntree = 2,
  mtry = 2,
  nodesize = 1,
  keep.forest = TRUE,
  importance = TRUE
)

model_rf
## 
## Call:
##  randomForest(formula = y ~ x1 + x2, data = data_rumah, ntree = 2,      mtry = 2, nodesize = 1, keep.forest = TRUE, importance = TRUE) 
##                Type of random forest: regression
##                      Number of trees: 2
## No. of variables tried at each split: 2
## 
##           Mean of squared residuals: 841.6667
##                     % Var explained: 84.25
# Prediksi rumah baru
pred_rf <- predict(model_rf, newdata = rumah_baru)

cat(
  "Prediksi Random Forest:",
  round(pred_rf, 2), "juta rupiah\n"
)
## Prediksi Random Forest: 465 juta rupiah
# Struktur kedua pohon
pohon_1 <- getTree(model_rf, k = 1, labelVar = TRUE)
pohon_2 <- getTree(model_rf, k = 2, labelVar = TRUE)

cat("\nStruktur Pohon 1:\n")
## 
## Struktur Pohon 1:
print(pohon_1)
##   left daughter right daughter split var split point status prediction
## 1             2              3        x2         8.0     -3   447.1429
## 2             4              5        x1        95.0     -3   530.0000
## 3             6              7        x1        50.0     -3   385.0000
## 4             0              0      <NA>         0.0     -1   510.0000
## 5             0              0      <NA>         0.0     -1   540.0000
## 6             0              0      <NA>         0.0     -1   320.0000
## 7             8              9        x2         9.5     -3   406.6667
## 8             0              0      <NA>         0.0     -1   420.0000
## 9             0              0      <NA>         0.0     -1   400.0000
cat("\nStruktur Pohon 2:\n")
## 
## Struktur Pohon 2:
print(pohon_2)
##   left daughter right daughter split var split point status prediction
## 1             2              3        x1        85.0     -3   394.2857
## 2             4              5        x1        57.5     -3   370.0000
## 3             0              0      <NA>         0.0     -1   540.0000
## 4             6              7        x2        13.0     -3   350.0000
## 5             8              9        x1        65.0     -3   410.0000
## 6             0              0      <NA>         0.0     -1   360.0000
## 7             0              0      <NA>         0.0     -1   320.0000
## 8             0              0      <NA>         0.0     -1   400.0000
## 9             0              0      <NA>         0.0     -1   420.0000

4.1 Fungsi Visualisasi Struktur Pohon Random Forest

Fungsi ini menggunakan hubungan node anak kiri dan kanan dari getTree(). Node pemisah menampilkan variabel dan titik pemisahan; node daun menampilkan nilai prediksi. Diagram ini adalah visualisasi struktur pohon hasil randomForest.

gambar_pohon_rf <- function(pohon, nomor_pohon) {

  jumlah_node <- nrow(pohon)

  cari_anak <- function(node, arah) {
    kolom <- if (arah == "kiri") "left daughter" else "right daughter"
    anak <- as.integer(pohon[node, kolom])

    if (is.na(anak) || anak == 0) {
      return(NA_integer_)
    }
    anak
  }

  hitung_daun <- function(node) {
    kiri <- cari_anak(node, "kiri")
    kanan <- cari_anak(node, "kanan")

    if (is.na(kiri) && is.na(kanan)) return(1)

    hasil <- 0
    if (!is.na(kiri)) hasil <- hasil + hitung_daun(kiri)
    if (!is.na(kanan)) hasil <- hasil + hitung_daun(kanan)
    hasil
  }

  posisi <- data.frame(
    node = seq_len(jumlah_node),
    x = rep(NA_real_, jumlah_node),
    y = rep(NA_real_, jumlah_node)
  )

  tempatkan <- function(node, batas_kiri, batas_kanan, level = 1) {
    posisi$x[node] <<- (batas_kiri + batas_kanan) / 2
    posisi$y[node] <<- -level

    anak_kiri <- cari_anak(node, "kiri")
    anak_kanan <- cari_anak(node, "kanan")

    if (!is.na(anak_kiri) && !is.na(anak_kanan)) {
      daun_kiri <- hitung_daun(anak_kiri)
      daun_kanan <- hitung_daun(anak_kanan)

      batas_tengah <- batas_kiri +
        (batas_kanan - batas_kiri) *
        daun_kiri / (daun_kiri + daun_kanan)

      tempatkan(anak_kiri, batas_kiri, batas_tengah, level + 1)
      tempatkan(anak_kanan, batas_tengah, batas_kanan, level + 1)
    } else if (!is.na(anak_kiri)) {
      tempatkan(anak_kiri, batas_kiri, batas_kanan, level + 1)
    } else if (!is.na(anak_kanan)) {
      tempatkan(anak_kanan, batas_kiri, batas_kanan, level + 1)
    }
  }

  tempatkan(1, 0, hitung_daun(1))

  jumlah_daun <- hitung_daun(1)
  kedalaman <- max(abs(posisi$y), na.rm = TRUE)

  plot(
    NA,
    xlim = c(-0.5, jumlah_daun + 0.5),
    ylim = c(-kedalaman - 0.7, 0.7),
    xaxt = "n",
    yaxt = "n",
    xlab = "",
    ylab = "",
    main = paste("Random Forest - Pohon", nomor_pohon),
    bty = "n"
  )

  # Garis cabang
  for (i in seq_len(jumlah_node)) {
    anak_kiri <- cari_anak(i, "kiri")
    anak_kanan <- cari_anak(i, "kanan")

    if (!is.na(anak_kiri)) {
      segments(
        posisi$x[i], posisi$y[i] - 0.12,
        posisi$x[anak_kiri], posisi$y[anak_kiri] + 0.12
      )
      text(
        (posisi$x[i] + posisi$x[anak_kiri]) / 2,
        (posisi$y[i] + posisi$y[anak_kiri]) / 2,
        labels = "<=",
        cex = 0.65,
        pos = 2
      )
    }

    if (!is.na(anak_kanan)) {
      segments(
        posisi$x[i], posisi$y[i] - 0.12,
        posisi$x[anak_kanan], posisi$y[anak_kanan] + 0.12
      )
      text(
        (posisi$x[i] + posisi$x[anak_kanan]) / 2,
        (posisi$y[i] + posisi$y[anak_kanan]) / 2,
        labels = ">",
        cex = 0.65,
        pos = 4
      )
    }
  }

  # Kotak dan label node
  for (i in seq_len(jumlah_node)) {
    variabel <- as.character(pohon[i, "split var"])
    is_daun <- is.na(variabel) ||
      variabel == "<NA>" ||
      variabel == "<leaf>"

    if (is_daun) {
      label <- paste0(
        "Node ", i, "\nPrediksi = ",
        round(pohon[i, "prediction"], 2)
      )
      warna <- "lightblue"
    } else {
      label <- paste0(
        "Node ", i, "\n",
        variabel, " <= ",
        round(pohon[i, "split point"], 2)
      )
      warna <- "lightyellow"
    }

    rect(
      posisi$x[i] - 0.38,
      posisi$y[i] - 0.2,
      posisi$x[i] + 0.38,
      posisi$y[i] + 0.2,
      col = warna,
      border = "gray40"
    )

    text(
      posisi$x[i],
      posisi$y[i],
      labels = label,
      cex = 0.65
    )
  }
}

4.2 Visualisasi Pohon Random Forest 1 dan 2

par(mfrow = c(1, 2), mar = c(1, 1, 3, 1))

gambar_pohon_rf(pohon_1, 1)
gambar_pohon_rf(pohon_2, 2)

par(mfrow = c(1, 1))

4.3 Grafik Aktual dan Prediksi Random Forest

prediksi_semua_rf <- predict(model_rf, newdata = data_rumah)

data_grafik_rf <- data.frame(
  index = seq_len(nrow(data_rumah)),
  Aktual = data_rumah$y,
  Prediksi = as.numeric(prediksi_semua_rf)
)

data_grafik_rf_long <- tidyr::pivot_longer(
  data_grafik_rf,
  cols = c(Aktual, Prediksi),
  names_to = "Keterangan",
  values_to = "Harga"
)

ggplot(
  data_grafik_rf_long,
  aes(
    x = index,
    y = Harga,
    color = Keterangan,
    group = Keterangan
  )
) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 2.5, color = "black") +
  scale_color_manual(
    values = c(
      "Aktual" = "#FF6666",
      "Prediksi" = "#00BFC4"
    )
  ) +
  scale_x_continuous(breaks = seq_len(nrow(data_rumah))) +
  labs(
    title = "Random Forest Regresi",
    x = "Index",
    y = "Harga Rumah (juta rupiah)",
    color = "Keterangan"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(hjust = 0, face = "bold"),
    legend.position = "right"
  )

5. Tabel Perbandingan Prediksi Rumah Baru

hasil_prediksi <- data.frame(
  Metode = c("Decision Tree", "Random Forest (2 Pohon)"),
  Prediksi_juta = c(
    as.numeric(pred_tree),
    as.numeric(pred_rf)
  )
)

cat("\nTabel prediksi rumah baru:\n")
## 
## Tabel prediksi rumah baru:
print(hasil_prediksi)
##                    Metode Prediksi_juta
## 1           Decision Tree           460
## 2 Random Forest (2 Pohon)           465

6. Grafik Perbandingan Harga Aktual dan Kedua Model

data_grafik <- data.frame(
  Index = seq_len(nrow(data_rumah)),
  Aktual = data_rumah$y,
  Decision_Tree = as.numeric(prediksi_semua_tree),
  Random_Forest = as.numeric(prediksi_semua_rf)
)

data_grafik_long <- tidyr::pivot_longer(
  data_grafik,
  cols = c(Aktual, Decision_Tree, Random_Forest),
  names_to = "Metode",
  values_to = "Harga"
)

data_grafik_long$Metode <- factor(
  data_grafik_long$Metode,
  levels = c("Aktual", "Decision_Tree", "Random_Forest"),
  labels = c("Aktual", "Decision Tree", "Random Forest")
)

ggplot(
  data_grafik_long,
  aes(
    x = Index,
    y = Harga,
    color = Metode,
    group = Metode
  )
) +
  # Garis prediksi digambar terlebih dahulu
  geom_line(
    data = subset(data_grafik_long, Metode == "Decision Tree"),
    linewidth = 1.1
  ) +
  geom_line(
    data = subset(data_grafik_long, Metode == "Random Forest"),
    linewidth = 1.1
  ) +
  # Garis aktual digambar terakhir agar terlihat
  geom_line(
    data = subset(data_grafik_long, Metode == "Aktual"),
    linewidth = 1.3
  ) +
  geom_point(size = 2.5) +
  scale_color_manual(
    values = c(
      "Aktual" = "#FF6666",
      "Decision Tree" = "#00A6B2",
      "Random Forest" = "#3366CC"
    )
  ) +
  scale_x_continuous(breaks = seq_len(nrow(data_rumah))) +
  labs(
    title = "Perbandingan Harga Rumah Aktual dan Prediksi",
    x = "Indeks Observasi",
    y = "Harga Rumah (juta rupiah)",
    color = "Keterangan"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    legend.position = "right"
  )

7. Grafik Perbandingan Prediksi Rumah Baru

data_prediksi_baru <- data.frame(
  Metode = c("Decision Tree", "Random Forest"),
  Harga = c(
    as.numeric(pred_tree),
    as.numeric(pred_rf)
  )
)

ggplot(
  data_prediksi_baru,
  aes(x = Metode, y = Harga, fill = Metode)
) +
  geom_col(width = 0.55) +
  geom_text(
    aes(label = round(Harga, 2)),
    vjust = -0.5,
    size = 4
  ) +
  labs(
    title = "Prediksi Harga Rumah Baru",
    subtitle = "Luas = 85 m² dan Jarak ke Pusat Kota = 7,5 km",
    x = "Metode",
    y = "Prediksi Harga (juta rupiah)"
  ) +
  scale_fill_manual(
    values = c(
      "Decision Tree" = "#00A6B2",
      "Random Forest" = "#3366CC"
    )
  ) +
  expand_limits(y = max(data_prediksi_baru$Harga) * 1.1) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    legend.position = "none"
  )

8. Kesimpulan

Hasil prediksi rumah baru dari Decision Tree dan Random Forest ditampilkan pada tabel di Bagian 5 dan diagram batang di Bagian 7. Grafik pada Bagian 3.2 dan 4.3 membandingkan harga aktual dengan prediksi masing-masing model, sedangkan grafik Bagian 6 membandingkan ketiganya secara bersamaan.