Dalam pekerjaan sehari-hari kita di bidang data science, seringkali kita menemui fitur kategorikal. Beberapa orang mungkin bingung tentang cara menangani fitur ini, terutama ketika kita ingin membuat model prediksi di mana model tersebut pada dasarnya adalah persamaan yang menerima angka; bukan kategori.
Salah satu caranya adalah mengenkoding semua variabel kategori menggunakan metode OneHotEncoding (mengenkoding semua kelas kategorikal menjadi nilai numerik 0 dan 1, di mana 0 berarti tidak ada dan 1 ada).
Metode ini lebih disukai oleh banyak orang karena informasinya masih ada dan konsepnya mudah dipahami. Kelemahannya adalah ketika kita memiliki banyak fitur kategorikal dengan kardinalitas tinggi, jumlah fitur setelah proses OneHotEncoding akan sangat besar. Mengapa kita tidak ingin banyak fitur dalam dataset pelatihan kita? Ini karena kutukan dimensionalitas.
Sementara menambahkan fitur dapat menurunkan kesalahan dalam model prediksi kita, hal tersebut hanya akan berkurang sampai jumlah fitur tertentu; setelah itu, kesalahan akan meningkat lagi. Ini adalah konsep dari kutukan dimensionalitas.
Banyak cara untuk mengurangi masalah ini, tetapi salah satu teknik andalan saya adalah dengan melakukan seleksi fitur melalui Tes Chi-Kuadrat untuk kemandirian.
Tes Chi-Kuadrat untuk Kemandirian
Tes Chi-Kuadrat untuk kemandirian digunakan untuk menentukan apakah ada hubungan yang signifikan antara dua variabel kategorikal (nominal). Ini berarti Tes Chi-Kuadrat untuk Kemandirian adalah tes pengujian hipotesis dengan 2 hipotesis yang ada; Hipotesis Nol dan Hipotesis Alternatif. Hipotesis dituliskan di bawah ini.
Hipotesis Nol (H0): Tidak ada hubungan antara variabel
Hipotesis Alternatif (H1): Ada hubungan antara variabel
Seperti setiap pengujian statistik, kita mengujinya terhadap p-value yang kita pilih (seringkali itu adalah 0.05). Jika p-value signifikan, kita dapat menolak hipotesis nol dan menyatakan bahwa temuan mendukung hipotesis alternatif.
Saya tidak akan banyak membahas teori statistik, karena tujuan dari artikel ini adalah untuk menunjukkan bagaimana seleksi fitur menggunakan Tes Chi-Kuadrat bekerja untuk penggunaan praktis.
Sebagai contoh, saya akan bekerja dengan dataset pinjaman dari Kaggle untuk masalah klasifikasi. Di sini, dataset termasuk berbagai variabel numerik, ordinal, dan nominal seperti yang dinyatakan di bawah ini (untuk tujuan artikel, saya akan membuang semua nilai Null yang sebenarnya membutuhkan analisis lain).
import pandas as pd
loan = pd.read_csv('loan_data_set.csv')
loan.drop('Loan_ID')
loan['Loan_Amount_Term'] = loan['Loan_Amount_Term'].astype('object')
loan['Credit_History'] = loan['Credit_History'].astype('object')
loan.dropna(inplace = True)
categorical_columns = loan.select_dtypes(exclude = 'number').drop('Loan_Status', axis = 1).columns
loan.info()
Dalam tes Chi-Kuadrat, kita menampilkan data dalam format tabulasi silang (kontingensi) dengan setiap baris mewakili satu tingkat (grup) untuk satu variabel dan setiap kolom mewakili satu tingkat (grup) untuk variabel lain. Mari kita coba membuat tabel tabulasi silang antara kolom Gender dan Loan_Status.
pd.crosstab(loan['Gender'], loan['Loan_Status'])
Sekarang, mari kita coba menggunakan tes Chi-Kuadrat untuk kemandirian untuk menguji hubungan antara kedua fitur ini. Untungnya, perpustakaan python scipy sudah mengandung fungsi tes untuk kita gunakan.
from scipy.stats import chi2_contingency
chi_res = chi2_contingency(pd.crosstab(loan['Loan_Status'], loan['Gender']))
print('Chi2 Statistic: {}, p-value: {}'.format(chi_res[0], chi_res[1]))
Jika kita memilih level p-value kita menjadi 0.05, karena hasil tes p-value lebih dari 0.05, kita gagal untuk menolak Hipotesis Nol. Ini berarti, tidak ada hubungan antara fitur Gender dan Loan_Status berdasarkan tes Chi-Kuadrat untuk kemandirian.
Kita bisa mencoba menggunakan tes ini dengan semua fitur kategorikal yang ada.
chi2_check = []
for i in categorical_columns:
if chi2_contingency(pd.crosstab(loan['Loan_Status'], loan[i]))[1] < 0.05:
chi2_check.append('Reject Null Hypothesis')
else:
chi2_check.append('Fail to Reject Null Hypothesis')
res = pd.DataFrame(data = [categorical_columns, chi2_check]
).T
res.columns = ['Column', 'Hypothesis']
print(res)
Pengujian Post Hoc Tes
Chi-kuadrat untuk kemandirian adalah tes omnibus yang berarti ia menguji data secara keseluruhan. Jika kita memiliki banyak kelas dalam satu kategori, kita tidak akan dapat dengan mudah menentukan kelas mana dari fitur yang bertanggung jawab atas hubungan tersebut jika tabel Chi-kuadrat lebih besar dari 2×2. Untuk menentukan kelas mana yang bertanggung jawab, kita memerlukan tes post hoc.
Untuk melakukan banyak tes Chi-kuadrat 2×2 untuk kemandirian, kita perlu mengelompokkan ulang fitur untuk setiap tes ke mana itu adalah satu kelas kategori melawan sisanya. Untuk melakukan ini, kita bisa menerapkan OneHotEncoding ke setiap kelas dan membuat tabel silang baru terhadap fitur lain.
Sebagai contoh, mari kita coba melakukan tes post hoc pada fitur Property_Area. Pertama, kita perlu melakukan OneHotEncoding pada fitur Property_Area.
property_dummies = pd.get_dummies(data = loan[['Property_Area', 'Loan_Status']], columns = ['Property_Area'])
Selanjutnya, kita membuat tabel tabulasi silang untuk setiap kelas Property_Area terhadap target Loan_Status.
pd.crosstab(property_dummies['Loan_Status'], property_dummies['Property_Area_Rural'])
Kemudian, kita bisa melakukan tes Chi-Kuadrat untuk kemandirian pada pasangan ini.
Namun, ada sesuatu yang perlu diingat. Membandingkan beberapa kelas satu sama lain berarti tingkat kesalahan positif palsu meningkat dengan setiap tes. Sebagai contoh, jika kita memilih tes pertama kita pada level p-value 0.05 berarti ada 5% kesempatan positif palsu; jika kita memiliki beberapa kelas, tes setelah itu akan menggabungkan kesalahan dengan kesempatan menjadi 10% positif palsu, dan seterusnya. Dengan setiap tes berikutnya, tingkat kesalahan akan meningkat sebesar 5%. Dalam kasus di atas, kita memiliki 3 perbandingan berpasangan. Ini berarti tes Chi-kuadrat kita akan memiliki tingkat kesalahan 15%. Berarti p-value yang diuji akan sama dengan 0.15, yang cukup tinggi.
Dalam kasus ini, kita bisa menggunakan metode koreksi Bonferroni-adjusted untuk mengoreksi p-value yang kita gunakan. Kami menyesuaikan P-value kami dengan jumlah perbandingan berpasangan yang ingin kami lakukan. Rumusnya adalah p/N, di mana p= p-value dari tes asli dan N= jumlah perbandingan berpasangan yang direncanakan. Sebagai contoh, dalam kasus kita di atas, kita memiliki 3 kelas dalam fitur Property_Area; yang berarti kita akan memiliki 3 perbandingan berpasangan jika kita menguji semua kelas terhadap fitur Loan_Status. P-value kita akan menjadi 0.05/3 = 0.0167.
Menggunakan P-value yang disesuaikan, kita bisa menguji semua hasil signifikan sebelumnya untuk melihat kelas mana yang bertanggung jawab atas pembuatan hubungan yang signifikan.
check = {}
for i in res[res['Hypothesis'] == 'Reject Null Hypothesis']['Column']:
dummies = pd.get_dummies(loan[i])
bon_p_value = 0.05/loan[i].nunique()
for series in dummies:
if chi2_contingency(pd.crosstab(loan['Loan_Status'], dummies[series]))[1] < bon_p_value:
check['{}-{}'.format(i, series)] = 'Reject Null Hypothesis'
else:
check['{}-{}'.format(i, series)] = 'Fail to Reject Null Hypothesis'
res_chi_ph = pd.DataFrame(data = [check.keys(), check.values()]).T
res_chi_ph.columns = ['Pair', 'Hypothesis']
res_chi_ph
Di sini saya juga menyertakan fitur biner untuk perbandingan berpasangan. Seperti yang bisa kita lihat, banyak kelas sebenarnya tidak signifikan. Bahkan Loan_Amount_Term yang sebelumnya signifikan sebelum tes post hoc menghasilkan semua kelas tidak signifikan.
Model Prediksi
Tujuan dari teknik seleksi fitur ini adalah untuk melihat bagaimana dampaknya terhadap model prediksi kita. Mari gunakan model paling sederhana; Regresi Logistik sebagai patokan. Pertama, saya akan menggunakan semua data dan melihat kinerja model pada fase awal. Di sini, saya memperlakukan semua data kategorikal sebagai nominal (bahkan data ordinal).
data_log = pd.get_dummies(data = loan, columns = loan.select_dtypes(exclude = 'number').drop('Loan_Status', axis =1).columns, drop_first =True)
data_log['Loan_Status'] = data_log['Loan_Status'].apply(lambda x: 0 if x == 'N' else 1)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(data_log.drop('Loan_Status', axis =1), data_log['Loan_Status'], test_size = 0.30, random_state = 101)
from sklearn.linear_model import LogisticRegression
log_model = LogisticRegression(max_iter = 1000)
log_model.fit(X_train, y_train)
from sklearn.metrics import classification_report, confusion_matrix, roc_curve,auc, accuracy_score
predictions = log_model.predict(X_test)
print(accuracy_score(y_test, predictions))
Out: 0.7708333333333334
print(classification_report(y_test,predictions))
#ROC-AUC plot
preds = log_model.predict_proba(X_test)[:,1]
fpr, tpr, threshold = roc_curve(y_test, preds)
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(10,8))
plt.title('Receiver Operator Characteristic')
plt.plot(fpr, tpr, 'b', label = 'AUC = {}'.format(round(roc_auc, 2)))
plt.legend(loc = 'lower right')
plt.plot([0,1], [0,1], 'r--')
plt.xlim([0,1])
plt.ylim([0,1])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()
Di atas adalah kinerja model jika kita menggunakan semua data, mari kita bandingkan dengan data yang kita pilih melalui Tes Chi-Kuadrat untuk Kemandirian.
significant_chi = []
for i in res_chi[res_chi['Hypothesis'] == 'Reject Null Hypothesis']['Pair']:
significant_chi.append('{}_{}'.format(i.split('-')[0],i.split('-')[1]))
#Drop the data with duplicate information
for i in ['Married_No', 'Credit_History_0.0']:
significant_chi.remove(i)
for i in loan.select_dtypes('number').columns:
significant_chi.append(i)
print(significant_chi)
Out: ['Married_Yes', 'Credit_History_1.0','Property_Area_Semiurban',
'ApplicantIncome','CoapplicantIncome', 'LoanAmount']
Sebelumnya, jika kita menggunakan semua data seperti adanya, kita akan berakhir dengan 21 variabel independen. Dengan seleksi fitur, kita hanya memiliki 6 fitur untuk dikerjakan. Saya tidak akan melakukan pemisahan data latih dan data uji sekali lagi karena saya ingin menguji data dengan data latihan dan data uji yang sama. Mari kita lihat bagaimana kinerja model kita dengan fitur-fitur terpilih ini.
log_model = LogisticRegression(max_iter = 1000)
log_model.fit(X_train[significant_chi], y_train)
#Metrics check
predictions = log_model.predict(X_test[significant_chi])
print(accuracy_score(y_test, predictions))
Out: 0.7847222222222222
print(classification_report(y_test,predictions))
Kesimpulan
Dari segi metrik, model dengan fitur terpilih berkinerja sedikit lebih baik daripada yang dilatih dengan semua fitur. Secara teoritis, ini bisa terjadi karena kita mengeliminasi semua kebisingan dalam data dan hanya berakhir dengan pola yang paling penting. Meskipun demikian, kita belum menganalisis data numerik yang juga bisa penting. Secara keseluruhan, saya telah menunjukkan bahwa dengan tes Chi-Kuadrat untuk kemandirian, kita bisa berakhir hanya dengan fitur kategorikal yang paling penting.
Comments