Prediksi Serangan Jantung dengan KNN Classification

Muhammad Fadhlan
11 min readFeb 20, 2021

--

Pertama kita coba mengenal data yang akan kita gunakan. Di sini saya menggunakan dataset tentang heart attack atau serangan jantung dari kaggle atau dari UCI .

Dataset ini aslinya ada 76 atribut tetapi hanya 14 atribut saja yang dipublikasikan. kolom ‘target’ merupakan kolom target dengan ketentuan bahwa 0 tidak atau kurang beresiko terkena serangan jantung, sedangkan 1 lebih beresiko terkena serangan jantung.

Informasi Atribut :
1. age = umur dalam tahun
2. sex = jenis kelamin (0 = perempuan, 1 = laki — laki)
3. cp = chest pain type / tipe nyeri dada (4 nilai yaitu 0, 1, 2, 3)
— Nilai 0: typical angina,
— Nilai 1: atypical angina,
— Nilai 2: non-anginal pain,
— Nilai 3: asymptomatic.
4. trestbps = resting blood pressure / tekanan darah (dalam mm Hg)
5. chol = serum kolesterol dalam mg/dl
6. fbs = fasting blood sugar / gula darah saat puasa : Jika lebih dari 120 mg/dl maka nilainya 1 (true), jika tidak 0 (false)
7. restecg = resting electrocardiographic results / hasil elektrokardiografi saat istirahat (nilai 0,1,2)
— Nilai 0: normal,
— Nilai 1: memiliki kelainan gelombang ST-T (inversi gelombang T dan / atau elevasi atau depresi ST> 0,05 mV),
— Nilai 2: menunjukkan kemungkinan atau pasti hipertrofi ventrikel kiri menurut kriteria Estes.
8. thalach = maximum heart rate achieved / detak jantung maksimum.
9. exang = exercise induced angina / angina yang diinduksi (1=iya, 0, tidak).
10. oldpeak = ST depression induced by exercise relative to rest / ST Depression disebabkan oleh latihan yang berhubungan dengan istirahat.
11. slope = the slope of the peak exercise ST segment / kemiringan puncak latihan dari ST Segment.
— Nilai 0: upsloping / miring ke atas
— Nilai 1: flat
— Nilai 2: downsloping / miring ke bawah
12. ca = number of major vessels colored by flourosopy / nomor dari pembuluh utama (0,1,2,3,4)
13. thal = 0 = normal; 1 = fixed defect/cacat tetap; 2 = reversible defect/cacat yang bisa dipulihkan.
14. target: 0= kurang beresiko terkena serangan jantung; 1= lebih beresiko terkena serangan jantung.

Setelah itu kita langsung saja eksekusi dataset tersebut!

Kita import library awal yang diperlukan untuk data preprocessing

#importing library
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

Kemudian kita baca dataset yang berekstensi csv tersebut dan memasukkannya ke dalam dataframe menggunakan bantuan library pandas.

df_heart = pd.read_csv('heart.csv')

Lalu kita lihat data tersebut dengan fungsi head() yang menampilkan 5 data teratas. Ketika kita run maka akan tampil seperti di bawah ini.

df_heart.head()

Cek data tersebut dengan fungsi info(). Fungsi ini mencetak informasi tentang DataFrame termasuk index dtype dan column dtypes, non-null values dan penggunaan memori.

df_heart.info()
terlihat bahwa ada 303 data dengan 14 kolom juga masing — masing tipe data pada kolomnya dan juga penggunaan memori. Dengan itu juga kita tahu bahwa tidak ada nilai kosong pada data tersebut.

Menggunakan fungsi describe() untuk melihat beberapa statistik dasar seperti jumlah data, persentil, mean, std, min, max.

df_heart.describe()

untuk mengetahui apakah ada nilai null pada setiap kolom bisa menggunakan cara ini

df_heart.isnull().sum()
dataset sudah bersih dari nilai null atau kosong

Kita periksa apakah ada data duplikat dengan fungsi duplicated(). pada data ini terdapat satu duplikat.

dups_data = df_heart.duplicated()
print(‘Jumlah baris yang ada duplikat: %d’ % dups_data.sum())
kode tersebut akan menghasilkan output seperti ini

Selanjutnya kita hapus data duplikat tersebut dengan fungsi drop_duplicates(). maka data yang awalnya 303 berkurang menjadi 302.

#drop baris yang ada duplikat
print('Jumlah baris sebelum membuang duplikat = %d' % df_heart.shape[0])
data_heart = df_heart.drop_duplicates()
print('Jumlah baris setelah membuang duplikat = %d' % data_heart.shape[0])
output dari kode di atas

Visualisasi Data

Selanjutnya kita visualisasi setiap kolom yang kategorikal dengan fungsi countplot dari seaborn. Ada 8 kolom yang merupakan data kategorikal. Namun disini datasetnya sudah diubah kedalam bentuk integer. Masing — masing angka sudah dijelaskan di atas.

kolom_kategori = ['sex', 'cp', 'fbs', 'restecg', 'exang', 'slope', 'ca', 'thal']
plt.figure(figsize=(15,10))
for i in range(0,8):
plt.subplot(3,3,i+1)
sns.countplot(data_heart[kolom_kategori[i]], hue ='target', data = data_heart)
plt.legend(['Resiko Rendah', 'Resiko Tinggi'])

Penjelasannya visualisasinya sebagai berikut :
1. sex => perempuan (0) resiko tinggi terkena serangan jantung lebih besar. laki — laki (1) resiko rendah terkena serangan jantung lebih besar. Namun resiko tinggi terkena serangan jantung tidak berbeda jauh.

2. cp => tipe nyeri dada dengan tipe 0 itu lebih rendah resiko serangan jantungnya. Tipe 1 lebih besar resiko serangan jantungnya. Begitu pula dengan tipe 2 dan 3.

3. fbs => Gula darah yang kurang dari 120 mg/dl (0) lebih besar resiko serangan jantungnya walau tidak jauh atau tidak signifikan dari resiko rendahnya. Gula darah yang lebih dari 120 mg/dl (1) memiliki resiko tinggi dan resiko rendah yang seimbang.

4. restecg => hasil elektrokardiografi saat istirahat pada tipe 0 lebih kecil resiko serangan jantung. Pada tipe 1 lebih besar resiko serangan jantung. Tipe 2 datanya terlalu sedikit dibanding tipe 0 dan 1.

5. exang => angina yang tidak diinduksi (0) lebih besar resiko serangan jantungnya. Angina yang diinduksi (1) resikonya rendah.

6. slope => Tipe 0 datanya terlalu sedikit. Tipe 1 resikonya rendah. Sedangkan Tipe 2 resikonya tinggi.

7. ca => Terlihat bahwa tipe 0 resikonya tinggi. Sedangkan tipe 1, 2, dan 3 resikonya rendah. Tipe 4 terlalu sedikit datanya.

8. thal => Tipe 2 resikonya tinggi. Sedangkan tipe 1, dan 3 resikonya rendah

Kemudian, kita lihat tingkat serangan jantung pada kolom ‘sex’ dengan melakukan pengelompokkan dengan aggregat mean pada setiap data target yaitu 0 dan 1 atau Perempuan dan Laki — laki. Hasilnya seperti di bawah ini. Terlihat bahwa Perempuan mempunyai tingkat serangan jantung yang lebih tinggi yaitu 0.75 sedangkan laki — laki sekitar 0.44.

df_sex_target = data_heart.groupby('sex')['target'].mean().reset_index()
sns.barplot(df_sex_target.sex, df_sex_target.target)
plt.ylabel('tingkat serangan jantung')
plt.xlabel('Jenis Kelamin')
plt.xticks(df_sex_target.sex,['Perempuan', 'Laki - Laki'])
plt.text(-0.1, 0.8, df_sex_target.target[0])
plt.text(0.65, 0.5, df_sex_target.target[1])
plt.show()

Di sini kita coba buat kolom baru yaitu ‘kelompok_umur’ dengan 3 range yaitu 29–49, 50–69, dan 70+. Terlihat pada gambar di bawah didominasi sekitar 66,9% pada range 50–69.

bins = [29, 50, 70, 77]
labels = ['29 - 49', '50 - 69', '70+']
data_heart['kelompok_umur'] = pd.cut(data_heart['age'] , bins=bins, labels = labels, include_lowest = True)
#pie chart
plt.pie(data_heart['kelompok_umur'].value_counts(), autopct="%.1f%%")
plt.legend(data_heart['kelompok_umur'].unique(),bbox_to_anchor=(0.00, 1))
plt.title('Kelompok umur ', loc='center', pad=10, fontsize=15)
plt.show()

Terlihat di bawah tingkat serangan jantung paling tinggi ada pada kelompok umur 70 lebih dengan angka 0.833333. Urutan kedua ada pada umur 29–49 dengan angka 0.691489. Justru yang paling rendah tingkat serangan jantungnya pada 50–69 dengan angka 0.465347.

df_kelompok_umur_target = data_heart.groupby('kelompok_umur')['target'].mean().reset_index()
sns.barplot(df_kelompok_umur_target.kelompok_umur, df_kelompok_umur_target.target)
plt.ylabel('tingkat serangan jantung')
plt.xlabel('kelompok_umur')
plt.show()

Kita lihat juga korelasi antar kolom dengan menggunakan bantuan library seaborn. Terlihat pada gambar di bawah ini jika warna pada kotak semakin gelap (biru tua) maka korelasi antar kolomnya semakin tinggi. sebaliknya, jika warna pada kotak semakin terang (kuning cerah) maka korelasi antar kolomnya semakin rendah. Di sini menggunnakan fungsi heatmap() dengan beberapa parameter, korelasi merupakan data yang ingin divisualisasikan. mask untuk memplot sebagian matriks (tidak penuh, sehingga bentuknya seperti tangga, bisa dilihat di dokumentasi searbon). cmap untuk mengganti warna dari plot tersebut. annot untuk menampilkan angka korelasi antar kolom. linewidth merupakan ketebalan garis yang membagi setiap sel.

fitur = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal']
korelasi = data_heart[fitur].corr()
mask = np.zeros_like(korelasi)
mask[np.triu_indices_from(mask)] = True
plt.figure(figsize= (12, 10))
sns.heatmap(korelasi, mask = mask, cmap = 'YlGnBu', annot = True, linewidth = .5, square = True)
plt.yticks(rotation=0)
plt.show()

Standarisasi

Di sini kita menggunakan StandarScaler fungsi dari library sklearn. buang kolom ‘kelompok_umur’ dengan fungsi drop(). Lalu kita fit dan transform data_heart dengan fungsi fit_transform() tersebut tanpa kolom ‘target’

from sklearn.preprocessing import StandardScaler#kita buang kelompok umur, karena sebelumnya kita menambahkan kolom
#'kelompok_umur'
data_heart = data_heart.drop('kelompok_umur', axis=1)
std_scaler = StandardScaler()
fitur_scaled = std_scaler.fit_transform(data_heart.drop('target',axis=1))

Lalu kita masukkan ke dalam df_scaled dan mengubahnya menjadi DataFrame. Tampilkan 5 data teratas dengan fungsi head(). Data yang tadinya punya nilai dengan range yang berbeda — beda, sekarang nilainya mempunyai range yang sama.

df_scaled = pd.DataFrame(fitur_scaled,columns=data_heart.columns[:-1])
df_scaled.head()

Train Test Split

Import train_test_split dari sklearn. Gunakan fungsi train_test_split() lalu kita tampung ke dalam 4 variabel X_train, X_test, y_train, y_test. Test sizenya sebesar 0.20 artinya ada 80% data yang menjadi data train dan 20% menjadi data test dengan random_state = 45 untuk menghidari nilai yang berbeda ketika melakukan train dan test data. Jangan lupa juga untuk mentransform X_test. kita coba lihat berapa jumlah data yang sudah di split dengan print shapenya.

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(df_scaled, data_heart['target'], test_size=0.20, random_state=45)
X_test = std_scaler.transform(X_test)
print(X_train.shape)
print(X_test.shape)
Data Train ada 241 baris dan 13 kolom, data test ada 61 baris dan 13 kolom

KNN

Lakukan Import KNeighborsClassifier dari sklearn untuk melakukan prediksi dengan KNN. Di sini kita coba dengan n_neighbors = 1.

from sklearn.neighbors import KNeighborsClassifierknn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train,y_train)
pred = knn.predict(X_test)

Selanjutnya kita import classification_report, confusion_matrix, dan accuracy_score dari sklearn. Confusion matrix dihitung untuk mengevaluasi keakuratan klasifikasi dimana jumlah true negatives pada array(0,0) yaitu 17, jumlah false negatives pada array(0,1) yaitu 10, jumlah false positives pada array (1,0) yaitu 5, dan jumlah true positives pada array(1,1) yaitu 29. Sedangkan classification report untuk menampilkan laporan teks klasifikasi metrik utama yaitu precision, recall, f1-score, dan support. terlihat disini accuracy dengan n_neighbors = 1 adalah 0.75

from sklearn.metrics import classification_report, confusion_matrix, accuracy_scoreprint('Confusion Matrix :\n', confusion_matrix(y_test,pred))
print('Classification Report :\n', classification_report(y_test,pred))

Mencari nilai n_neighbors terbaik

Cara pertama kita gunakan GridSearchCV untuk menemukan n_neighbors terbaik. GridSearchCV melakukan pencarian menyeluruh atas nilai parameter tertentu untuk estimator yang disini merupakan knn. parameternya yaitu n_neighbors 1–49, cv alias cross-validation dengan nilai 5 atau 5-Fold, verbose dengan input 3 artinya menampilkan fold, indeks parameternya, scorenya, dan waktu komputasi. Lalu dapatkan n_neighbors terbaik dengan best_params maka terlihat bahwa n_neighbors terbaik yang didapatkan GridSearchCV adalah 24

from sklearn.model_selection import GridSearchCVknn = KNeighborsClassifier()
parameter_knn = {'n_neighbors': np.arange(1,50)}
#mencari k-values terbaik dengan GridSearchCV
knn_cv_model = GridSearchCV(knn, parameter_knn, cv = 5, verbose=3).fit(X_train, y_train)
n_neig = knn_cv_model.best_params_['n_neighbors']print('K value terbaik berdasar GridSearchCV adalah : ', n_neig)
5 Fold = 5 kali perulangan pada setiap kandidat (49 kandidat dari 1–49).

Cara Kedua dengan melakukan perbandingan error rate pada setiap nilai n_neighbors dimana disini di atur 1–39. Lalu kita visualisasi dengan plot dari matplotlib.

def find_k_values(X_train, y_train):
error_rate = []
for i in range(1,40):
knn = KNeighborsClassifier(n_neighbors=i)
knn.fit(X_train,y_train)
pred_i = knn.predict(X_test)
error_rate.append(np.mean(pred_i != y_test))

plt.figure(figsize=(10,6))
plt.plot(range(1,40),error_rate,color='green', linestyle='dashed', marker='o',
markerfacecolor='red', markersize=10)
plt.title('Error Rate vs. K Value')
plt.xlabel('K Value')
plt.ylabel('Error Rate')
plt.show()

Terlihat nilai dengan error rate terkecil adalah pada n_neighbor atau K Value 35.

find_k_values(X_train, y_train)

Maka di sini kita menggunakan K value 35. Lakukan prediksi lagi. Terlihat Accuracynya naik menjadi 0.85 karena sudah menggunakan n_neighbors yang terbaik. Terlihat juga pada confusion matrixnya dimana jumlah true negatives pada array(0,0) yaitu 19 (bertambah 2), jumlah false negatives pada array(0,1) yaitu 8 (berkurang 2), jumlah false positives pada array (1,0) yaitu 1 (berkurang 4), dan jumlah true positives pada array(1,1) yaitu 33 (bertambah 4).

knn_new = KNeighborsClassifier(n_neighbors = 35).fit(X_train, y_train)
y_pred = knn_new.predict(X_test)
print('Accuracy score for KNN: {}\n'.format(accuracy_score(y_test, y_pred)))
print(confusion_matrix(y_test,y_pred))
print(classification_report(y_test,y_pred))

Principal Component Analysis (PCA)

PCA dapat melakukan reduksi dimensi jika variabel berkorelasi. Jika tidak, PCA hanya mengurutkannya sesuai variansinya. Dalam PCA Data harus discale terlebih dahulu, oleh karena itu kita gunakan data df_scaled di atas karena sudah di scale.

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
x = df_scaled.loc[:, fitur].values
data = pca.fit_transform(x)
pca_df = pd.DataFrame(data = data, columns = ['PCA1', 'PCA2'])final_df = pd.concat([pca_df, data_heart[['target']]], axis = 1)
final_df.dropna(subset = ['PCA1','PCA2','target'], inplace=True)
final_df
fig = plt.figure(figsize = (8,8))
ax = fig.add_subplot(1,1,1)
ax.set_xlabel('Principal Component 1', fontsize = 15)
ax.set_ylabel('Principal Component 2', fontsize = 15)
ax.set_title('2 Component PCA', fontsize = 20)
targets = [0.0, 1.0]
colors = ['r', 'g']
for target, color in zip(targets,colors):
index = final_df['target'] == target
ax.scatter(final_df.loc[index, 'PCA1']
, final_df.loc[index, 'PCA2']
, c = color
, s = 50)
ax.legend(['Resiko Rendah', 'Resiko Tinggi'])
ax.grid()

Terlihat di atas Resiko Rendah (0) dengan Resiko Tinggi (1) masih ada yang tidak terpisah dengan baik.

Explained variance memberi tahu berapa banyak informasi (varians) yang dapat dikaitkan ke masing-masing principal components.

Secara bersama 2 PCA tersebut memiliki 33.16% dari informasi , PCA1 berisi 21,31% varian dan PCA2 berisi 11,85% varian. Persentase dari varian tersebut cukup kecil.

pca.explained_variance_ratio_

Streamlit.io

Streamlit.io bisa mengubah kode yang telah kita kerjakan entah di jupyter notebook, google colab notebook, atau yang lainnya menjadi aplikasi berbasis web lebih cepat, tentunya menggunakan python dan gratis. Streamlit.io ini kompatibel dengan berbagai macam libraries dan frameworks seperti scikit.learn, keras, bokeh, tensorflow, pytorch, opencv, dan lain sebagainya.

Cara installnya cukup mudah jalankan perintah$ pip install streamlit pada command prompt atau notebook yang kalian gunakan.

Lalu cara menggunakannya cukup panggil import streamlit as st pada file python kalian. Ada banyak fitur yang disediakan oleh streamlit.io misalnya untuk menampilkan teks yaitu text, markdown, latex, title, header, subheader, code. Untuk menampilkan data ada dataframe, table, dan json. Menampilkan chart juga bisa, line chart, area chart, bar chart bahkan map. Menampilkan media ada seperti image, audio, dan video. Juga menggunakan widget yang interaktif, button, checkbox, radio, selectbox, slider, text, number, date, time, file uploader, dan lain sebagainya. Cukup lengkap apabila ingin mendeploy model yang telah kita buat. Sisanya masih banyak bisa dilihat di cheatsheetnya. Aplikasi Web dengan streamlit.io telah saya simpan di repository.

Deploy dengan bantuan Heroku

Kita akan mencoba mendeploy aplikasi web tersebut dengan heroku.
Langkah pertama pastikan sudah mempunyai akun heroku.

Pada kesempatan kali ini saya menggunakan command line untuk mendeploynya.

Langkah kedua karena menggunakan command line dari heroku. Pastikan juga sudah mendownloadnya (Heroku CLI).

Langkah ketiga masuk kedalam direktori file yang berisi aplikasi web. Selanjutnya membuat repositori Git baru dengan cara :

$git init

Langkah kelima login ke heroku dengan mengetikkan perintah

$heroku login

Jika sudah berhasil login, ketikkan perintah di bawah ini.

$heroku create [nama-aplikasi]

jika [nama-aplikasi] tidak diisi. Maka heroku akan secara otomatis memberinya nama.

Setelah itu kita ketik perintah di bawah ini untuk menambahkan file baru atau file yang diubah dalam direktori kerja kita ke area staging Git.

$git add .

Selanjutnya kita melakukkan commit dengan pesan sesuai yang kita ingin tulis. Perintah git commit akan menyimpan perubahan — perubahan yang sedang dilakukan dalam proyek git.

$git commit -m "messages"

Terakhir, kita lakukan push ke heroku. Perintah git push di sini digunakan untuk mengupload file — file dalam repositori lokal kita ke repositori remote milik Heroku. Sehingga, kita bisa melihatnya secara online, tidak di local environment lagi

$git push heroku master

Aplikasi Web dengan streamlit.io

https://muhfadh-streamlit-heart-attack-a-heart-attack-prediction-6xw5tb.streamlitapp.com/

Mungkin modelling pada aplikasi web tersebut masih banyak kurangnya. Tapi, setidaknya sudah bisa mencoba untuk men-deploy prediksi serangan jantung dengan KNN classification dan bisa dilihat secara online.

--

--

Muhammad Fadhlan
Muhammad Fadhlan

Written by Muhammad Fadhlan

Purportedly Data Science Enthusiast

Responses (1)