1 Pendahuluan

Analisis ini menggunakan pendekatan CRISP-DM untuk menyelesaikan studi kasus prediksi status akademik mahasiswa.

Dataset yang digunakan adalah Prediksi Status Akademik Mahasiswa dari Kaggle.
Dataset ini dipilih karena konteksnya mudah dipahami, yaitu berkaitan dengan aktivitas dan performa akademik mahasiswa.

Metode yang digunakan adalah Decision Tree Classification karena target yang diprediksi berupa kategori, yaitu status akademik mahasiswa.

2 Pemahaman Masalah

Permasalahan yang dianalisis adalah bagaimana membangun model yang dapat memprediksi status akademik mahasiswa berdasarkan data aktivitas dan performa akademiknya.

Atribut dalam dataset berkaitan dengan kegiatan akademik mahasiswa, seperti kehadiran, partisipasi diskusi, nilai tugas, aktivitas e-learning, dan IPK.

Karena target yang diprediksi berupa kelas atau kategori, maka metode yang digunakan adalah klasifikasi.
Algoritma yang dipilih adalah Decision Tree karena hasilnya mudah dipahami dan dapat divisualisasikan dalam bentuk pohon keputusan.

Tujuan analisis ini adalah:

  1. Memahami karakteristik dataset status akademik mahasiswa.
  2. Melakukan preprocessing data.
  3. Membangun model klasifikasi menggunakan Decision Tree.
  4. Membuat visualisasi pohon keputusan.
  5. Mengevaluasi hasil model menggunakan confusion matrix dan metrik evaluasi.
  6. Menarik kesimpulan berdasarkan hasil analisis.

3 Pemahaman Data

3.1 Sumber Data

Dataset diperoleh dari Kaggle dengan judul Prediksi Status Akademik Mahasiswa.

Link dataset:

https://www.kaggle.com/datasets/einherjar3451234123/prediksi-status-akademik-mahasiswa

File dataset diunduh terlebih dahulu dari Kaggle.
Setelah itu, file Excel disimpan dalam folder yang sama dengan file R Markdown ini.

3.2 Membaca Dataset

file_dataset <- file.choose()

file_dataset
## [1] "C:\\Users\\A S U S.LAPTOP-Q1GO7RFS\\Downloads\\Dataset_Mahasiswa_Kehadiran_Aktivitas_IPK.xlsx"
ext_file <- tools::file_ext(file_dataset)

if (ext_file %in% c("xlsx", "xls")) {
  data_mahasiswa <- readxl::read_excel(file_dataset)
} else if (ext_file == "csv") {
  data_mahasiswa <- read.csv(file_dataset)
} else {
  stop("Format file belum didukung. Gunakan file .xlsx, .xls, atau .csv")
}

head(data_mahasiswa)
## # A tibble: 6 × 10
##   Nama          `Jenis Kelamin`  Umur `Status Menikah` `Kehadiran (%)`
##   <chr>         <chr>           <dbl> <chr>                      <dbl>
## 1 Kiki Ananda   Laki-laki          20 Belum Menikah                 73
## 2 Putri Fauzi   Laki-laki          21 Belum Menikah                 42
## 3 Mira Hidayat  Laki-laki          25 Belum Menikah                 71
## 4 Mira Ananda   Laki-laki          23 Belum Menikah                 44
## 5 Mira Septiani Laki-laki          21 Menikah                       54
## 6 Eka Hidayat   Perempuan          20 Menikah                       53
## # ℹ 5 more variables: `Partisipasi Diskusi (skor)` <dbl>,
## #   `Nilai Tugas (rata-rata)` <dbl>, `Aktivitas E-Learning (skor)` <dbl>,
## #   IPK <dbl>, `Status Akademik` <chr>

Kode di atas digunakan untuk membaca file dataset secara langsung dari lokasi penyimpanan di laptop.
Saat proses Knit dijalankan, R akan membuka pilihan file. Pilih file dataset yang sudah diunduh dari Kaggle.

3.3 Merapikan Nama Kolom

clean_names <- function(x) {
  x <- tolower(x)
  x <- gsub("[^a-z0-9]+", "_", x)
  x <- gsub("^_|_$", "", x)
  return(x)
}

names(data_mahasiswa) <- clean_names(names(data_mahasiswa))

names(data_mahasiswa)
##  [1] "nama"                      "jenis_kelamin"            
##  [3] "umur"                      "status_menikah"           
##  [5] "kehadiran"                 "partisipasi_diskusi_skor" 
##  [7] "nilai_tugas_rata_rata"     "aktivitas_e_learning_skor"
##  [9] "ipk"                       "status_akademik"

Nama kolom dirapikan agar lebih mudah dipanggil dalam proses analisis.
Huruf dibuat kecil dan spasi diganti menjadi garis bawah.

3.4 Struktur Data

str(data_mahasiswa)
## tibble [500 × 10] (S3: tbl_df/tbl/data.frame)
##  $ nama                     : chr [1:500] "Kiki Ananda" "Putri Fauzi" "Mira Hidayat" "Mira Ananda" ...
##  $ jenis_kelamin            : chr [1:500] "Laki-laki" "Laki-laki" "Laki-laki" "Laki-laki" ...
##  $ umur                     : num [1:500] 20 21 25 23 21 20 24 19 20 18 ...
##  $ status_menikah           : chr [1:500] "Belum Menikah" "Belum Menikah" "Belum Menikah" "Belum Menikah" ...
##  $ kehadiran                : num [1:500] 73 42 71 44 54 53 64 57 77 73 ...
##  $ partisipasi_diskusi_skor : num [1:500] 99 83 61 88 92 71 59 60 100 62 ...
##  $ nilai_tugas_rata_rata    : num [1:500] 91 53 87 62 67 92 86 84 99 84 ...
##  $ aktivitas_e_learning_skor: num [1:500] 88 64 54 81 64 49 71 65 69 52 ...
##  $ ipk                      : num [1:500] 3.77 3.55 2.73 3.59 3.02 1.52 2.93 1.66 3.87 2.37 ...
##  $ status_akademik          : chr [1:500] "Lulus" "Lulus" "Lulus" "Tidak" ...

3.5 Jumlah Data

dim(data_mahasiswa)
## [1] 500  10

3.6 Ringkasan Data

summary(data_mahasiswa)
##      nama           jenis_kelamin           umur       status_menikah    
##  Length:500         Length:500         Min.   :18.00   Length:500        
##  Class :character   Class :character   1st Qu.:20.00   Class :character  
##  Mode  :character   Mode  :character   Median :22.00   Mode  :character  
##                                        Mean   :21.54                     
##                                        3rd Qu.:23.00                     
##                                        Max.   :25.00                     
##    kehadiran      partisipasi_diskusi_skor nilai_tugas_rata_rata
##  Min.   : 40.00   Min.   : 40.00           Min.   : 40.00       
##  1st Qu.: 54.00   1st Qu.: 55.00           1st Qu.: 55.00       
##  Median : 69.00   Median : 69.50           Median : 69.00       
##  Mean   : 69.43   Mean   : 69.62           Mean   : 70.18       
##  3rd Qu.: 85.00   3rd Qu.: 84.00           3rd Qu.: 85.00       
##  Max.   :100.00   Max.   :100.00           Max.   :100.00       
##  aktivitas_e_learning_skor      ipk        status_akademik   
##  Min.   : 40.00            Min.   :1.500   Length:500        
##  1st Qu.: 54.00            1st Qu.:2.185   Class :character  
##  Median : 69.50            Median :2.815   Mode  :character  
##  Mean   : 69.48            Mean   :2.806                     
##  3rd Qu.: 85.00            3rd Qu.:3.422                     
##  Max.   :100.00            Max.   :3.990

4 Preprocessing Data

4.1 Mengecek Missing Value

colSums(is.na(data_mahasiswa))
##                      nama             jenis_kelamin                      umur 
##                         0                         0                         0 
##            status_menikah                 kehadiran  partisipasi_diskusi_skor 
##                         0                         0                         0 
##     nilai_tugas_rata_rata aktivitas_e_learning_skor                       ipk 
##                         0                         0                         0 
##           status_akademik 
##                         0

Missing value perlu diperiksa karena data kosong dapat mengganggu proses pembangunan model.
Jika terdapat missing value, data tersebut akan dihapus agar model dibangun menggunakan data yang lengkap.

4.2 Menghapus Missing Value

data_clean <- na.omit(data_mahasiswa)

dim(data_clean)
## [1] 500  10

4.3 Mengecek Informasi Kolom

info_kolom <- data.frame(
  Nama_Kolom = names(data_clean),
  Tipe_Data = sapply(data_clean, function(x) class(x)[1]),
  Jumlah_Nilai_Unik = sapply(data_clean, function(x) length(unique(x)))
)

info_kolom
##                                          Nama_Kolom Tipe_Data Jumlah_Nilai_Unik
## nama                                           nama character               278
## jenis_kelamin                         jenis_kelamin character                 2
## umur                                           umur   numeric                 8
## status_menikah                       status_menikah character                 2
## kehadiran                                 kehadiran   numeric                61
## partisipasi_diskusi_skor   partisipasi_diskusi_skor   numeric                61
## nilai_tugas_rata_rata         nilai_tugas_rata_rata   numeric                61
## aktivitas_e_learning_skor aktivitas_e_learning_skor   numeric                61
## ipk                                             ipk   numeric               213
## status_akademik                     status_akademik character                 2

Tabel di atas digunakan untuk melihat nama kolom, tipe data, dan jumlah nilai unik pada setiap kolom.
Kolom target klasifikasi biasanya memiliki jumlah nilai unik yang sedikit, misalnya Lulus dan Tidak.

4.4 Menentukan Kolom Target Secara Otomatis

cek_target <- function(x) {
  nilai <- unique(na.omit(tolower(trimws(as.character(x)))))
  
  kondisi_jumlah <- length(nilai) >= 2 && length(nilai) <= 10
  kondisi_isi <- any(grepl("lulus|tidak|gagal|berhasil|aktif|nonaktif|risiko|aman", nilai))
  
  return(kondisi_jumlah && kondisi_isi)
}

target_candidates <- names(data_clean)[sapply(data_clean, cek_target)]

target_candidates
## [1] "status_akademik"
if (length(target_candidates) == 0) {
  stop(
    paste(
      "Kolom target tidak ditemukan secara otomatis. Cek nama kolom berikut:",
      paste(names(data_clean), collapse = ", ")
    )
  )
}

target_col <- target_candidates[1]

target_col
## [1] "status_akademik"

Kolom target ditentukan berdasarkan isi kolom yang memiliki kategori seperti Lulus dan Tidak.
Kolom ini akan digunakan sebagai kelas yang diprediksi oleh model Decision Tree.

4.5 Menghapus Kolom Identitas

kolom_identitas <- names(data_clean)[
  grepl("nama|nim|id|no|nomor|mahasiswa_id|student_id", names(data_clean))
]

kolom_identitas <- setdiff(kolom_identitas, target_col)

kolom_identitas
## [1] "nama"
data_model <- data_clean %>%
  select(-any_of(kolom_identitas))

head(data_model)
## # A tibble: 6 × 9
##   jenis_kelamin  umur status_menikah kehadiran partisipasi_diskusi_skor
##   <chr>         <dbl> <chr>              <dbl>                    <dbl>
## 1 Laki-laki        20 Belum Menikah         73                       99
## 2 Laki-laki        21 Belum Menikah         42                       83
## 3 Laki-laki        25 Belum Menikah         71                       61
## 4 Laki-laki        23 Belum Menikah         44                       88
## 5 Laki-laki        21 Menikah               54                       92
## 6 Perempuan        20 Menikah               53                       71
## # ℹ 4 more variables: nilai_tugas_rata_rata <dbl>,
## #   aktivitas_e_learning_skor <dbl>, ipk <dbl>, status_akademik <chr>

Kolom identitas seperti nama, NIM, ID, nomor, atau kode mahasiswa tidak digunakan dalam model.
Kolom tersebut hanya berfungsi sebagai identitas data dan tidak diperlukan dalam proses klasifikasi.

4.6 Menghapus Kolom yang Terlalu Unik

kolom_terlalu_unik <- names(data_model)[
  sapply(data_model, function(x) length(unique(x)) > 0.9 * nrow(data_model))
]

kolom_terlalu_unik <- setdiff(kolom_terlalu_unik, target_col)

kolom_terlalu_unik
## character(0)
data_model <- data_model %>%
  select(-any_of(kolom_terlalu_unik))

names(data_model)
## [1] "jenis_kelamin"             "umur"                     
## [3] "status_menikah"            "kehadiran"                
## [5] "partisipasi_diskusi_skor"  "nilai_tugas_rata_rata"    
## [7] "aktivitas_e_learning_skor" "ipk"                      
## [9] "status_akademik"

Kolom yang hampir semua nilainya berbeda tidak digunakan dalam model karena biasanya tidak membantu membentuk pola klasifikasi.
Contohnya adalah kolom ID atau nomor unik.

4.7 Mengubah Target Menjadi Faktor

data_model[[target_col]] <- as.factor(data_model[[target_col]])

levels(data_model[[target_col]])
## [1] "Lulus" "Tidak"
jumlah_kelas_target <- nlevels(data_model[[target_col]])

jumlah_kelas_target
## [1] 2

Target diubah menjadi faktor karena Decision Tree digunakan untuk klasifikasi.
Jika jumlah kelas target sedikit, maka target tersebut sesuai untuk model klasifikasi.

4.8 Mengubah Kolom Karakter Menjadi Faktor

data_model <- data_model %>%
  mutate(across(where(is.character), as.factor))

str(data_model)
## tibble [500 × 9] (S3: tbl_df/tbl/data.frame)
##  $ jenis_kelamin            : Factor w/ 2 levels "Laki-laki","Perempuan": 1 1 1 1 1 2 1 2 2 2 ...
##  $ umur                     : num [1:500] 20 21 25 23 21 20 24 19 20 18 ...
##  $ status_menikah           : Factor w/ 2 levels "Belum Menikah",..: 1 1 1 1 2 2 1 1 1 1 ...
##  $ kehadiran                : num [1:500] 73 42 71 44 54 53 64 57 77 73 ...
##  $ partisipasi_diskusi_skor : num [1:500] 99 83 61 88 92 71 59 60 100 62 ...
##  $ nilai_tugas_rata_rata    : num [1:500] 91 53 87 62 67 92 86 84 99 84 ...
##  $ aktivitas_e_learning_skor: num [1:500] 88 64 54 81 64 49 71 65 69 52 ...
##  $ ipk                      : num [1:500] 3.77 3.55 2.73 3.59 3.02 1.52 2.93 1.66 3.87 2.37 ...
##  $ status_akademik          : Factor w/ 2 levels "Lulus","Tidak": 1 1 1 2 1 2 1 2 1 2 ...

4.9 Validasi Target

if (!target_col %in% names(data_model)) {
  stop("Kolom target tidak ditemukan dalam data_model.")
}

if (!is.factor(data_model[[target_col]])) {
  stop("Kolom target belum berbentuk faktor.")
}

if (nlevels(data_model[[target_col]]) > 10) {
  stop("Kolom target memiliki terlalu banyak kelas. Kemungkinan kolom target yang dipilih masih salah.")
}

Validasi ini memastikan bahwa kolom target benar-benar sesuai untuk klasifikasi.
Target klasifikasi seharusnya berupa kategori, bukan angka identitas atau angka bebas.

4.10 Distribusi Target

table(data_model[[target_col]])
## 
## Lulus Tidak 
##   270   230
jumlah_kelas <- length(levels(data_model[[target_col]]))

ggplot(data_model, aes(x = .data[[target_col]], fill = .data[[target_col]])) +
  geom_bar(width = 0.6) +
  scale_fill_manual(values = pal_pink[1:jumlah_kelas]) +
  labs(
    title = "Distribusi Status Akademik Mahasiswa",
    x = "Status Akademik",
    y = "Jumlah Data",
    fill = "Status"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(color = "#c94f93", face = "bold", size = 16),
    axis.title = element_text(color = "#8a5a73"),
    legend.position = "bottom"
  )

Grafik di atas menunjukkan jumlah data pada setiap status akademik mahasiswa.
Distribusi target penting untuk diperhatikan karena dapat memengaruhi performa model klasifikasi.

4.11 Membagi Data Latih dan Data Uji

Data dibagi menjadi:

  • 80% data latih
  • 20% data uji
set.seed(123)

index_train <- sample(
  1:nrow(data_model),
  size = 0.8 * nrow(data_model)
)

data_train <- data_model[index_train, ]
data_test <- data_model[-index_train, ]

data_train[[target_col]] <- droplevels(data_train[[target_col]])
data_test[[target_col]] <- factor(
  data_test[[target_col]],
  levels = levels(data_train[[target_col]])
)

dim(data_train)
## [1] 400   9
dim(data_test)
## [1] 100   9

Data latih digunakan untuk membangun model Decision Tree.
Data uji digunakan untuk mengevaluasi kemampuan model dalam memprediksi data baru.

5 Pembangunan Model

5.1 Membuat Formula Model

fitur_model <- setdiff(names(data_train), target_col)

formula_model <- reformulate(
  termlabels = fitur_model,
  response = target_col
)

formula_model
## status_akademik ~ jenis_kelamin + umur + status_menikah + kehadiran + 
##     partisipasi_diskusi_skor + nilai_tugas_rata_rata + aktivitas_e_learning_skor + 
##     ipk

5.2 Model Decision Tree

model_tree <- rpart(
  formula_model,
  data = data_train,
  method = "class",
  parms = list(split = "information"),
  control = rpart.control(
    cp = 0.005,
    minsplit = 10,
    maxdepth = 4
  )
)

model_tree
## n= 400 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 400 187 Lulus (0.53250000 0.46750000)  
##    2) ipk>=2.495 251  83 Lulus (0.66932271 0.33067729)  
##      4) nilai_tugas_rata_rata>=66.5 139  23 Lulus (0.83453237 0.16546763)  
##        8) aktivitas_e_learning_skor>=53.5 115  11 Lulus (0.90434783 0.09565217) *
##        9) aktivitas_e_learning_skor< 53.5 24  12 Lulus (0.50000000 0.50000000)  
##         18) ipk< 3.225 10   2 Lulus (0.80000000 0.20000000) *
##         19) ipk>=3.225 14   4 Tidak (0.28571429 0.71428571) *
##      5) nilai_tugas_rata_rata< 66.5 112  52 Tidak (0.46428571 0.53571429)  
##       10) kehadiran>=78.5 35  10 Lulus (0.71428571 0.28571429)  
##         20) aktivitas_e_learning_skor>=69.5 17   0 Lulus (1.00000000 0.00000000) *
##         21) aktivitas_e_learning_skor< 69.5 18   8 Tidak (0.44444444 0.55555556) *
##       11) kehadiran< 78.5 77  27 Tidak (0.35064935 0.64935065)  
##         22) aktivitas_e_learning_skor>=97.5 3   0 Lulus (1.00000000 0.00000000) *
##         23) aktivitas_e_learning_skor< 97.5 74  24 Tidak (0.32432432 0.67567568) *
##    3) ipk< 2.495 149  45 Tidak (0.30201342 0.69798658)  
##      6) nilai_tugas_rata_rata>=84.5 33  15 Lulus (0.54545455 0.45454545)  
##       12) partisipasi_diskusi_skor>=71.5 15   3 Lulus (0.80000000 0.20000000)  
##         24) umur>=19.5 12   1 Lulus (0.91666667 0.08333333) *
##         25) umur< 19.5 3   1 Tidak (0.33333333 0.66666667) *
##       13) partisipasi_diskusi_skor< 71.5 18   6 Tidak (0.33333333 0.66666667)  
##         26) partisipasi_diskusi_skor< 41.5 3   0 Lulus (1.00000000 0.00000000) *
##         27) partisipasi_diskusi_skor>=41.5 15   3 Tidak (0.20000000 0.80000000) *
##      7) nilai_tugas_rata_rata< 84.5 116  27 Tidak (0.23275862 0.76724138)  
##       14) partisipasi_diskusi_skor< 43.5 7   2 Lulus (0.71428571 0.28571429) *
##       15) partisipasi_diskusi_skor>=43.5 109  22 Tidak (0.20183486 0.79816514) *

Model Decision Tree dibangun menggunakan seluruh variabel selain target sebagai prediktor.
Kriteria split yang digunakan adalah information, sehingga pembentukan pohon berkaitan dengan konsep entropy dan information gain.

Parameter maxdepth = 4 digunakan agar pohon keputusan tidak terlalu panjang dan tetap mudah dibaca.

6 Visualisasi Decision Tree

6.1 Visualisasi Decision Tree dari Software

jumlah_kelas_model <- length(levels(data_train[[target_col]]))

rpart.plot(
  model_tree,
  type = 4,
  extra = 104,
  fallen.leaves = TRUE,
  under = TRUE,
  faclen = 0,
  varlen = 0,
  branch = 0.8,
  branch.col = "#d85c9f",
  box.palette = as.list(pal_pink[1:jumlah_kelas_model]),
  shadow.col = "#f8bbd0",
  roundint = FALSE,
  cex = 0.8,
  main = "Decision Tree Prediksi Status Akademik Mahasiswa"
)

Visualisasi di atas menunjukkan pohon keputusan yang dibentuk oleh software R.
Pohon ini memperlihatkan aturan yang digunakan model untuk menentukan status akademik mahasiswa.

6.2 Visualisasi Decision Tree Lengkap dengan Entropy

buat_label_aman <- function(x) {
  x <- gsub('"', "'", x)
  x <- gsub("<=", "≤", x)
  x <- gsub(">=", "≥", x)
  x <- gsub("<", "kurang dari", x)
  x <- gsub(">", "lebih dari", x)
  x <- gsub(",", ", ", x)
  return(x)
}

hitung_entropy <- function(count_vector) {
  total <- sum(count_vector)
  
  if (total == 0) {
    return(0)
  }
  
  p <- count_vector / total
  p <- p[p > 0]
  
  entropy <- -sum(p * log2(p))
  return(round(entropy, 3))
}

frame_tree <- model_tree$frame
node_id <- as.numeric(row.names(frame_tree))

kelas <- levels(data_train[[target_col]])
jumlah_kelas <- length(kelas)

yval2 <- frame_tree$yval2

counts <- yval2[, 2:(jumlah_kelas + 1), drop = FALSE]

prediksi_node <- kelas[frame_tree$yval]

node_text <- c()

for (i in seq_along(node_id)) {
  
  jumlah_data <- frame_tree$n[i]
  entropy_value <- hitung_entropy(as.numeric(counts[i, ]))
  
  jenis_node <- ifelse(
    frame_tree$var[i] == "<leaf>",
    "LEAF NODE",
    paste0("SPLIT: ", frame_tree$var[i])
  )
  
  jumlah_per_kelas <- paste(
    paste0(kelas, " = ", as.numeric(counts[i, ])),
    collapse = "\\n"
  )
  
  hasil_prediksi <- prediksi_node[i]
  
  label_node <- paste(
    paste0("Node ", node_id[i]),
    jenis_node,
    paste0("Data = ", jumlah_data),
    jumlah_per_kelas,
    paste0("Entropy = ", entropy_value),
    paste0("Prediksi = ", hasil_prediksi),
    sep = "\\n"
  )
  
  warna_node <- ifelse(
    frame_tree$var[i] == "<leaf>",
    "#ffeaf4",
    "#f8bbd0"
  )
  
  node_text[i] <- paste0(
    node_id[i],
    ' [label = "',
    buat_label_aman(label_node),
    '", fillcolor = "',
    warna_node,
    '"];'
  )
}

edge_text <- c()

for (parent in node_id[frame_tree$var != "<leaf>"]) {
  
  children <- c(parent * 2, parent * 2 + 1)
  
  for (child in children) {
    
    if (child %in% node_id) {
      
      path_child <- path.rpart(model_tree, nodes = child, print.it = FALSE)[[1]]
      label_edge <- tail(path_child, 1)
      label_edge <- buat_label_aman(label_edge)
      
      edge_text <- c(
        edge_text,
        paste0(parent, " -> ", child, ' [label = "', label_edge, '"];')
      )
    }
  }
}

dot_code <- paste0(
"
digraph decision_tree {

  graph [
    layout = dot,
    rankdir = TB,
    bgcolor = '#fff7fb',
    labelloc = 't',
    label = 'Visualisasi Decision Tree Status Akademik Mahasiswa dengan Entropy',
    fontsize = 20,
    fontname = 'Segoe UI',
    fontcolor = '#c94f93',
    ranksep = 0.60,
    nodesep = 0.40
  ]

  node [
    shape = box,
    style = 'filled, rounded',
    fontname = 'Segoe UI',
    fontsize = 12,
    color = '#d85c9f',
    penwidth = 2,
    margin = 0.18
  ]

  edge [
    fontname = 'Segoe UI',
    fontsize = 10,
    color = '#c94f93',
    fontcolor = '#8a3d68',
    penwidth = 2
  ]

",
paste(node_text, collapse = "\n"),
"\n",
paste(edge_text, collapse = "\n"),
"
}
"
)

grViz(dot_code, width = "100%", height = "900px")

Visualisasi Decision Tree di atas dibuat untuk menampilkan informasi yang lebih lengkap.
Setiap node menampilkan jumlah data, jumlah data pada masing-masing kelas, nilai entropy, dan hasil prediksi.

Nilai entropy menunjukkan tingkat ketidakmurnian data pada node.
Jika entropy semakin kecil, maka data pada node tersebut semakin murni.
Jika entropy bernilai 0, berarti seluruh data pada node tersebut berada pada satu kelas yang sama.

7 Prediksi dan Evaluasi Model

7.1 Melakukan Prediksi pada Data Uji

prediksi <- predict(model_tree, data_test, type = "class")

head(prediksi)
##     1     2     3     4     5     6 
## Lulus Tidak Tidak Tidak Tidak Lulus 
## Levels: Lulus Tidak

7.2 Confusion Matrix

prediksi <- factor(prediksi, levels = levels(data_test[[target_col]]))

conf_matrix <- table(
  Aktual = data_test[[target_col]],
  Prediksi = prediksi
)

conf_matrix
##        Prediksi
## Aktual  Lulus Tidak
##   Lulus    29    28
##   Tidak    12    31

Confusion matrix digunakan untuk melihat jumlah prediksi benar dan salah.
Baris menunjukkan kelas aktual, sedangkan kolom menunjukkan kelas hasil prediksi model.

7.3 Accuracy

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)

accuracy
## [1] 0.6

7.4 Precision, Recall, dan F1-Score

kelas_eval <- levels(data_test[[target_col]])

metric_per_class <- data.frame(
  Kelas = kelas_eval,
  Precision = NA,
  Recall = NA,
  F1_Score = NA
)

for (i in seq_along(kelas_eval)) {
  
  kelas_i <- kelas_eval[i]
  
  TP <- conf_matrix[kelas_i, kelas_i]
  FP <- sum(conf_matrix[, kelas_i]) - TP
  FN <- sum(conf_matrix[kelas_i, ]) - TP
  
  precision <- ifelse((TP + FP) == 0, 0, TP / (TP + FP))
  recall <- ifelse((TP + FN) == 0, 0, TP / (TP + FN))
  f1 <- ifelse((precision + recall) == 0, 0, 2 * precision * recall / (precision + recall))
  
  metric_per_class$Precision[i] <- precision
  metric_per_class$Recall[i] <- recall
  metric_per_class$F1_Score[i] <- f1
}

metric_per_class
##   Kelas Precision    Recall  F1_Score
## 1 Lulus 0.7073171 0.5087719 0.5918367
## 2 Tidak 0.5254237 0.7209302 0.6078431
hasil_evaluasi <- data.frame(
  Metrik = c("Accuracy", "Macro Precision", "Macro Recall", "Macro F1-Score"),
  Nilai = c(
    accuracy,
    mean(metric_per_class$Precision),
    mean(metric_per_class$Recall),
    mean(metric_per_class$F1_Score)
  )
)

hasil_evaluasi
##            Metrik     Nilai
## 1        Accuracy 0.6000000
## 2 Macro Precision 0.6163704
## 3    Macro Recall 0.6148511
## 4  Macro F1-Score 0.5998399

Metrik evaluasi digunakan untuk menilai performa model.

  • Accuracy menunjukkan proporsi prediksi yang benar dari seluruh data uji.
  • Precision menunjukkan ketepatan model saat memprediksi suatu kelas.
  • Recall menunjukkan kemampuan model dalam menemukan data dari suatu kelas.
  • F1-Score menunjukkan keseimbangan antara precision dan recall.

7.5 Visualisasi Metrik Evaluasi

ggplot(hasil_evaluasi, aes(x = Metrik, y = Nilai, fill = Metrik)) +
  geom_col(width = 0.6) +
  scale_fill_manual(values = pal_pink[1:nrow(hasil_evaluasi)]) +
  ylim(0, 1) +
  labs(
    title = "Hasil Evaluasi Model Decision Tree",
    x = "Metrik Evaluasi",
    y = "Nilai"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(color = "#c94f93", face = "bold", size = 16),
    axis.title = element_text(color = "#8a5a73"),
    legend.position = "none"
  )

## Interpretasi Evaluasi Model

Berdasarkan hasil evaluasi, model Decision Tree memperoleh nilai accuracy sebesar 60%. Nilai ini menunjukkan bahwa dari seluruh data uji, sekitar 60% data berhasil diklasifikasikan dengan benar oleh model.

Nilai macro precision sebesar 61.64% menunjukkan rata-rata ketepatan model ketika memberikan prediksi pada setiap kelas. Nilai macro recall sebesar 61.49% menunjukkan kemampuan model dalam menemukan data yang benar dari masing-masing kelas. Sementara itu, nilai macro F1-score sebesar 59.98% menunjukkan keseimbangan antara precision dan recall.

Secara umum, performa model dapat dikategorikan sedang. Artinya, model sudah mampu mengenali sebagian pola pada data, tetapi hasilnya belum sepenuhnya optimal. Kondisi ini masih wajar karena Decision Tree yang digunakan dibatasi kedalamannya agar visualisasi tetap sederhana dan mudah dibaca.

Nilai evaluasi menunjukkan bahwa model Decision Tree sudah dapat digunakan untuk melakukan klasifikasi status akademik mahasiswa. Namun, apabila nilai accuracy berada di sekitar 60%, maka performa model belum dapat dikatakan sangat baik. Model masih tergolong sedang, sehingga hasilnya perlu diinterpretasikan secara hati-hati.

Keputusan untuk tetap menggunakan Decision Tree pada analisis ini didasarkan pada keunggulan interpretasi. Decision Tree memungkinkan proses pengambilan keputusan ditampilkan dalam bentuk pohon, sehingga lebih mudah dipahami dibandingkan model yang lebih kompleks.

8 Interpretasi Hasil

8.1 Variable Importance

importance <- model_tree$variable.importance

if (is.null(importance)) {
  importance_df <- data.frame(
    Variabel = "Tidak ada variabel penting",
    Nilai_Importance = 0
  )
} else {
  importance_df <- data.frame(
    Variabel = names(importance),
    Nilai_Importance = as.numeric(importance)
  )
}

importance_df
##                    Variabel Nilai_Importance
## 1                       ipk        35.063812
## 2     nilai_tugas_rata_rata        27.530325
## 3 aktivitas_e_learning_skor        24.511179
## 4  partisipasi_diskusi_skor        14.280827
## 5                 kehadiran        12.179518
## 6                      umur         4.318690
## 7            status_menikah         1.764682
## 8             jenis_kelamin         1.513081
ggplot(importance_df, aes(x = reorder(Variabel, Nilai_Importance), y = Nilai_Importance, fill = Variabel)) +
  geom_col(width = 0.6) +
  coord_flip() +
  scale_fill_manual(values = pal_pink[1:nrow(importance_df)]) +
  labs(
    title = "Tingkat Kepentingan Variabel pada Decision Tree",
    x = "Variabel",
    y = "Nilai Importance"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(color = "#c94f93", face = "bold", size = 16),
    axis.title = element_text(color = "#8a5a73"),
    legend.position = "none"
  )

Variable importance menunjukkan variabel mana yang paling berpengaruh dalam proses klasifikasi.
Semakin tinggi nilai importance, semakin besar peran variabel tersebut dalam membentuk model Decision Tree.

8.2 Interpretasi Hasil Model

Berdasarkan model Decision Tree, status akademik mahasiswa diprediksi menggunakan beberapa atribut akademik dan aktivitas pembelajaran yang tersedia pada dataset. Model membaca pola dari data latih, kemudian membentuk aturan keputusan berdasarkan atribut yang paling mampu memisahkan kelas target.

Atribut yang muncul di bagian atas pohon keputusan merupakan atribut yang memiliki pengaruh besar pada proses pemisahan data. Berdasarkan grafik variable importance, variabel yang paling berpengaruh dalam model ini adalah ipk. Artinya, variabel tersebut memiliki peran paling besar dalam membantu model membedakan status akademik mahasiswa.

Proses kerja model dapat dipahami dari arah cabang pada pohon keputusan. Data mahasiswa akan masuk dari node paling atas, kemudian mengikuti aturan tertentu hingga sampai pada leaf node. Leaf node tersebut berisi kelas akhir yang menjadi hasil prediksi model.

Nilai entropy pada node menunjukkan tingkat ketidakmurnian data. Jika entropy masih besar, berarti data pada node tersebut masih bercampur antara beberapa kelas. Sebaliknya, jika entropy semakin kecil atau mendekati nol, berarti data pada node tersebut semakin murni dan lebih mudah diklasifikasikan.

Interpretasi utama dari model ini adalah bahwa Decision Tree mampu memberikan aturan klasifikasi yang mudah dipahami. Model tidak hanya memberikan hasil prediksi, tetapi juga menunjukkan jalur keputusan yang digunakan untuk menghasilkan prediksi tersebut.

Walaupun performa model belum sempurna, model tetap bermanfaat untuk memahami pola umum dalam data. Misalnya, atribut akademik tertentu dapat menjadi indikator penting dalam memprediksi status akademik mahasiswa.

9 Kesimpulan

Berdasarkan analisis menggunakan tahapan CRISP-DM, dataset Prediksi Status Akademik Mahasiswa dapat dianalisis menggunakan metode klasifikasi. Tahapan analisis dimulai dari pemahaman masalah, pemahaman data, preprocessing data, pembangunan model, evaluasi, interpretasi hasil, hingga penarikan kesimpulan.

Metode yang digunakan adalah Decision Tree Classification karena target data berupa kategori. Decision Tree sesuai digunakan pada studi kasus ini karena model dapat membentuk aturan keputusan yang mudah dipahami dan dapat divisualisasikan dalam bentuk pohon keputusan.

Hasil evaluasi menunjukkan bahwa model memperoleh accuracy sebesar 60%. Nilai ini menunjukkan bahwa model sudah mampu melakukan klasifikasi, tetapi performanya masih tergolong sedang dan belum optimal. Oleh karena itu, hasil prediksi model perlu digunakan secara hati-hati dan tidak dapat langsung dianggap sebagai keputusan akhir tanpa pertimbangan lain.

Performa model yang belum terlalu tinggi dapat disebabkan oleh beberapa faktor, seperti pola data yang belum sepenuhnya dapat dipisahkan dengan satu pohon keputusan, fitur yang belum cukup kuat, atau pembatasan kedalaman pohon agar visualisasi tetap mudah dibaca. Meskipun demikian, Decision Tree tetap dipertahankan karena tujuan analisis ini tidak hanya mengejar akurasi, tetapi juga menekankan kemudahan interpretasi model.

Secara keseluruhan, model Decision Tree dapat digunakan untuk memahami pola awal dalam prediksi status akademik mahasiswa. Untuk pengembangan selanjutnya, performa model dapat ditingkatkan dengan pengaturan parameter yang lebih optimal, penambahan fitur yang lebih relevan, atau perbandingan dengan metode lain seperti Random Forest.