From 47bbbece4744163f43eb85c05ec406a0a8641466 Mon Sep 17 00:00:00 2001 From: Sandi Indika Saputra Date: Wed, 1 May 2024 00:34:40 +0700 Subject: [PATCH] first commit --- .gitignore | 3 + .streamlit/config.toml | 9 + CHANGELOG.md | 3 + Fia Qonita.code-workspace | 8 + LICENSE.txt | 21 + README.md | 13 + RUN.bat | 6 + css/style.css | 31 ++ requirements.txt | 6 + src/__pycache__/functions.cpython-311.pyc | Bin 0 -> 20140 bytes src/__pycache__/functions.cpython-312.pyc | Bin 0 -> 18051 bytes src/app.py | 419 ++++++++++++++++++++ src/functions.py | 462 ++++++++++++++++++++++ 13 files changed, 981 insertions(+) create mode 100644 .gitignore create mode 100644 .streamlit/config.toml create mode 100644 CHANGELOG.md create mode 100644 Fia Qonita.code-workspace create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 RUN.bat create mode 100644 css/style.css create mode 100644 requirements.txt create mode 100644 src/__pycache__/functions.cpython-311.pyc create mode 100644 src/__pycache__/functions.cpython-312.pyc create mode 100644 src/app.py create mode 100644 src/functions.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87f4a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.venv +/.vscode +/data \ No newline at end of file diff --git a/.streamlit/config.toml b/.streamlit/config.toml new file mode 100644 index 0000000..88ef5fd --- /dev/null +++ b/.streamlit/config.toml @@ -0,0 +1,9 @@ +[theme] +primaryColor = "#B6244F" +backgroundColor = "#F4F4F8" +secondaryBackgroundColor = "E6E6EA" +textColor = "#020122" +font = "sans serif" + +[server] +runOnSave = true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7c6e001 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +### [1.0.0] - 2024-05-01 + +## Release \ No newline at end of file diff --git a/Fia Qonita.code-workspace b/Fia Qonita.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/Fia Qonita.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..036cbc5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Sandi Indika Saputra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c6c788a --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# KLASIFIKASI MUSIK BERDASARKAN GENRE MENGGUNAKAN METODE NAIVE BAYES + +## Instalasi dan Dependensi + +Untuk instalasi perangkat lunak, pastikan dependensi bawaan telah diinstal. Dependensi yang dibutuhkan tercantum dalam: + +```bash +requirements.txt +``` + +## Dukungan atau Kontak + +Untuk informasi lebih lanjut atau bantuan, hubungi melalui email: bimbingin.id@gmail.com or sandidikaputra@gmail.com. \ No newline at end of file diff --git a/RUN.bat b/RUN.bat new file mode 100644 index 0000000..9b085b7 --- /dev/null +++ b/RUN.bat @@ -0,0 +1,6 @@ +@echo off + +echo Running the program... +streamlit run src/app.py + +pause \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..f37d61e --- /dev/null +++ b/css/style.css @@ -0,0 +1,31 @@ +.title, h1, h2 { + text-align: center; +} + +.st-emotion-cache-1o4beor p { + text-align: center; +} + +h4, h5 { + text-align: left; +} + +.ms-20{ + margin: 20px; +} + +.ms-40{ + margin: 40px; +} + +.ms-60{ + margin: 60px; +} + +.ms-80{ + margin: 80px; +} + +.paragraph { + text-align: justify; +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3b066eb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +streamlit +librosa +numpy +pandas +matplotlib +scikit-learn \ No newline at end of file diff --git a/src/__pycache__/functions.cpython-311.pyc b/src/__pycache__/functions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55e7151abccad89b7359b73b3c5d89a4d47a36f7 GIT binary patch literal 20140 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 literal 0 HcmV?d00001 diff --git a/src/__pycache__/functions.cpython-312.pyc b/src/__pycache__/functions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d5326ffac98f5cb8eee731792f18abfe9423587 GIT binary patch literal 18051 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 + #MainMenu {visibility: hidden;} + header {visibility: hidden;} + footer {visibility: hidden;} + .st-emotion-cache-z5fcl4 {padding-top: 1rem;} + """, + unsafe_allow_html= True +) + +## CSS on style.css +with open("./css/style.css") as file: + st.markdown( + "".format(file.read()), unsafe_allow_html= True + ) + +class MyApp(): + """Class dari MyApp + + Parameters + ---------- + message : bool, default= False + Jika False, maka pesan error tidak akan ditampilkan dalam Webpage + Sistem. Jika True, maka akan menampilkan pesan dalam Webpage Sistem + yang dapat dianalisis. + + Attributes + ---------- + message : bool + Tampilkan pesan error pada Webpage Sistem atau tidak. + + pathdata : str + Path (jalur) data disimpan dalam lokal direktori. + + menu_ : list + Daftar menu yang akan ditampilkan dalam Webpage Sistem. + """ + + def __init__(self, message= False): + self.message = message + self.pathdata = "./data/music" + self.menu_ = [ + "Beranda", "Dataset", "Ekstraksi Fitur", "Permutation Importance", + "Klasifikasi", "Evaluasi" + ] + + def _exceptionMessage(self, e): + """Tampilkan pesan galat + + Parameters + ---------- + e : exception object + Obyek exception yang tersimpan. + """ + ms_20() + with ml_center(): + st.error("Terjadi masalah...") + if self.message: + st.exception(e) # tampilkan keterangan galat + + def _pageBeranda(self): + """Tab beranda + + Halaman ini akan menampilkan judul penelitian dan abstrak dari proyek. + """ + try: + ms_20() + show_text( + "Klasifikasi Musik Berdasarkan Genre Menggunakan Metode Naive\ + Bayes", + size= 2, division= True + ) + + ms_40() + with ml_center(): + show_paragraf( + "Musik telah menjadi satu kesatuan dalam kehidupan\ + mayoritas orang. Pembawaan musik pada era modern telah\ + tergantikan dari yang semula album fisik kini menjadi\ + musik digital. Label kategorikal (genre) diberikan pada\ + setiap karya musik untuk mengidentifikasi jenis genre\ + musiknya. Berdasarkan fitur yang di ekstrak menggunakan\ + teknik machine learning, musik dapat diklasifikasikan\ + secara otomatis sesuai genrenya sehingga dinilai lebih\ + efisien jika dibandingkan dengan melakukan klasifikasi\ + secara manual setiap trek dari database musik besar.\ + Penelitian ini dilakukan untuk mengklasifikasikan genre\ + musik menggunakan dataset GTZAN dengan fitur ekstraksi\ + yang digunakan adalah Mel Frequency Cepstral Coefficients\ + (MFCC) dan Naïve Bayes untuk klasifikasi. Keunggulan\ + penggunaan MFCC sebagai fitur ekstraksi adalah kemampuan\ + dalam mengenali karakteristik suara yang sangat penting\ + bagi pengenalan suara dan menghasilkan data yang sangat\ + penting bagi pengenalan musik dan menghasilkan data\ + seminimal mungkin tanpa menghilangkan informasi penting.\ + Berdasarkan hasil penelitian yang telah dilakukan,\ + rata-rata akurasi dari model Naive Bayes menunjukkan\ + performa yang baik dalam klasifikasi pada dataset dengan\ + rata-rata akurasi sekitar 92,80% dengan menggunakan 5\ + genre optimal yaitu: classical, disco, hiphop, pop, dan\ + reggae. Hal ini berbanding terbalik jika menggunakan\ + model yang dilatih dengan 10 genre, didapatkan akurasi\ + rata-rata sebesar 68,90%. Dengan meningkatnya jumlah\ + genre, kompleksitas data juga meningkat. Ini berarti\ + terdapat lebih banyak variasi dalam fitur-fitur yang\ + digunakan untuk membedakan antara genre-genre tersebut.\ + Dengan menggunakan permutation importance, didapatkan\ + model optimal dengan menggunakan 12 fitur dalam MFCC\ + dengan rata-rata akurasi sebesar 95%." + ) + except Exception as e: + self._exceptionMessage(e) + + def _pageDataset(self): + """Halaman Dataset + + Bagian ini akan menampilkan obyek DataFrame dengan detail data + penelitian. + """ + try: + ms_20() + show_text("Data Musik", division= True) + + ms_40() + with ml_center(): + df = get_list_musik(self.pathdata) + st.dataframe(df, use_container_width= True, hide_index= True) + + mk_dir("./data/dataframe") + df.to_csv("./data/dataframe/list-music.csv", index= False) + except Exception as e: + self._exceptionMessage(e) + + def _pageFeatureExtraction(self): + """Ekstraksi Fitur MFCC + + Halaman ini akan mengekstraksi fitur MFCC dari data dengan membaca + filepath setiap file musik. Number input disediakan untuk optimasi + pada durasi musik dan koefisien hitung MFCC. + """ + try: + ms_20() + show_text("Ekstraksi Fitur") + show_caption("Mel Frequency Cepstral Coefficients", division= True) + + left, right = ml_right() + with left: + ms_20() + duration = st.number_input( + "Durasi Musik (detik)", min_value= 1, value= 30, step= 1, + key= "Number input untuk nilai durasi musik" + ) + coef = st.number_input( + "Koefisien MFCC", min_value= 1, value= 13, step= 1, max_value= 20, + key= "Number input untuk nilai koefisien hitung MFCC" + ) + + ms_40() + with ml_center(): + btn_extract = st.button( + "Submit", key= "Button fit feature extraction", use_container_width= True + ) + with right: + if btn_extract: + ss.fit_extract = True + + with st.spinner("Feature extraction is running..."): + df = feature_extraction_mfcc( + get_csv("./data/dataframe/list-music.csv"), + duration= duration, coef= coef + ) + + df.to_csv("./data/dataframe/mfcc_features.csv", index= False) + if ss.fit_extract: + show_caption("Fitur MFCC", size= 2) + st.dataframe( + get_csv("./data/dataframe/mfcc_features.csv"), + use_container_width= True, hide_index= True + ) + except Exception as e: + self._exceptionMessage(e) + + def _pagePermutationImportance(self): + """Permutation Importance + + Teknik yang digunakan untuk mengukur pentingnya fitur dalam model + machine learning. Teknik ini bekerja dengan mengacak urutan nilai + fitur untuk satu contoh pada satu waktu, dan kemudian mengamati + perubahan performa model. + """ + try: + ms_20() + show_text("Permutation Importance", division= True) + + df = get_csv("./data/dataframe/mfcc_features.csv") + features = df.iloc[:, 1:-1] + feature_names = features.columns + + left, right = ml_right() + with left: + choice_genre = st.radio( + "Gunakan semua genre?", ["Yes", "No"], horizontal= True, + key= "Radio button pilihan jenis genre" + ) + + choice_genre = True if choice_genre == "Yes" else False + if choice_genre: + labels_selected = st.multiselect( + "Pilih genre musik (multi)", df.iloc[:, -1].unique(), + placeholder= "Pilih opsi", + key= "Multiselect untuk pilihan genre" + ) + + ms_40() + with ml_center(): + btn_perm = st.button( + "Submit", key= "Button fit permutation importance", use_container_width= True + ) + + with right: + features["genre"] = df.iloc[:, -1] + if choice_genre: + unwanted_genres = [genre for genre in features["genre"].unique() if genre not in labels_selected] + features = features[~features["genre"].isin(unwanted_genres)] + + temp_title = st.empty() + temp_data = st.empty() + + with temp_title: + show_caption("Final Data", size= 2) + temp_data.dataframe(features, use_container_width= True, hide_index= True) + + labels = features.iloc[:, -1].values + features = features.iloc[:, :-1].values + + if btn_perm: + temp_title.empty() + temp_data.empty() + + with st.spinner("Permutation Importance is running..."): + model, score, X_test, y_test = nbc_model( + features, labels + ) + + permutation_scores, sorted_idx = permutation_importance(model, X_test, y_test) + show_caption("Permutation Importance Scores", size= 2) + st.dataframe( + pd.DataFrame({ + "Feature": [f"mfcc_{i + 1}" for i in range(len(permutation_scores))], + "Score": permutation_scores + }), + hide_index= True, + use_container_width= True + ) + except Exception as e: + self._exceptionMessage(e) + + def _pageKlasifikasi(self): + """Klasifikasi Naive Bayes + + Halaman ini untuk setting dan training model klasifikasi. + """ + try: + ms_20() + show_text("Klasifikasi", division= True) + + df = get_csv("./data/dataframe/mfcc_features.csv") + features = df.iloc[:, 1:-1] + feature_names = features.columns + + left, right = ml_right() + with left: + kfold = st.selectbox( + "Pilih jumlah subset Fold", + [4, 5, 10], index= 1, + key= "Selectbox untuk memilih jumlah subset Fold" + ) + + choice_feature = st.radio( + "Gunakan semua fitur?", ["Yes", "No"], horizontal= True, + key= "Radio button untuk pilihan fitur" + ) + choice_feature = True if choice_feature == "Yes" else False + if choice_feature: + features_selected = st.multiselect( + "Pilih fitur musik (multi)", feature_names, + placeholder= "Pilih opsi", + key= "Multiselect untuk memilih fitur koefisien MFCC" + ) + + choice_genre = st.radio( + "Gunakan semua genre?", ["Yes", "No"], horizontal= True, + key= "Radio button untuk pilihan jenis genre" + ) + + choice_genre = True if choice_genre == "Yes" else False + if choice_genre: + labels_selected = st.multiselect( + "Pilih genre musik (multi)", df.iloc[:, -1].unique(), + placeholder= "Pilih opsi", + key= "Multiselect untuk memilih genre" + ) + + ms_40() + with ml_center(): + btn_classify = st.button( + "Submit", key= "Button fit classification", use_container_width= True + ) + + with right: + if choice_feature: + unwanted_columns = [col for col in features.columns if col not in features_selected] + features.drop(unwanted_columns, axis= 1, inplace= True) + + features["genre"] = df.iloc[:, -1] + if choice_genre: + unwanted_genres = [genre for genre in features["genre"].unique() if genre not in labels_selected] + features = features[~features["genre"].isin(unwanted_genres)] + + temp_title = st.empty() + temp_data = st.empty() + + with temp_title: + show_caption("Final Data", size= 2) + temp_data.dataframe(features, use_container_width= True, hide_index= True) + + labels = features.iloc[:, -1].values + features = features.iloc[:, :-1].values + + if btn_classify: + temp_title.empty() + temp_data.empty() + + with st.spinner("Classification is running..."): + model, score, X_test, y_test = nbc_model( + features, labels, K= kfold + ) + + ms_20() + st.success("Pelatihan model berhasil!") + st.info(f"Rata-rata score model: {score:.4f}") + except Exception as e: + self._exceptionMessage(e) + + def _pageEvaluasi(self): + """Evaluasi Model Klasifikasi + + Halaman ini untuk evaluasi model hasil klasifikasi. + """ + try: + ms_20() + show_text("Evaluasi", division= True) + + scores, precision, recall = evaluation_metrics() + + left, right = ml_split() + with left: + ms_20() + for key, val in enumerate(scores): + st.success(f"Akurasi Fold {key + 1}: {val * 100:.2f}%") + + ms_20() + for key, val in enumerate(precision): + st.info(f"Precision Fold {key + 1}: {val * 100:.2f}%") + + ms_20() + for key, val in enumerate(recall): + st.success(f"Recall Fold {key + 1}: {val * 100:.2f}%") + with right: + plot_confusion_matrix() + except Exception as e: + self._exceptionMessage(e) + + def main(self): + """Main program + + Setting session page diatur disini dan konfigurasi setiap halaman + dipanggil disini. + """ + with st.container(): + tabs = st.tabs(self.menu_) + + if "fit_extract" not in ss: + ss.fit_extract = False + + with tabs[0]: + self._pageBeranda() + with tabs[1]: + self._pageDataset() + with tabs[2]: + self._pageFeatureExtraction() + with tabs[3]: + self._pagePermutationImportance() + with tabs[4]: + self._pageKlasifikasi() + with tabs[5]: + self._pageEvaluasi() + +if __name__ == "__main__": + app = MyApp(message= True) + app.main() \ No newline at end of file diff --git a/src/functions.py b/src/functions.py new file mode 100644 index 0000000..6a8353b --- /dev/null +++ b/src/functions.py @@ -0,0 +1,462 @@ +# LIBRARY / MODULE / PUSTAKA + +import streamlit as st +import librosa +import itertools, os, pickle + +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +from sklearn.model_selection import KFold +from sklearn.naive_bayes import GaussianNB +from sklearn.metrics import confusion_matrix, accuracy_score +from sklearn.metrics import precision_score, recall_score + +from warnings import simplefilter + +simplefilter(action= "ignore", category= FutureWarning) + +# DEFAULT FUNCTIONS + +"""Make Space + +Fungsi-fungsi untuk membuat jarak pada webpage menggunakan margin space dengan +ukuran yang bervariatif. +""" + +def ms_20(): + st.markdown("
", unsafe_allow_html= True) + +def ms_40(): + st.markdown("
", unsafe_allow_html= True) + +def ms_60(): + st.markdown("
", unsafe_allow_html= True) + +def ms_80(): + st.markdown("
", unsafe_allow_html= True) + +"""Make Layout + +Fungsi-fungsi untuk layouting webpage menggunakan fungsi columns() dari +Streamlit. + +Returns +------- +self : object containers + Mengembalikan layout container. +""" + +def ml_center(): + left, center, right = st.columns([.3, 2.5, .3]) + return center + +def ml_split(): + left, center, right = st.columns([1, .1, 1]) + return left, right + +def ml_left(): + left, center, right = st.columns([2, .1, 1]) + return left, right + +def ml_right(): + left, center, right = st.columns([1, .1, 2]) + return left, right + +"""Cetak text + +Fungsi-fungsi untuk menampilkan teks dengan berbagai gaya menggunakan method +dari Streamlit seperti title(), write(), dan caption(). + +Parameters +---------- +text : str + Teks yang ingin ditampilkan dalam halaman. + +size : int + Ukuran Heading untuk teks yang akan ditampilkan. + +division : bool + Kondisi yang menyatakan penambahan garis divisi teks ditampilkan. +""" + +def show_title(text, division= False): + st.title(text) + if division: + st.markdown("---") + +def show_text(text, size= 3, division= False): + heading = "#" if size == 1 else ( + "##" if size == 2 else ( + "###" if size == 3 else ( + "####" if size == 4 else "#####" + ) + ) + ) + + st.write(f"{heading} {text}") + if division: + st.markdown("---") + +def show_caption(text, size= 3, division= False): + heading = "#" if size == 1 else ( + "##" if size == 2 else ( + "###" if size == 3 else ( + "####" if size == 4 else "#####" + ) + ) + ) + + st.caption(f"{heading} {text}") + if division: + st.markdown("---") + +def show_paragraf(text): + st.markdown(f"
{text}
", unsafe_allow_html= True) + +"""Load file + +Fungsi-fungsi untuk membaca file dalam lokal direktori. + +Parameters +---------- +filepath : str + Jalur tempat data tersedia di lokal direktori. + +Returns +------- +self : object DataFrame or str + Obyek dengan informasi yang berhasil diekstrak. +""" + +def get_csv(filepath): + return pd.read_csv(filepath) + +def get_excel(filepath): + return pd.read_excel(filepath) + +# ---------- + +def mk_dir(dirpath): + """Buat folder + + Fungsi ini akan memeriksa path folder yang diberikan. Jika tidak ada + folder sesuai path yang dimaksud, maka folder akan dibuat. + + Parameters + ---------- + dirpath : str + Jalur tempat folder akan dibuat. + """ + if not os.path.exists(dirpath): + os.makedirs(dirpath) + +# CUSTOM FUNCTIONS + +def get_list_musik(directory): + """Baca file musik + + File musik yang telah dibagi per folder (genre) akan dibaca disini. + Penjelajahan file dilakukan dengan module os yang nantinya akan didapatkan + 3 elemen dari data musik: filepath, filename, dan label genre musik. + + Parameters + ---------- + directory : str + Jalur utama tempat file musik akan diakses. + + Returns + ------- + df : object DataFrame or TextFileReader + File csv (comma-separated values) dikembalikan sebagai struktur data + dua dimensi dengan sumbu yang diberi label. + """ + temp_filepath, temp_genre, temp_filename = [], [], [] + for _dir in os.listdir(directory): # main directory + folderpath = os.path.join(directory, _dir) + if os.path.isdir(folderpath): + for filename in os.listdir(folderpath): # genre directory + filepath = os.path.join(folderpath, filename) + + temp_filepath.append(filepath) + temp_filename.append(filename) + temp_genre.append(_dir) + else: + temp_filepath.append(folderpath) + temp_filename.append(_dir) + temp_genre.append(directory) + + df = pd.DataFrame({ + "filepath": temp_filepath, + "filename": temp_filename, + "genre": temp_genre + }) + return df + +@st.cache_data(ttl= 3600, show_spinner= "MFCC Features Extraction...") +def feature_extraction_mfcc(df, duration= 30, coef= 13): + """Ekstraksi Fitur MFCC + + Fitur audio MFCC didasarkan pada persepsi pendengaran manusia. Ekstraksi + MFCC dilakukan dengan menggunakan module Librosa yang menyediakan + pemrosesan audio. + + Parameters + ---------- + df : object DataFrame + Object DataFrame tempat semua file musik (path file) tersimpan. + + duration : int or float + Durasi musik yang ingin di ekstraksi fiturnya. + + coef : int + Jumlah koefisien dari MFCC yang akan dihitung. + + Returns + ------- + res : object DataFrame + DataFrame dari data musik dengan fitur yang telah di ekstraksi dan + label genre musik. + """ + mfcc_features = [] + for _dir in df.iloc[:, 0]: + y, sr = librosa.load(_dir, duration= duration) + mfcc = librosa.feature.mfcc(y= y, sr= sr, n_mfcc= coef) + + feature = np.mean(mfcc, axis= 1) + mfcc_features.append(feature) + + res = pd.DataFrame({ + "filename": df.iloc[:, 1], + **{f"mfcc_{i + 1}": [x[i] for x in mfcc_features] for i in range(coef)}, + "genre": df.iloc[:, -1] + }) + return res + +def min_max_scaler(feature_names, df): + """Transformasikan fitur dengan menskalakan setiap fitur ke rentang + tertentu + + Estimator ini menskalakan dan menerjemahkan setiap fitur satu per satu + sehingga berada dalam rentang tertentu pada set pelatihan, misalnya + antara nol dan satu. + + Transformasi dilakukan dengan:: + + X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) + X_scaled = X_std * (max - min) + min + + dimana min, max = feature_range. + + Transformasi ini sering digunakan sebagai alternatif terhadap mean nol, + penskalaan unit varians. + + Parameters + ---------- + feature_names : ndarray of shape + Nama fitur dari kumpulan data (DataFrame). Didefinisikan hanya ketika + `X` memiliki nama fitur yang semuanya berupa string. + + df : object DataFrame + Object DataFrame yang menyimpan fitur musik beserta label genrenya. + + Returns + ------- + self : object DataFrame + DataFrame dari data musik dengan fitur yang telah di skalakan. + """ + for col in feature_names: # loop untuk setiap fitur dalam `X` + min_ = df[col].min() + max_ = df[col].max() + df[col] = (df[col] - min_) / (max_ - min_) + return df + +def permutation_importance(model, X_test, y_test, metric= accuracy_score): + """Calculate Permutation Importance + + Teknik yang digunakan untuk mengukur pentingnya fitur dalam model machine + learning. Teknik ini bekerja dengan mengacak urutan nilai fitur untuk satu + contoh pada satu waktu, dan kemudian mengamati perubahan performa model. + + Parameters + ---------- + model : trained Naive Bayes model + Trained Naive Bayes model ready for inference. + + X_test : ndarray or shape (n_samples, n_features) + Sampel OOB (Out-of-Bag) dari data test yang dihasilkan KFold. + + y_test : ndarray or shape (n_samples, 1, n_outputs) + Label sampel OOB dari data test yang dihasilkan KFold. + + metric : metric function, default= accuracy_score + Perhitungan metric yang digunakan untuk mengevaluasi hasil permutation + importance. + + Returns + ------- + self : ndarray + Nilai score dari setiap fitur yang dihitung menggunakan permutation + importance. + """ + baseline = metric(y_test, model.predict(X_test)) + scores = [] + + for feature in range(X_test.shape[1]): + permuted_X = X_test.copy() + permuted_X[:, feature] = np.random.permutation(permuted_X[:, feature]) + permuted_score = metric(y_test, model.predict(permuted_X)) + scores.append(baseline - permuted_score) + scores = np.array(scores) + return scores, np.argsort(scores[::-1]) + +@st.cache_data(ttl= 3600, show_spinner= "Naive Bayes Classification...") +def nbc_model(features, labels, K= 5): + """Naive Bayes Clasifier Model + + Pelatihan model menggunakan Naive Bayes dengan beberapa persiapan seperti + setting parameter dan validasi KFold. + + Parameters + ---------- + features : ndarray or shape (n_samples, n_features) + Sampel OOB (Out-of-Bag). + + labels : ndarray or shape (n_samples, 1, n_outputs) + Label sampel OOB. + + K : int + Jumlah subset Fold. + + Returns + ------- + dump_model : trained Naive Bayes model + Trained Naive Bayes model ready for inference. + + avg_score : float + Rata-rata dari score model selama pelatihan dengan KFold. + + dump_features_test : ndarray or shape (n_samples, n_features) + Sampel OOB (Out-of-Bag) dari data test yang dihasilkan KFold. + + dump_labels_test : ndarray or shape (n_samples, 1, n_outputs) + Label sampel OOB dari data test yang dihasilkan KFold. + """ + kfold = KFold(n_splits= K, shuffle= True, random_state= 42) + + temp_score, avg_score = 0, 0 + list_labels_test, list_labels_predict = [], [] + + for tr_index, ts_index in kfold.split(features): + X_train, X_test = features[tr_index], features[ts_index] + y_train, y_test = labels[tr_index], labels[ts_index] + + model = GaussianNB() + model.fit(X_train, y_train) + + y_pred = model.predict(X_test) + score = accuracy_score(y_test, y_pred) + avg_score += score + + if temp_score < score: + temp_score = score + + dump_model = model + dump_features_test = X_test + dump_labels_test = y_test + + list_labels_test.append(y_test) + list_labels_predict.append(y_pred) + + mk_dir("./data/pickle") + with open("./data/pickle/labels_test.pickle", "wb") as file: + pickle.dump(list_labels_test, file) + with open("./data/pickle/labels_predict.pickle", "wb") as file: + pickle.dump(list_labels_predict, file) + return dump_model, avg_score / K, dump_features_test, dump_labels_test + +def evaluation_metrics(): + """Perhitungan evaluasi + + Returns + ------- + score_list : list + Daftar akurasi dari keseluruhan model dalam fold. + + precision_list : list + Daftar precision dari keseluruhan model dalam fold. + + recall_list : list + Daftar recall dari keseluruhan model dalam fold. + """ + with open("./data/pickle/labels_predict.pickle", "rb") as file: + list_labels_predict = pickle.load(file) + with open("./data/pickle/labels_test.pickle", "rb") as file: + list_labels_test = pickle.load(file) + + score_list, precision_list, recall_list = [], [], [] + for y_test, y_pred in zip(list_labels_test, list_labels_predict): + score = accuracy_score(y_test, y_pred) + precision = precision_score(y_test, y_pred, average= "macro") + recall = recall_score(y_test, y_pred, average= "macro") + + score_list.append(score) + precision_list.append(precision) + recall_list.append(recall) + + return score_list, precision_list, recall_list + +def plot_confusion_matrix(normalize= False, title= "Confusion Matrix" + ): + """Plot Confusion Matrix + + Fungsi untuk menggambar confusion matrix + + Parameters + ---------- + normalize : bool + Jika True, maka confusion matrix akan dinormalisasi, dan False + sebaliknya. + + title : str + Menampilkan judul dari Plot. + """ + + with open("./data/pickle/labels_predict.pickle", "rb") as file: + list_labels_predict = pickle.load(file) + with open("./data/pickle/labels_test.pickle", "rb") as file: + list_labels_test = pickle.load(file) + + for key, (y_test, y_pred) in enumerate(zip(list_labels_test, list_labels_predict)): + show_caption(f"Fold-{key + 1}") + cm = confusion_matrix(y_test, y_pred) + + classes = np.unique(y_test) + + fig = plt.figure(figsize= (10, 10)) + plt.imshow(cm, interpolation= "nearest", cmap= plt.cm.Blues) + plt.title(title) + plt.colorbar() + + tick_marks = np.arange(len(classes)) + plt.xticks(tick_marks, classes, rotation= 45) + plt.yticks(tick_marks, classes) + + if normalize: + cm = cm.astype("float") / cm.sum(axis= 1)[:, np.newaxis] + st.write("Normalized Confusion Matrix") + else: + st.write("Confusion Matrix, without Normalization") + + thresh = cm.max() / 2 + for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): + plt.text( + j, i, cm[i, j], horizontalalignment= "center", fontsize= 12, + color= 'white' if cm[i, j] > thresh else 'black' + ) + + plt.tight_layout() + plt.ylabel("True Label") + plt.xlabel("Predicted Label") + st.pyplot(fig) \ No newline at end of file