Prediksi Tulang Belakang Normal, Disk Hernia, atau Spondylolisthesis#

Pendahuluan#

Permasalahan kelainan tulang belakang merupakan salah satu gangguan kesehatan yang sering terjadi pada masyarakat, terutama akibat faktor usia, postur tubuh yang buruk, maupun beban kerja fisik yang berlebihan. Dua jenis kelainan yang umum ditemukan adalah disk hernia dan spondylolisthesis, yang dapat menyebabkan rasa nyeri, keterbatasan gerak, dan penurunan kualitas hidup. Deteksi dini terhadap kelainan-kelainan tersebut sangat penting agar penanganan medis dapat dilakukan secepat mungkin.

Dengan berkembangnya teknologi, khususnya di bidang kecerdasan buatan. proses klasifikasi atau diagnosis kelainan tulang belakang kini dapat dibantu oleh sistem cerdas berbasis data.

Data Understanding#

Sumber Data#

Dataset diambil dari link dibawah ini:

https://archive.ics.uci.edu/dataset/212/vertebral+column

Salah satu dataset yang umum digunakan dalam pengembangan dan penelitian klasifikasi medis adalah Vertebral Column Dataset yang tersedia di UCI Machine Learning Repository. Dataset ini memuat parameter biomekanis yang diambil dari citra radiografi tulang belakang pasien, seperti pelvic incidence, sacral slope, dan lumbar lordosis angle.

Tujuan#

Penelitian ini bertujuan untuk mengimplementasikan algoritma klasifikasi dalam mengelompokkan kondisi tulang belakang pasien ke dalam tiga kategori, yaitu: normal, hernia diskus, dan spondylolisthesis. Dengan memanfaatkan teknik pembelajaran mesin, diharapkan sistem ini mampu menjadi alat bantu diagnosis awal yang efisien, akurat, dan bermanfaat dalam bidang medis, khususnya ortopedi.

Integrasi Data#

untuk mengambil data agar dapat diolah, perlu untuk menginstall package yang telah disediakan oleh UCI Dataset. Instalasi dilakukan berguna untuk menarik data yang berasal dari UCI dataset agar dapat diolah. peritah untuk mengambil data dari UCI dataset dapat di lihat ketika menekan tombol import in python pada datase yang diinginkan dan ikuti perintah tersebut agar data dapat diambil dari UCI dataset. Contoh pengambilan data dari UCI dataset dapat dilihat pada gambar dan perintah berikut:

image.png

!pip install ucimlrepo
Requirement already satisfied: ucimlrepo in /usr/local/python/3.12.1/lib/python3.12/site-packages (0.0.7)
Requirement already satisfied: pandas>=1.0.0 in /home/codespace/.local/lib/python3.12/site-packages (from ucimlrepo) (2.2.3)
Requirement already satisfied: certifi>=2020.12.5 in /home/codespace/.local/lib/python3.12/site-packages (from ucimlrepo) (2024.8.30)
Requirement already satisfied: numpy>=1.26.0 in /home/codespace/.local/lib/python3.12/site-packages (from pandas>=1.0.0->ucimlrepo) (2.2.0)
Requirement already satisfied: python-dateutil>=2.8.2 in /home/codespace/.local/lib/python3.12/site-packages (from pandas>=1.0.0->ucimlrepo) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /home/codespace/.local/lib/python3.12/site-packages (from pandas>=1.0.0->ucimlrepo) (2024.2)
Requirement already satisfied: tzdata>=2022.7 in /home/codespace/.local/lib/python3.12/site-packages (from pandas>=1.0.0->ucimlrepo) (2024.2)
Requirement already satisfied: six>=1.5 in /home/codespace/.local/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas>=1.0.0->ucimlrepo) (1.17.0)
[notice] A new release of pip is available: 24.3.1 -> 25.1.1
[notice] To update, run: python3 -m pip install --upgrade pip
from ucimlrepo import fetch_ucirepo

# fetch dataset
vertebral_column = fetch_ucirepo(id=212)

# data (as pandas dataframes)
X = vertebral_column.data.features
y = vertebral_column.data.targets

data = vertebral_column.data.original

data.to_csv("vertebral_column.csv", index=True)
print(data.info()) #untuk menampilkan info fitur-fitur yang ada di tabel

print(data.head()) #untuk menampilkan 5 baris pertama
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 310 entries, 0 to 309
Data columns (total 7 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   pelvic_incidence          310 non-null    float64
 1   pelvic_tilt               310 non-null    float64
 2   lumbar_lordosis_angle     310 non-null    float64
 3   sacral_slope              310 non-null    float64
 4   pelvic_radius             310 non-null    float64
 5   degree_spondylolisthesis  310 non-null    float64
 6   class                     310 non-null    object 
dtypes: float64(6), object(1)
memory usage: 17.1+ KB
None
   pelvic_incidence  pelvic_tilt  lumbar_lordosis_angle  sacral_slope  \
0         63.027817    22.552586              39.609117     40.475232   
1         39.056951    10.060991              25.015378     28.995960   
2         68.832021    22.218482              50.092194     46.613539   
3         69.297008    24.652878              44.311238     44.644130   
4         49.712859     9.652075              28.317406     40.060784   

   pelvic_radius  degree_spondylolisthesis   class  
0      98.672917                 -0.254400  Hernia  
1     114.405425                  4.564259  Hernia  
2     105.985135                 -3.530317  Hernia  
3     101.868495                 11.211523  Hernia  
4     108.168725                  7.918501  Hernia  

Eksplorasi Data#

VIsualisasi Data#

display(data) #display dataset
pelvic_incidence pelvic_tilt lumbar_lordosis_angle sacral_slope pelvic_radius degree_spondylolisthesis class
0 63.027817 22.552586 39.609117 40.475232 98.672917 -0.254400 Hernia
1 39.056951 10.060991 25.015378 28.995960 114.405425 4.564259 Hernia
2 68.832021 22.218482 50.092194 46.613539 105.985135 -3.530317 Hernia
3 69.297008 24.652878 44.311238 44.644130 101.868495 11.211523 Hernia
4 49.712859 9.652075 28.317406 40.060784 108.168725 7.918501 Hernia
... ... ... ... ... ... ... ...
305 47.903565 13.616688 36.000000 34.286877 117.449062 -4.245395 Normal
306 53.936748 20.721496 29.220534 33.215251 114.365845 -0.421010 Normal
307 61.446597 22.694968 46.170347 38.751628 125.670725 -2.707880 Normal
308 45.252792 8.693157 41.583126 36.559635 118.545842 0.214750 Normal
309 33.841641 5.073991 36.641233 28.767649 123.945244 -0.199249 Normal

310 rows × 7 columns

Tampilkan banyaknya class#

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from ucimlrepo import fetch_ucirepo

# Ambil dataset
vertebral_column = fetch_ucirepo(id=212)

# Gabungkan fitur dan target
X = vertebral_column.data.features
y = vertebral_column.data.targets
data = pd.concat([X, y], axis=1)
plt.figure(figsize=(6, 4))
sns.countplot(data=data, x='class', palette='Set2')
plt.title('Distribusi Kelas')
plt.xlabel('Kelas')
plt.ylabel('Jumlah')
plt.show()
/tmp/ipykernel_2773/468467621.py:2: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.countplot(data=data, x='class', palette='Set2')
_images/6d4f0cd4ca0eb3f8d9774c63f33c053c56902cfe7d2b9bdb82ea2efc15b3bfa4.png

Struktur Dataset#

Fitur dan Tabel#

data.shape #untuk mengetahui bentuk dataset(baris & kolom)
(310, 7)

Terdapat 310 baris dan 7 kolom.

data.columns #untuk mengetahui kolom-kolom yang ada pada dataset
Index(['pelvic_incidence', 'pelvic_tilt', 'lumbar_lordosis_angle',
       'sacral_slope', 'pelvic_radius', 'degree_spondylolisthesis', 'class'],
      dtype='object')

Fitur adalah atribut atau variabel independen dalam dataset yang digunakan untuk membuat prediksi.

Terdapat 6 fitur pada dataset ini:

  • pelvic_incidence: Sudut antara garis tegak lurus terhadap sakrum dan garis yang menghubungkan pusat kepala femur. (Nilai numerik dalam derajat (°))

  • pelvic_tilt: Mengukur kemiringan panggul terhadap garis vertikal. Ini mencerminkan posisi panggul terhadap postur tubuh. (Nilai derajat kemiringa)

  • lumbar_lordosis_angle: udut kelengkungan tulang belakang bagian bawah (lumbar). (Nilai numerik dalam derajat)

  • sacral_slope: Sudut kemiringan sakrum terhadap horizontal. Ini berbanding langsung dengan pelvic incidence. (Nilai numerik deraja)

  • pelvic_radius: Jarak antara pusat sakrum dan kepala femur. Merupakan parameter ukuran anatomi panggul. (Nilai panjang (kemungkinan satuannya mm atau cm))

  • degree_spondylolisthesis: Derajat pergeseran atau “slip” tulang belakang ke depan terhadap ruas bawahnya. (Nilai real/float yang bisa positif atau negatif)

Label adalah variabel dependen atau target yang ingin diprediksi oleh model machine learning. Label merupakan output yang dipelajari oleh model dari data.

Terdapat 1 field label dengan 3 tipe pada dataset ini:

  • Normal

  • Disk Hernia

  • spondylolisthesis

data.dtypes #untuk mengetahui tipe data dari masing-masing kolom
pelvic_incidence            float64
pelvic_tilt                 float64
lumbar_lordosis_angle       float64
sacral_slope                float64
pelvic_radius               float64
degree_spondylolisthesis    float64
class                        object
dtype: object

Identifikasi Kualitas Dataset#

Deteksi Missing Value#

Missing value merupakan data yang hilang pada suatu dataset. Hal ini bisa terjadi oleh beberapa faktor, diantaranya adalah :

  • Interviewer recording error terjadi akibat kelalaian petugas pengumpul data (pewawancara), misalnya ada sejumlah pertanyaan yang terlewatkan.

  • Respondent inability error terjadi akibat ketidakmampuan responden dalam memberikan jawaban akurat, misalnya karena tidak memahami pertanyaan, bosan atau kelelahan (respondent fatigue) akhirnya responden mengosongkan sejumlah pertanyaan atau berhenti mengisi kuesioner di tengah jalan.

  • Unwillingness respondent error terjadi karena responden tidak berkenan memberikan jawaban yang akurat, misalnya pertanyaan soal penghasilan, usia, berat badan, pengalaman melakukan pelanggaran hukum, dll.

Cara penanganan Missing Values :

  • Mengabaikan dan membuang missing data.

  • Estimasi parameter.

  • Imputasi.

pada kolom di bawah ini akan dilakukan pendeteksian missing values terlebih dahulu pada masing-masing kolom.

# Cek apakah ada missing value di setiap kolom
missing_values = data.isnull().sum()

# Tampilkan hasilnya
print("Jumlah Missing Value per Kolom:\n")
print(missing_values)

# (Opsional) Tampilkan kolom mana saja yang punya missing value
print("\nKolom dengan missing value:")
print(missing_values[missing_values > 0])
Jumlah Missing Value per Kolom:

pelvic_incidence            0
pelvic_tilt                 0
lumbar_lordosis_angle       0
sacral_slope                0
pelvic_radius               0
degree_spondylolisthesis    0
class                       0
dtype: int64

Kolom dengan missing value:
Series([], dtype: int64)

Didapatkan missing value 0 pada setiap fitur

Prepocessing Data#

Preprocessing dilakukan untuk:

  • Menghindari bias algoritma akibat skala fitur

  • Meningkatkan akurasi model

  • Memastikan data bersih dan siap digunakan

Normalisasi#

  • Karena penelitian ini akan menggunakan KNN maka perlu normalisasi karean algoritma ini sensitif terhadap jarak antar fitur

  • Naive Bayes dan Decision Tree sebenarnya tidak terlalu bergantung pada skala, namun tetap direkomendasikan untuk menjaga konsistensi.

from sklearn.preprocessing import StandardScaler

# Normalisasi fitur (tanpa label)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Konversi kembali ke DataFrame (jika ingin melihat)
X_scaled_df = pd.DataFrame(X_scaled, columns=X.columns)
X_scaled_df.head()
pelvic_incidence pelvic_tilt lumbar_lordosis_angle sacral_slope pelvic_radius degree_spondylolisthesis
0 0.147086 0.501369 -0.665177 -0.184950 -1.447647 -0.708059
1 -1.245864 -0.748769 -1.453001 -1.041521 -0.264385 -0.579556
2 0.484370 0.467932 -0.099262 0.273083 -0.897686 -0.795421
3 0.511390 0.711562 -0.411339 0.126128 -1.207303 -0.402288
4 -0.626648 -0.789693 -1.274745 -0.215876 -0.733455 -0.490106

Deteksi dan Penanganan Outlier#

Outlier/pencilan merupakan data pada dataset yang menyimpang dari data lainnya,mendeteksi outlier perlu agar data yang diolah memberikan hasil yang baik pada model yang akan dibuat nantinya. Outlier dapat diidentifikasi dan ditangani dengan beberapa cara antara lain yang akan kami gunakan yaitu LOF(Local Outlier Factor).

Konsep Local Outlier Factor#

Outlier adalah titik data yang berbeda atau jauh dari titik data lainnya. Local Outlier Factor (LOF) adalah algoritma yang mengidentifikasi outlier yang ada dalam kumpulan data. Ketika suatu titik dianggap sebagai outlier berdasarkan lingkungan lokalnya, maka titik tersebut disebut local outlier. LOF akan mengidentifikasi outlier dengan mempertimbangkan kepadatan lingkungan. LOF bekerja dengan baik ketika kepadatan data tidak sama di seluruh kumpulan data.

Untuk memahami LOF, ada beberapa konsep yang harus dipelajari secara berurutan:

  • K-distance dan K-neighbors

  • Reachability Distance (RD)

  • Local Reachability Density (LRD)

  • Local Outlier Factor (LOF)

K-distance & K-neighbors

image.png

K-distance adalah jarak antara suatu titik, dan merupakan tetangga terdekat Kᵗʰ. Tetangga K yang dilambangkan dengan Nₖ(A) mencakup himpunan titik yang terletak di dalam atau pada lingkaran berjari-jari jarak K. K-tetangga bisa lebih dari atau sama dengan nilai K. Kita akan melihat contohnya. Katakanlah kita mempunyai empat titik A, B, C, dan D. Jika K=2, K-tetangga A adalah C, B, dan D. Di sini, nilai K=2 tetapi ||N₂(A)|| = 3. Oleh karena itu, ||Nₖ(titik)|| akan selalu lebih besar atau sama dengan K.

Reachability Distance (RD)

didefinisikan sebagai jarak K maksimum Xj dan jarak antara Xi dan Xj. Ukuran jarak bersifat khusus untuk masalah (Euclidean, Manhattan, dll.) Dalam istilah awam, jika titik Xi terletak di dalam K-tetangga Xj, maka jarak jangkauannya adalah K-jarak Xj (garis biru), jika tidak, jarak jangkauannya adalah jarak antara Xi dan Xj (garis oranye).

Local reachability density (LRD) image.png

LRD merupakan kebalikan dari rata-rata jarak jangkauan A dari tetangganya. Semakin besar jarak jangkauan rata-rata (yaitu, tetangga jauh dari titik tersebut), semakin sedikit kepadatan titik yang ada di sekitar titik tertentu. Ini menunjukkan seberapa jauh suatu titik dari kelompok titik terdekat. Nilai LRD yang rendah menunjukkan bahwa cluster terdekat berada jauh dari titik.

Local Outlier Factor (LOF)

image.png

LRD tiap titik digunakan untuk membandingkan dengan rata-rata LRD K tetangganya. LOF adalah perbandingan rata-rata LRD K tetangga A terhadap LRD A. Jika suatu titik bukan merupakan pencilan (inlier), rasio rata-rata LRD tetangganya kira-kira sama dengan LRD suatu titik (karena kepadatan suatu titik dan tetangganya kira-kira sama). Dalam hal ini, LOF hampir sama dengan 1. Sebaliknya, jika suatu titik merupakan outlier, LRD suatu titik lebih kecil dari rata-rata LRD tetangganya. Maka nilai LOF akan tinggi. Umumnya jika LOF > 1 maka dianggap outlier, namun hal tersebut tidak selalu benar. Katakanlah kita mengetahui bahwa kita hanya memiliki satu outlier dalam data, lalu kita ambil nilai LOF maksimum di antara semua nilai LOF, dan titik yang sesuai dengan nilai LOF maksimum akan dianggap sebagai outlier.

Untuk langkah-langkah perhitungan manual Local Outlier Factor (LOF) yaitu :

  1. Menghitung jarak dan menentukan tetangga

  2. Menghitung Reachability Distance (RD)

  3. Menghitung Local Reachability Distance (LRD)

  4. Menghitung Local Outlier Factor (LOF)

Implementasi Local Outlier Factor dengan Scikit Learn#

from sklearn.neighbors import LocalOutlierFactor
import matplotlib.pyplot as plt
import pandas as pd

# Pastikan data tersedia dan label terakhir dihapus
data1 = data.drop(columns=['class'])  # Buang label untuk analisis outlier

# Membuat model LOF
lof = LocalOutlierFactor(n_neighbors=9, contamination=0.03)

# Menyimpan indeks outlier dalam list
outlier_indices = []

# Melakukan deteksi outlier untuk setiap fitur secara terpisah
for column in data1.columns:
    feature_values = data1[column].values.reshape(-1, 1)
    y_pred = lof.fit_predict(feature_values)

    # Tambahkan indeks yang dideteksi sebagai outlier (label -1)
    outlier_indices.extend([(i, column) for i, label in enumerate(y_pred) if label == -1])

# Menghilangkan duplikat indeks
outlier_indices = list(set(outlier_indices))

# Menampilkan informasi outlier
print("===================================== OUTLIER =====================================")
for i in outlier_indices:
    print(f"Outlier ditemukan pada baris => {i[0]}, kolom => {i[1]}")

# Menyusun indeks baris yang mengandung outlier (tanpa duplikat)
outlier_row_indices = sorted(set([i[0] for i in outlier_indices]))

# Tampilkan baris yang mengandung outlier
outlier_data = data.iloc[outlier_row_indices]

print("===================================== DATA =====================================")
print("Data pada baris yang mengandung outlier:")
print(outlier_data)
===================================== OUTLIER =====================================
Outlier ditemukan pada baris => 39, kolom => pelvic_incidence
Outlier ditemukan pada baris => 115, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 7, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 179, kolom => pelvic_tilt
Outlier ditemukan pada baris => 95, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 166, kolom => pelvic_radius
Outlier ditemukan pada baris => 11, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 94, kolom => pelvic_incidence
Outlier ditemukan pada baris => 162, kolom => pelvic_incidence
Outlier ditemukan pada baris => 206, kolom => pelvic_incidence
Outlier ditemukan pada baris => 11, kolom => sacral_slope
Outlier ditemukan pada baris => 163, kolom => sacral_slope
Outlier ditemukan pada baris => 141, kolom => pelvic_tilt
Outlier ditemukan pada baris => 96, kolom => pelvic_incidence
Outlier ditemukan pada baris => 26, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 24, kolom => sacral_slope
Outlier ditemukan pada baris => 290, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 26, kolom => sacral_slope
Outlier ditemukan pada baris => 75, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 192, kolom => pelvic_radius
Outlier ditemukan pada baris => 37, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 26, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 235, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 112, kolom => pelvic_tilt
Outlier ditemukan pada baris => 248, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 168, kolom => pelvic_incidence
Outlier ditemukan pada baris => 257, kolom => pelvic_tilt
Outlier ditemukan pada baris => 37, kolom => sacral_slope
Outlier ditemukan pada baris => 304, kolom => pelvic_radius
Outlier ditemukan pada baris => 147, kolom => pelvic_tilt
Outlier ditemukan pada baris => 94, kolom => sacral_slope
Outlier ditemukan pada baris => 162, kolom => sacral_slope
Outlier ditemukan pada baris => 197, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 96, kolom => sacral_slope
Outlier ditemukan pada baris => 228, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 75, kolom => pelvic_radius
Outlier ditemukan pada baris => 10, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 115, kolom => pelvic_incidence
Outlier ditemukan pada baris => 142, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 167, kolom => pelvic_radius
Outlier ditemukan pada baris => 151, kolom => pelvic_tilt
Outlier ditemukan pada baris => 287, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 206, kolom => pelvic_tilt
Outlier ditemukan pada baris => 252, kolom => pelvic_tilt
Outlier ditemukan pada baris => 147, kolom => pelvic_radius
Outlier ditemukan pada baris => 168, kolom => sacral_slope
Outlier ditemukan pada baris => 122, kolom => pelvic_tilt
Outlier ditemukan pada baris => 54, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 83, kolom => pelvic_radius
Outlier ditemukan pada baris => 180, kolom => pelvic_radius
Outlier ditemukan pada baris => 278, kolom => degree_spondylolisthesis
Outlier ditemukan pada baris => 163, kolom => pelvic_incidence
Outlier ditemukan pada baris => 263, kolom => pelvic_tilt
Outlier ditemukan pada baris => 49, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 85, kolom => pelvic_radius
Outlier ditemukan pada baris => 189, kolom => pelvic_incidence
Outlier ditemukan pada baris => 173, kolom => pelvic_radius
Outlier ditemukan pada baris => 40, kolom => lumbar_lordosis_angle
Outlier ditemukan pada baris => 115, kolom => sacral_slope
Outlier ditemukan pada baris => 26, kolom => pelvic_incidence
===================================== DATA =====================================
Data pada baris yang mengandung outlier:
     pelvic_incidence  pelvic_tilt  lumbar_lordosis_angle  sacral_slope  \
7           45.366754    10.755611              29.038349     34.611142   
10          49.706610    13.040974              31.334500     36.665635   
11          31.232387    17.715819              15.500000     13.516568   
24          36.125683    22.758753              29.000000     13.366931   
26          26.147921    10.759454              14.000000     15.388468   
37          35.703458    19.443253              20.700000     16.260205   
39          52.419385    19.011561              35.872660     33.407825   
40          35.492446    11.701672              15.590363     23.790774   
49          41.767732    17.899402              20.030886     23.868330   
54          41.171680    17.321206              33.469403     23.850474   
75          70.221452    39.822724              68.118403     30.398728   
83          81.104100    24.794168              77.887020     56.309932   
85          45.443750     9.906072              45.000000     35.537678   
94          94.174822    15.380770              67.705721     78.794052   
95          57.522356    33.647075              50.909858     23.875281   
96          96.657315    19.461581              90.211498     77.195734   
112         42.021386    -6.554948              67.900000     48.576334   
115        129.834041     8.404475              48.384057    121.429566   
122         80.074914    48.069531              52.403439     32.005383   
141         89.504947    48.903653              72.003423     40.601295   
142         85.290173    18.278890             100.744220     67.011283   
147         55.080766    -3.759930              56.000000     58.840695   
151         48.030624     3.969815              58.344519     44.060809   
162        118.144655    38.449501              50.838520     79.695154   
163        115.923261    37.515436              76.800000     78.407825   
166         56.991404     6.874089              57.009005     50.117315   
167         72.343594    16.420790              59.869012     55.922805   
168         95.382596    24.822631              95.157633     70.559965   
173         50.825029     9.064729              56.300000     41.760300   
179         68.721910    49.431864              68.056012     19.290046   
180         37.903910     4.479099              24.710274     33.424811   
189         82.406524    29.276422              77.054565     53.130102   
192         74.469082    33.283157              66.942101     41.185925   
197         58.828379    37.577873             125.742385     21.250506   
206         95.480229    46.550053              59.000000     48.930176   
228         38.046551     8.301669              26.236830     29.744881   
235         63.929470    19.971097              40.177050     43.958373   
248         47.319648     8.573680              35.560252     38.745967   
252         42.918041    -5.845994              58.000000     48.764035   
257         50.160078    -2.970024              42.000000     53.130102   
263         33.788843     3.675110              25.500000     30.113733   
278         40.413366    -1.329412              30.982768     41.742778   
287         33.041688    -0.324678              19.071075     33.366366   
290         36.422485    13.879424              20.242562     22.543061   
304         45.075450    12.306951              44.583177     32.768499   

     pelvic_radius  degree_spondylolisthesis              class  
7       117.270067                -10.675871             Hernia  
10      108.648265                 -7.825986             Hernia  
11      120.055399                  0.499751             Hernia  
24      115.577116                 -3.237562             Hernia  
26      125.203296                -10.093108             Hernia  
37      137.540613                 -0.263490             Hernia  
39      116.559771                  1.694705             Hernia  
40      106.938852                 -3.460358             Hernia  
49      118.363389                  2.062963             Hernia  
54      116.377889                 -9.569250             Hernia  
75      148.525562                145.378143  Spondylolisthesis  
83      151.839857                 65.214616  Spondylolisthesis  
85      163.071041                 20.315315  Spondylolisthesis  
94      114.890113                 53.255220  Spondylolisthesis  
95      140.981712                148.753711  Spondylolisthesis  
96      120.673041                 64.080998  Spondylolisthesis  
112     111.585782                 27.338671  Spondylolisthesis  
115     107.690466                418.543082  Spondylolisthesis  
122     110.709912                 67.727316  Spondylolisthesis  
141     134.634291                118.353370  Spondylolisthesis  
142     110.660701                 58.884948  Spondylolisthesis  
147     109.915367                 31.773583  Spondylolisthesis  
151     125.350962                 35.000078  Spondylolisthesis  
162      81.024541                 74.043767  Spondylolisthesis  
163     104.698603                 81.198927  Spondylolisthesis  
166     109.978045                 36.810111  Spondylolisthesis  
167      70.082575                 12.072644  Spondylolisthesis  
168      89.307547                 57.660841  Spondylolisthesis  
173      78.999454                 23.041524  Spondylolisthesis  
179     125.018517                 54.691289  Spondylolisthesis  
180     157.848799                 33.607027  Spondylolisthesis  
189     117.042244                 62.765348  Spondylolisthesis  
192     146.466001                124.984406  Spondylolisthesis  
197     135.629418                117.314683  Spondylolisthesis  
206      96.683903                 77.283072  Spondylolisthesis  
228     123.803413                  3.885773             Normal  
235     113.065939                -11.058179             Normal  
248     120.576972                  1.630664             Normal  
252     121.606859                 -3.362045             Normal  
257     131.802491                 -8.290203             Normal  
263     128.325356                 -1.776111             Normal  
278     119.335655                 -6.173675             Normal  
287     120.388611                  9.354365             Normal  
290     126.076861                  0.179717             Normal  
304     147.894637                 -8.941709             Normal  

Visualisasi#

# Visualisasi outlier per fitur (jika ingin diaktifkan)
for col in data1.columns:
    plt.figure(figsize=(8, 3))
    plt.scatter(range(len(data1[col])), data1[col], label=col)
    plt.scatter(
        [i[0] for i in outlier_indices if i[1] == col],
        data1[col].iloc[[i[0] for i in outlier_indices if i[1] == col]],
        color='red', label="Outlier", s=50
    )
    plt.xlabel("Index")
    plt.ylabel(col)
    plt.title(f"Outlier Detection for {col}")
    plt.legend()
    plt.show()
_images/3d3a87b0bd5264131e9cc62959089ef6dbb9f3c4dd2ea26c67470ee10d6df700.png _images/511440ddbb8cdba3e2df81fa851d1d37d6210119b8671e05170982ba5dfdfb1f.png _images/f26ca8176c84bcfcf8cf9d7d0b6308fc8e4da7a02e5acec2709609e773a047d0.png _images/19ab98e14bb63968a8cb64f985ea39c4ad016a2b6c379be2f1b8898c1de98972.png _images/809db2227377dff0ee4747f3ed5ea29019605d4c5078e3a0855063e0cf91355c.png _images/d3dcebd764dfcc49ba3b1e0b431bd8bbeaeda68125672064f6f630d97d549ede.png

untuk penanganan outlier tidak kita lakukan dikarenakan data yang terindikasi outlier masih berada di rentang yang normal(tidak melebihi interval) dan Distribusi outlier menyebar di semua kelas dan Beberapa nilai negatif pada fitur tertentu

Klasifikasi#

Modelling Klasifikasi Gaussian Naive Bayes#

Modeling merupakan proses pembuatan dan pengujian model statistik atau matematis yang digunakan untuk menggambarkan dan menganalisis pola atau hubungan dalam data. Tujuan utama dari pemodelan dalam data mining adalah untuk mengidentifikasi pola yang berguna atau prediksi yang akurat dari data yang tersedia.Untuk modelling kali ini bertujuan untuk menentukan class pada suatu data inputan .Data akan dibagi menjadi 2 tipe yaitu data test dan data train dan pemodelan kali ini menggunakan metode Gaussian Naive Bayes.

Pada bagian ini akan ditentukan severity dari data yang akan saya inputan apakah termasuk jinak atau ganas.

cara kalkulasi GNB dapat melalui tahap berikut:

  1. Bagi Dataset menjadi data test dan data train

ada dua jenis pembagian rasio dataset yang sering digunakan yaitu:

  • 80% data train dan 20% data test.

  • 70% data train dan 30% data test.

Untuk kali ini kita akan menggunkan raiso 80% data train dan 20% data test,namun kalian bisa mengubahnya tergantung situasi dan kondisi dilapangan nantinya.

  1. Mengghitung Probabilitas pada data train setiap sheet

lakukan perhitungan probabilitas dari masing-masing kelas sesuai dengan jumlah data train pada kelas tersebut kemudian dibagi dengan banyaknya total data train.

ini digunakan untuk menghitung Prior

image.png

  1. Menghitung Mean dan Standart dev

hitung mean dan standart deviasi setiap fitur pada setiap kelas di data train kita

  1. menghitung Distribusi gaussian

lakukan perhitungan dengan rumus distribusi gaussian dengan rumus sebagai berikut:

image.png

image.png

  1. hitung posterior

Setelah didapat semua hasil dari distribusi gaussian, langkah selanjutnya adalah menentukan posteriori-nya. Berikut ini untuk rumusnya:

image.png

P(A|Hi) : hasil perkalian setiap fitur pada setiap kelas

P(a) : probabilitas setiap kelas

posteriori = P(A|Hi) * P(a)

  1. tentukan maximum posterior

setelah kita menghitung semua posterior maka dapat kita cari mana yang terbesar

data posterior paling besar tersebut yang merupakan akan menjadi class dari data yang kita inputkan

MODEL

import pandas as pd
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# Asumsikan data kamu sudah dalam bentuk dataframe "data"
# data = pd.concat([X, y], axis=1) ← sebelumnya sudah dilakukan

# Pisahkan fitur dan label
X = data[['pelvic_incidence', 'pelvic_tilt', 'lumbar_lordosis_angle',
          'sacral_slope', 'pelvic_radius', 'degree_spondylolisthesis']]
y = data['class']  # Label kolom

# Encode label jika masih teks
le = LabelEncoder()
y_encoded = le.fit_transform(y)

# Split data menjadi training dan testing
X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.2, random_state=42)

# Buat model Gaussian Naive Bayes
gnb_model = GaussianNB()

# Latih model
gnb_model.fit(X_train, y_train)

# Prediksi
y_pred = gnb_model.predict(X_test)

# Evaluasi
acc = accuracy_score(y_test, y_pred)
prec = precision_score(y_test, y_pred, average='macro')  # karena multiclass
rec = recall_score(y_test, y_pred, average='macro')
f1 = f1_score(y_test, y_pred, average='macro')

# Tampilkan hasil
print("========== HASIL EVALUASI NAIVE BAYES ==========")
print(f"Akurasi       : {acc:.2f}")
print(f"Presisi       : {prec:.2f}")
print(f"Recall        : {rec:.2f}")
print(f"F1-Score      : {f1:.2f}")

y_pred = gnb_model.predict(X_test)

print("=========================== X TRAINING =================================")
print(X_train)
print("=========================== X TESTING =================================")
print(X_test)
print("=========================== Y TESTING =================================")
print(y_test)
========== HASIL EVALUASI NAIVE BAYES ==========
Akurasi       : 0.87
Presisi       : 0.83
Recall        : 0.82
F1-Score      : 0.83
=========================== X TRAINING =================================
     pelvic_incidence  pelvic_tilt  lumbar_lordosis_angle  sacral_slope  \
126         70.676898    21.704402              59.181161     48.972496   
109         68.613001    15.082235              63.014696     53.530766   
247         49.828135    16.736435              28.000000     33.091700   
234         37.731992     9.386298              42.000000     28.345694   
202         76.314028    41.933683              93.284863     34.380345   
..                ...          ...                    ...           ...   
188         85.680950    38.650035              82.680977     47.030914   
71          86.900794    32.928168              47.794347     53.972627   
106         65.013773     9.838262              57.735837     55.175511   
270         51.311771     8.875541              57.000000     42.436230   
102         70.399308    13.469986              61.200000     56.929322   

     pelvic_radius  degree_spondylolisthesis  
126     103.008354                 27.810148  
109     123.431174                 39.497987  
247     121.435558                  1.913307  
234     135.740926                 13.683047  
202     132.267286                101.218783  
..             ...                       ...  
188     120.840707                 61.959034  
71      135.075364                101.719092  
106      94.738525                 49.696955  
270     126.472258                 -2.144044  
102     102.337524                 25.538429  

[248 rows x 6 columns]
=========================== X TESTING =================================
     pelvic_incidence  pelvic_tilt  lumbar_lordosis_angle  sacral_slope  \
289         44.430701    14.174264              32.243495     30.256437   
9           36.686353     5.010884              41.948751     31.675469   
57          46.855781    15.351514              38.000000     31.504267   
60          74.377678    32.053104              78.772013     42.324573   
25          54.124920    26.650489              35.329747     27.474432   
..                ...          ...                    ...           ...   
198         74.854480    13.909084              62.693259     60.945396   
195         71.241764     5.268270              85.999584     65.973493   
210         38.505273    16.964297              35.112814     21.540976   
224         89.834676    22.639217              90.563461     67.195460   
158         57.035097     0.345728              49.198003     56.689369   

     pelvic_radius  degree_spondylolisthesis  
289     131.717613                 -3.604255  
9        84.241415                  0.664437  
57      116.250917                  1.662706  
60      143.560690                 56.125906  
25      121.447011                  1.571205  
..             ...                       ...  
198     115.208701                 33.172255  
195     110.703107                 38.259864  
210     127.632875                  7.986683  
224     100.501192                  3.040973  
158     103.048698                 52.165145  

[62 rows x 6 columns]
=========================== Y TESTING =================================
[1 0 0 2 0 2 2 2 1 0 2 2 1 1 1 1 0 2 2 2 2 2 2 2 2 2 2 0 1 1 2 0 2 1 2 2 0
 0 2 2 0 0 2 1 1 2 1 0 1 2 2 1 2 2 2 1 1 2 2 1 1 2]

berikut cara menjadikan data train kita ke dalam bentuk csv.

import pandas as pd

# Concatenate X_train and y_train as new column
train_data = pd.concat([X_train, pd.Series(y_train, name='Severity')], axis=1)

# Save training data to CSV with tab-separated values
train_data.to_csv('Train_mammographic_mass.csv', sep="\t", index=False)

uji coba inputan dengan data test menggunakan model yang kita buat.

# Menampilkan data asli baris ke-231 (baris ke-230 karena index dimulai dari 0)
print("Data yang akan digunakan adalah:")
print(data.iloc[230])  # data adalah DataFrame gabungan X dan y

# Contoh input baru dengan 6 fitur (sesuai struktur data)
# Format: [pelvic_incidence, pelvic_tilt, lumbar_lordosis_angle, sacral_slope, pelvic_radius, degree_spondylolisthesis]
input_data = [[85.0, 20.0, 75.0, 65.0, 120.0, 60.0]]

# Prediksi kelas menggunakan model Gaussian Naive Bayes
predicted_class_index = gnb_model.predict(input_data)[0]
predicted_class_label = le.inverse_transform([predicted_class_index])[0]  # kembalikan ke label teks

print("Hasil prediksi:")
print(f"Data yang diinputkan diprediksi sebagai class = {predicted_class_label}")
Data yang akan digunakan adalah:
pelvic_incidence             65.611802
pelvic_tilt                  23.137919
lumbar_lordosis_angle        62.582179
sacral_slope                 42.473883
pelvic_radius               124.128001
degree_spondylolisthesis     -4.083298
class                           Normal
Name: 230, dtype: object
Hasil prediksi:
Data yang diinputkan diprediksi sebagai class = Spondylolisthesis
/home/codespace/.local/lib/python3.12/site-packages/sklearn/utils/validation.py:2739: UserWarning: X does not have valid feature names, but GaussianNB was fitted with feature names
  warnings.warn(
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# Evaluasi model
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='macro')   # rata-rata presisi semua kelas
recall = recall_score(y_test, y_pred, average='macro')         # rata-rata recall semua kelas
fscore = f1_score(y_test, y_pred, average='macro')             # rata-rata F1 semua kelas

# Menampilkan hasil evaluasi
print("========== EVALUASI MODEL NAIVE BAYES ==========")
print(f'Akurasi : {accuracy:.2f}')
print(f'Presisi : {precision:.2f}')
print(f'Recall  : {recall:.2f}')
print(f'F1-Score: {fscore:.2f}')
========== EVALUASI MODEL NAIVE BAYES ==========
Akurasi : 0.87
Presisi : 0.83
Recall  : 0.82
F1-Score: 0.83

Modelling Klasifikasi KNN#

K-Nearest Neighbors (KNN) merupakan sebuah cara untuk mengklasifikasikan dengan cara melihat sesuatu yang berada di dekatnya. KNN juga disebut dengan Algoritna oembelajar malas. Karena tidak memerlukan pembelajaran terlebih dahulu, dan langsung meyimpan data set dan pada saat klaifikasi melakukan set data. KNN bekerja dengan menggunakan kedekatan dan pemungutan suara mayoritas untuk membuat prediksi atau forecasting. Pada KNN terdapat istilah “k”. “k” merupakan angka yang memberi tahu algoritma beberapa banya titik terdekat (tetangga) yang digunakan untuk membuat keputusan. Contohnya: Misalkan menntukan nama buah dan membandingkannya dengan buah yang telah dikenal. kemudian menggunakan nilai “k” sebanyak 3 kemudian 2 dari 3 merupakan buah apel dan 1 dari 3 merupakan buah pisang. jadi algoritma ini mengatakan bahwa buah tersebut merupakan apel karena sebagian besar tetangganya apel.

Menentukan Nilai “k”#

Dalam KNN, pemilihan nilai “k” sangat penting untuk menentukan hipotesis hasil dari prediksi. Jika kumpulan data memiliki outlier atau noise yang signifikan, nilai “k” yang lebih tinggi dapat membantu memperhalus prediksi dan mengurangi data yang noise. Namun, pemilihan nilai yang tinggi dapat menyebabkan underfitting.

Pertama, kita harus menentukan nilai K terlebih dahulu. Penentuan nilai K ini tidak ada rumus pastinya. Namun satu tips yang dapat dipertimbangkan, yakni jika kelas berjumlah genap maka sebaiknya nilai K-nya ganjil, sebaliknya jika kelas berjumlah ganjil maka sebaiknya nilai K-nya genap. Dalam prakteknya di Python, Anda dapat menghitung menggunakan kode program untuk mencari nilai K terbaik dari berbagai opsi nilai (misalnya dari K=2 sampai K=10).

Hitung jarak antara data baru dan masing-masing data lainnya#

menghitung jarak menggunakan metode Euclidean distance

image.png

Jika ada lebih dari satu, kita dapat menjumlahkannya seperti di bawah ini.

image.png

Ambil tiga data dengan jarak terdekat. Dari perhitungan Euclidean distance di atas, jika kita rangkum dari jarak terdekat

Impelementasi KNN menggunakan Scikit Learn#

from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report

# Gunakan data yang sudah disiapkan sebelumnya:
# X, y_encoded, le → label encoder
# X_train, X_test, y_train, y_test → hasil train_test_split

# Buat model KNN
knn_model = KNeighborsClassifier(n_neighbors=5)

# Latih model
knn_model.fit(X_train, y_train)

# Prediksi
y_pred_knn = knn_model.predict(X_test)

# Evaluasi
accuracy_knn = accuracy_score(y_test, y_pred_knn)
precision_knn = precision_score(y_test, y_pred_knn, average='macro')
recall_knn = recall_score(y_test, y_pred_knn, average='macro')
f1_knn = f1_score(y_test, y_pred_knn, average='macro')

# Tampilkan hasil
print("========== EVALUASI MODEL KNN ==========")
print(f'Akurasi : {accuracy_knn:.2f}')
print(f'Presisi : {precision_knn:.2f}')
print(f'Recall  : {recall_knn:.2f}')
print(f'F1-Score: {f1_knn:.2f}')

# Optional: Report klasifikasi
print("\n=== Classification Report KNN ===")
print(classification_report(y_test, y_pred_knn, target_names=le.classes_))
========== EVALUASI MODEL KNN ==========
Akurasi : 0.84
Presisi : 0.78
Recall  : 0.77
F1-Score: 0.77

=== Classification Report KNN ===
                   precision    recall  f1-score   support

           Hernia       0.64      0.58      0.61        12
           Normal       0.72      0.72      0.72        18
Spondylolisthesis       0.97      1.00      0.98        32

         accuracy                           0.84        62
        macro avg       0.78      0.77      0.77        62
     weighted avg       0.83      0.84      0.84        62

Modelling Klasifikasi Decision Tree#

Decision Tree (Pohon Keputusan) adalah algoritma machine learning yang membangun model prediksi dalam bentuk struktur pohon. Algoritma ini memecah dataset menjadi himpunan data yang lebih kecil dan lebih murni secara bertahap, di mana setiap pemecahan didasarkan pada fitur data yang memberikan informasi paling banyak.


Konsep dan Rumus Inti (Decision Tree - C4.5 / ID3)#

1. Entropy (Ukuran Ketidakpastian)#

Entropy digunakan untuk mengukur seberapa acak atau tidak murni suatu set data. Nilai 0 artinya seluruh data dalam satu kelas (murni), sedangkan nilai lebih tinggi artinya campuran berbagai kelas.

\[Entropy(S) = \sum_{i=1}^{c} -p_i \log_2(p_i)\]
  • \(p_i\) = proporsi jumlah data pada kelas ke-i dari total data.

2. Information Gain (Pengurangan Ketidakpastian)#

Information Gain digunakan untuk menentukan seberapa efektif suatu fitur dalam memisahkan data. Fitur dengan gain tertinggi akan dipilih sebagai node cabang.

\[Gain(S, A) = Entropy(S) - \sum_{v \in \text{Values}(A)} \frac{|S_v|}{|S|} \cdot Entropy(S_v)\]

Penerapan pada Dataset Vertebral Column#

Dataset ini terdiri dari 3 kelas utama:

  • Normal

  • Hernia

  • Spondylolisthesis

Dan 6 fitur numerik:

  • pelvic_incidence

  • pelvic_tilt

  • lumbar_lordosis_angle

  • sacral_slope

  • pelvic_radius

  • degree_spondylolisthesis

Karena fitur-fitur pada dataset ini bersifat numerik, maka:

  • Decision Tree akan melakukan pemisahan berbasis nilai threshold, misalnya:
    pelvic_incidence <= 65.5 kiri, > 65.5 kanan.


Contoh Proses Pembangunan Pohon#

Langkah 1: Menghitung Entropy Awal (Entropy(S))#

Misalkan:

  • Jumlah data = 310

  • Distribusi kelas:

    • Normal = 100

    • Hernia = 60

    • Spondylolisthesis = 150

\[ Entropy(S) = -\frac{100}{310}\log_2(\frac{100}{310}) - \frac{60}{310}\log_2(\frac{60}{310}) - \frac{150}{310}\log_2(\frac{150}{310}) \approx 1.47 \]

Langkah 2: Menghitung Gain untuk Setiap Fitur#

Misal, untuk fitur pelvic_incidence, data dipecah pada threshold tertentu:

  • \(S_1\): data dengan pelvic_incidence <= 65.5

  • \(S_2\): data dengan pelvic_incidence > 65.5

Hitung entropy masing-masing subset lalu gunakan formula:

\[ Gain(S, pelvic\_incidence) = Entropy(S) - \left( \frac{|S_1|}{|S|}Entropy(S_1) + \frac{|S_2|}{|S|}Entropy(S_2) \right) \]

Lakukan hal yang sama untuk fitur lain. Fitur dengan Gain tertinggi akan dijadikan node cabang.


Contoh Struktur Pohon Hasil Training (Misal)#

degree_spondylolisthesis <= 25.3?
|
|--- yes ---> class: Normal
|
|--- no ----> pelvic_incidence <= 80.0?
|
|--- yes ---> class: Hernia
|
|--- no ----> class: Spondylolisthesis

Implementasi Decision Tree dengan Scikit-learn#

Setelah kita memahami cara kerja Decision Tree secara manual dengan menghitung Entropy dan Information Gain, kini saatnya kita melihat bagaimana proses ini dilakukan secara otomatis menggunakan library machine learning populer, yaitu Scikit-learn (sklearn). Scikit-learn menyediakan implementasi Decision Tree yang efisien dan siap pakai.

Kita akan menggunakan data yang sudah didiskretisasi sebelumnya, lalu melatih model DecisionTreeClassifier dari Scikit-learn untuk membangun pohon keputusan. Hasil pohon yang dibangun oleh Scikit-learn ini seharusnya konsisten dengan logika perhitungan manual yang sudah kita lakukan.

Terakhir, kita akan memvisualisasikan pohon yang dihasilkan oleh Scikit-learn untuk melihat strukturnya secara grafis.

import pandas as pd
from ucimlrepo import fetch_ucirepo
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report
import matplotlib.pyplot as plt

# Fetch dataset dari UCI
vertebral_column = fetch_ucirepo(id=212)

# Ekstrak fitur dan target
X = vertebral_column.data.features
y = vertebral_column.data.targets

# Encode label ('Normal', 'Hernia', 'Spondylolisthesis') menjadi angka
le = LabelEncoder()
y_encoded = le.fit_transform(y)

# Bagi data menjadi train dan test
X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.2, random_state=0)
/home/codespace/.local/lib/python3.12/site-packages/sklearn/preprocessing/_label.py:110: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)

Training Decision Tree#

# Buat dan latih model Decision Tree
dt_model = DecisionTreeClassifier(criterion='entropy', max_depth=4, random_state=0)
dt_model.fit(X_train, y_train)

# Prediksi
y_pred_dt = dt_model.predict(X_test)

Evaluasi Model Decision TRee#

# Evaluasi kinerja
accuracy_dt = accuracy_score(y_test, y_pred_dt)
precision_dt = precision_score(y_test, y_pred_dt, average='macro')
recall_dt = recall_score(y_test, y_pred_dt, average='macro')
f1_dt = f1_score(y_test, y_pred_dt, average='macro')

# Tampilkan hasil evaluasi
print("========== EVALUASI MODEL DECISION TREE ==========")
print(f'Akurasi : {accuracy_dt:.2f}')
print(f'Presisi : {precision_dt:.2f}')
print(f'Recall  : {recall_dt:.2f}')
print(f'F1-Score: {f1_dt:.2f}')

# Tampilkan klasifikasi per kelas
print("\n=== Classification Report ===")
print(classification_report(y_test, y_pred_dt, target_names=le.classes_))
========== EVALUASI MODEL DECISION TREE ==========
Akurasi : 0.76
Presisi : 0.72
Recall  : 0.68
F1-Score: 0.67

=== Classification Report ===
                   precision    recall  f1-score   support

           Hernia       0.67      0.31      0.42        13
           Normal       0.60      0.79      0.68        19
Spondylolisthesis       0.90      0.93      0.92        30

         accuracy                           0.76        62
        macro avg       0.72      0.68      0.67        62
     weighted avg       0.76      0.76      0.74        62

Visualisasi Pohon Keputusan#

# Visualisasi pohon keputusan
plt.figure(figsize=(20,10))
plot_tree(dt_model,
          feature_names=X.columns,
          class_names=le.classes_,
          filled=True,
          rounded=True)
plt.title("Visualisasi Pohon Keputusan (Decision Tree)")
plt.show()
_images/219044f279bb8e333c609eb32414f75663b534f72b2ce85f0c8375c778485934.png

Feature paling berpengaruh: degree_spondylolisthesis, sacral_slope, pelvic_tilt, pelvic_radius.

Model kuat memprediksi Spondylolisthesis, cukup baik untuk Normal, dan perlu perhatian lebih untuk Hernia.

Evaluasi#

Dari ketiga model yang dibuat dapat dilihat akurasi dari masing-masing model, yaitu:

  • Naive Bayes: memiliki akurasi 0.87

  • KNN: memiliki akurasi 0.84

  • Decision Tree: memiliki akurasi 0.76

Dari ketiga model diatas dapat disimpulkan bahwa single model Naive Bayes memiliki akurasi tertinggi yaitu 87%.