Istilah scorecard atau ‘predictive scorecard’ mengacu pada sebuah model kredit kuantitatif yang digunakan untuk menilai kelayakan dan kemampuan bayar peminjam berdasarkan credit score yang dihasilkan.
Kita akan mencoba membuat suatu scorecard berdasarkan analisa dari suatu dataset behaviour customer di suatu bank di Taiwan.
Berikut beberapa library yang digunakan selama pemodelan klasifikasi dan pembentukan scorecard
Kita akan mencoba untuk membaca data dengan format excel. Di R, kita
bisa gunakan syntax berikut:
read_xlsx("lokasi working directory")
dari
library(readxl)
dari data
Dataset memiliki 25 kolom dan 30,952 dengan penjelasan sebagai berikut:
id
= id debiturlimit_bal
= Besaran kredit limit yang diberikan dalam
dolar NTsex
= jenis kelamin
education
= Pendidikan terakhir
marriage
= Status pernikahan
age
= Usia dalam tahunpay_*
= Status pembayaran dalam bulan April (1) -
September (6).
bill_amt*
= Jumlah tagihan pada bulan April (1) -
September (6) dalam dolar NTpay_amt*
= Jumlah pembayaran/pengeluaran sebelumnya
pada bulan April (1) - September(6) dalam dolar NTgb_flag
= Flagging pembayaran default (gagal bayar)
pada bulan berikutnya
data_clean <- data %>%
select(-id) %>%
mutate_at(.vars = c("sex", "education", "marriage", "gb_flag"), as.factor)
glimpse(data_clean)
#> Rows: 30,952
#> Columns: 24
#> $ limit_bal <dbl> 20000, 100000, 80000, 280000, 130000, 340000, 260000, 20000,…
#> $ sex <fct> 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, …
#> $ education <fct> 3, 3, 1, 2, 2, 2, 3, 3, 2, 3, 1, 2, 4, 2, 2, 2, 1, 1, 3, 3, …
#> $ marriage <fct> 2, 2, 2, 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, …
#> $ age <dbl> 39, 49, 26, 31, 53, 47, 49, 54, 35, 27, 31, 28, 38, 36, 40, …
#> $ pay_1 <dbl> 0, 0, 2, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 2, 2, 0, 0, 1, 0, 0, …
#> $ pay_2 <dbl> 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, …
#> $ pay_3 <dbl> 2, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, …
#> $ pay_4 <dbl> 2, 0, 2, 2, 0, 0, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, …
#> $ pay_5 <dbl> 3, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ pay_6 <dbl> 2, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ bill_amt1 <dbl> 12241, 1440, 37097, 127609, 109994, 196112, 8654, 16120, 105…
#> $ bill_amt2 <dbl> 16020, 0, 38174, 76057, 125138, 183453, 15260, 17160, 11312,…
#> $ bill_amt3 <dbl> 16457, 0, 40550, 75377, 114344, 167592, 6790, 18083, 13050, …
#> $ bill_amt4 <dbl> 20906.0, 0.0, 41577.0, 68277.0, 96995.0, 162628.0, 10070.0, …
#> $ bill_amt5 <dbl> 20289.0, 0.0, 41595.0, 72042.0, 100086.0, 142779.0, 585.0, 1…
#> $ bill_amt6 <dbl> 20407, 0, 43264, 65921, 92344, 139548, 435, 15720, 13179, 48…
#> $ pay_amt1 <dbl> 4000, 0, 2000, 6000, 17100, 6446, 15328, 1308, 1200, 0, 586,…
#> $ pay_amt2 <dbl> 1000, 0, 3000, 6000, 17, 6207, 6914, 1231, 1900, 0, 3660, 60…
#> $ pay_amt3 <dbl> 4750, 0, 2000, 0, 4000, 5758, 10098, 1244, 0, 0, 5754, 5000,…
#> $ pay_amt4 <dbl> 0, 0, 1000, 4800, 5000, 4853, 585, 544, 1600, 1790, 5666, 50…
#> $ pay_amt5 <dbl> 600, 0, 2500, 0, 4000, 4940, 435, 565, 0, 1890, 5666, 4000, …
#> $ pay_amt6 <dbl> 0, 0, 1000, 2226, 4000, 4600, 427, 1500, 500, 1740, 6246, 50…
#> $ gb_flag <fct> 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, …
Cross Validation adalah metode yang kita gunakan untuk mengetahui seberapa baik performa model kita memprediksi terhadap data baru.
Lantas, bagaimana cara mengetahui apakah model yang kita buat telah
baik dalam memprediksi data baru? Di sinilah mengapa kita melakukan
Train-test splitting. Kita membagi data kita menjadi 2 kelompok, yaitu
data train
dan test
.
Fungsi initial_split()
dari paket rsample
memiliki beberapa parameter:
data
= data frame awalprop
= proporsi dari data yang akan digunakan sebagai
data train (80:20 atau 70:30)Selanjutnya, fungsi training()
and
testing()
dilakukan untuk mengekstrak data train dan test
dari hasil split.
# dipakai untuk melakukan CV satu paket dengan set seed
RNGkind(sample.kind= "Rounding")
set.seed(123)
# membuat binary split data menjadi set data training dan testing
splitter <- initial_split(data_clean, prop = 0.8) # 0.8 di data train, 0.2 di data test
# splitting
train <- training(splitter)
test <- testing(splitter)
test
Initial Characteristic Analysis merupakan langkah awal dalam mengidentifikasi dan menganalisis karakteristik atau variabel yang potensial untuk dimasukkan ke dalam model behaviour scoring. Tujuan utama dari langkah ini adalah untuk memahami hubungan antara variabel-variabel yang tersedia dengan perilaku pembayaran kredit yang akan diprediksi oleh skor kredit.
Pembentukan WOE bertujuan melakukan binning / kategorisasi seluruh variabel prediktor yang ada pada data, kemudian menghitung kekuatan masing-masing hasil kategori dalam memisahkan kelas positif dan kelas negatif.
📐 Formula WOE:
\[ ln(\frac{Distr_{Good}}{Distr_{Bad}}) \]
Keterangan:
Note: Nilai WoE negatif menunjukkan bahwa banyak sebaran kelas negatif pada kelompok tersebut -> kekuatannya negatif. Begitupula sebaliknya.
📌 Cara membuat binning dan menghitung WOE:
Gunakan fungsi woebin()
, dengan parameter:
dt
: data frame trainy
: nama variabel y pada datapositive
: kelas positive (debitur good)#> ✔ Binning on 24761 rows and 24 columns in 00:00:07
#> $limit_bal
#> variable bin count count_distr neg pos posprob woe
#> 1: limit_bal [-Inf,50000) 4862 0.19635717 1296 3566 0.7334430 1.1717014
#> 2: limit_bal [50000,150000) 9973 0.40277049 4550 5423 0.5437682 0.3350614
#> 3: limit_bal [150000,360000) 7744 0.31274989 5663 2081 0.2687242 -0.8415657
#> 4: limit_bal [360000, Inf) 2182 0.08812245 1857 325 0.1489459 -1.5833529
#> bin_iv total_iv breaks is_special_values
#> 1: 0.25306622 0.6761588 50000 FALSE
#> 2: 0.04539898 0.6761588 150000 FALSE
#> 3: 0.20287037 0.6761588 360000 FALSE
#> 4: 0.17482326 0.6761588 Inf FALSE
#>
#> $sex
#> variable bin count count_distr neg pos posprob woe bin_iv
#> 1: sex 1 10226 0.4129882 5118 5108 0.4995111 0.1575837 0.010298870
#> 2: sex 2 14535 0.5870118 8248 6287 0.4325421 -0.1119472 0.007316302
#> total_iv breaks is_special_values
#> 1: 0.01761517 1 FALSE
#> 2: 0.01761517 2 FALSE
#>
#> $education
#> variable bin count count_distr neg pos posprob woe bin_iv
#> 1: education 1 7962 0.3215541 5087 2875 0.3610902 -0.4110961 0.052739067
#> 2: education 2 12073 0.4875813 5979 6094 0.5047627 0.1785909 0.015620792
#> 3: education 3%,%4 4726 0.1908647 2300 2426 0.5133305 0.2128742 0.008689948
#> total_iv breaks is_special_values
#> 1: 0.07704981 1 FALSE
#> 2: 0.07704981 2 FALSE
#> 3: 0.07704981 3%,%4 FALSE
#>
#> $marriage
#> variable bin count count_distr neg pos posprob woe
#> 1: marriage 0%,%1 11369 0.4591495 6086 5283 0.4646847 0.01804257
#> 2: marriage 2%,%3 13392 0.5408505 7280 6112 0.4563919 -0.01533730
#> bin_iv total_iv breaks is_special_values
#> 1: 0.0001495723 0.000276718 0%,%1 FALSE
#> 2: 0.0001271457 0.000276718 2%,%3 FALSE
#>
#> $age
#> variable bin count count_distr neg pos posprob woe
#> 1: age [-Inf,26) 3662 0.14789387 1550 2112 0.5767340 0.46891994
#> 2: age [26,28) 2274 0.09183797 1212 1062 0.4670185 0.02742154
#> 3: age [28,46) 14924 0.60272202 8607 6317 0.4232779 -0.14979191
#> 4: age [46, Inf) 3901 0.15754614 1997 1904 0.4880800 0.11185039
#> bin_iv total_iv breaks is_special_values
#> 1: 0.03253299297 0.04799841 26 FALSE
#> 2: 0.00006912784 0.04799841 28 FALSE
#> 3: 0.01341856563 0.04799841 46 FALSE
#> 4: 0.00197772528 0.04799841 Inf FALSE
#>
#> $pay_1
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_1 [-Inf,1) 15681 0.6332943 11152 4529 0.2888209 -0.7415782
#> 2: pay_1 [1,2) 5120 0.2067768 1286 3834 0.7488281 1.2519115
#> 3: pay_1 [2, Inf) 3960 0.1599289 928 3032 0.7656566 1.3434855
#> bin_iv total_iv breaks is_special_values
#> 1: 0.3239961 0.8889654 1 FALSE
#> 2: 0.3007706 0.8889654 2 FALSE
#> 3: 0.2641987 0.8889654 Inf FALSE
#>
#> $pay_2
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_2 [-Inf,2) 17485 0.7061508 11743 5742 0.3283958 -0.5559102
#> 2: pay_2 [2, Inf) 7276 0.2938492 1623 5653 0.7769379 1.4074496
#> bin_iv total_iv breaks is_special_values
#> 1: 0.2082814 0.7356067 2 FALSE
#> 2: 0.5273253 0.7356067 Inf FALSE
#>
#> $pay_3
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_3 [-Inf,2) 18311 0.7395097 12086 6225 0.3399596 -0.5039348
#> 2: pay_3 [2, Inf) 6450 0.2604903 1280 5170 0.8015504 1.5555521
#> bin_iv total_iv breaks is_special_values
#> 1: 0.1803796 0.7371777 2 FALSE
#> 2: 0.5567980 0.7371777 Inf FALSE
#>
#> $pay_4
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_4 [-Inf,1) 19538 0.7890634 12699 6839 0.3500358 -0.4593422
#> 2: pay_4 [1, Inf) 5223 0.2109366 667 4556 0.8722956 2.0809498
#> bin_iv total_iv breaks is_special_values
#> 1: 0.1607338 0.8889034 1 FALSE
#> 2: 0.7281696 0.8889034 Inf FALSE
#>
#> $pay_5
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_5 [-Inf,2) 20251 0.8178587 12889 7362 0.3635376 -0.4005031
#> 2: pay_5 [2, Inf) 4510 0.1821413 477 4033 0.8942350 2.2942888
#> bin_iv total_iv breaks is_special_values
#> 1: 0.1274559 0.8575895 2 FALSE
#> 2: 0.7301335 0.8575895 Inf FALSE
#>
#> $pay_6
#> variable bin count count_distr neg pos posprob woe bin_iv
#> 1: pay_6 [-Inf,2) 20166 0.8144259 12721 7445 0.3691858 -0.376172 0.1122446
#> 2: pay_6 [2, Inf) 4595 0.1855741 645 3950 0.8596300 1.971760 0.5883466
#> total_iv breaks is_special_values
#> 1: 0.7005912 2 FALSE
#> 2: 0.7005912 Inf FALSE
#>
#> $bill_amt1
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt1 [-Inf,5000) 5553 0.22426396 4145 1408 0.2535566 -0.9201930
#> 2: bill_amt1 [5000,10000) 2161 0.08727434 1279 882 0.4081444 -0.2121022
#> 3: bill_amt1 [10000, Inf) 17047 0.68846169 7942 9105 0.5341116 0.2961981
#> bin_iv total_iv breaks is_special_values
#> 1: 0.171664081 0.2362164 5000 FALSE
#> 2: 0.003878966 0.2362164 10000 FALSE
#> 3: 0.060673346 0.2362164 Inf FALSE
#>
#> $bill_amt2
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt2 [-Inf,5000) 5719 0.2309681 4335 1384 0.2420003 -0.9822042
#> 2: bill_amt2 [5000,15000) 3616 0.1460361 2072 1544 0.4269912 -0.1345984
#> 3: bill_amt2 [15000,115000) 12428 0.5019183 5792 6636 0.5339556 0.2955712
#> 4: bill_amt2 [115000, Inf) 2998 0.1210775 1167 1831 0.6107405 0.6099654
#> bin_iv total_iv breaks is_special_values
#> 1: 0.199263323 0.2906929 5000 FALSE
#> 2: 0.002627653 0.2906929 15000 FALSE
#> 3: 0.044046722 0.2906929 115000 FALSE
#> 4: 0.044755251 0.2906929 Inf FALSE
#>
#> $bill_amt3
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt3 [-Inf,5000) 5785 0.23363354 4395 1390 0.2402766 -0.9916243
#> 2: bill_amt3 [5000,10000) 2035 0.08218570 1247 788 0.3872236 -0.2994583
#> 3: bill_amt3 [10000,155000) 15227 0.61495901 7126 8101 0.5320155 0.2877770
#> 4: bill_amt3 [155000, Inf) 1714 0.06922176 598 1116 0.6511085 0.7834549
#> bin_iv total_iv breaks is_special_values
#> 1: 0.205103665 0.3051728 5000 FALSE
#> 2: 0.007229909 0.3051728 10000 FALSE
#> 3: 0.051161575 0.3051728 155000 FALSE
#> 4: 0.041677690 0.3051728 Inf FALSE
#>
#> $bill_amt4
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt4 [-Inf,5000) 6019 0.24308388 4563 1456 0.2419006 -0.9827478
#> 2: bill_amt4 [5000,10000) 2036 0.08222608 1252 784 0.3850688 -0.3085490
#> 3: bill_amt4 [10000,135000) 14734 0.59504867 6904 7830 0.5314239 0.2854011
#> 4: bill_amt4 [135000, Inf) 1972 0.07964137 647 1325 0.6719067 0.8763610
#> bin_iv total_iv breaks is_special_values
#> 1: 0.209927969 0.325774 5000 FALSE
#> 2: 0.007673121 0.325774 10000 FALSE
#> 3: 0.048691988 0.325774 135000 FALSE
#> 4: 0.059480954 0.325774 Inf FALSE
#>
#> $bill_amt5
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt5 [-Inf,10000) 8454 0.34142401 6128 2326 0.2751360 -0.8091789
#> 2: bill_amt5 [10000,130000) 14391 0.58119624 6639 7752 0.5386700 0.3145290
#> 3: bill_amt5 [130000, Inf) 1916 0.07737975 599 1317 0.6873695 0.9473896
#> bin_iv total_iv breaks is_special_values
#> 1: 0.20581636 0.3305998 10000 FALSE
#> 2: 0.05774448 0.3305998 130000 FALSE
#> 3: 0.06703900 0.3305998 Inf FALSE
#>
#> $bill_amt6
#> variable bin count count_distr neg pos posprob woe
#> 1: bill_amt6 [-Inf,10000) 9013 0.36399984 6509 2504 0.2778209 -0.7957569
#> 2: bill_amt6 [10000,130000) 13966 0.56403215 6309 7657 0.5482601 0.3531826
#> 3: bill_amt6 [130000, Inf) 1782 0.07196801 548 1234 0.6924804 0.9712804
#> bin_iv total_iv breaks is_special_values
#> 1: 0.21265520 0.3486324 10000 FALSE
#> 2: 0.07061633 0.3486324 130000 FALSE
#> 3: 0.06536092 0.3486324 Inf FALSE
#>
#> $pay_amt1
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt1 [-Inf,500) 5963 0.24082226 2675 3288 0.5514003 0.36586960324
#> 2: pay_amt1 [500,5000) 12696 0.51274181 6853 5843 0.4602237 0.00009735736
#> 3: pay_amt1 [5000,16000) 4838 0.19538791 2864 1974 0.4080198 -0.21261780170
#> 4: pay_amt1 [16000, Inf) 1264 0.05104802 974 290 0.2294304 -1.05199087427
#> bin_iv total_iv breaks is_special_values
#> 1: 0.032347606745362 0.09096089 500 FALSE
#> 2: 0.000000004860019 0.09096089 5000 FALSE
#> 3: 0.008726079449159 0.09096089 16000 FALSE
#> 4: 0.049887202211970 0.09096089 Inf FALSE
#>
#> $pay_amt2
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt2 [-Inf,500) 5887 0.23775292 2797 3090 0.5248853 0.25916318
#> 2: pay_amt2 [500,9500) 16208 0.65457776 8653 7555 0.4661278 0.02384302
#> 3: pay_amt2 [9500,15500) 1384 0.05589435 857 527 0.3807803 -0.32669786
#> 4: pay_amt2 [15500, Inf) 1282 0.05177497 1059 223 0.1739470 -1.39836907
#> bin_iv total_iv breaks is_special_values
#> 1: 0.0160446007 0.105683 500 FALSE
#> 2: 0.0003724565 0.105683 9500 FALSE
#> 3: 0.0058379460 0.105683 15500 FALSE
#> 4: 0.0834279536 0.105683 Inf FALSE
#>
#> $pay_amt3
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt3 [-Inf,5000) 19465 0.78611526 10009 9456 0.4857950 0.10270428
#> 2: pay_amt3 [5000,6000) 1252 0.05056339 779 473 0.3777955 -0.33937615
#> 3: pay_amt3 [6000,12500) 2457 0.09922863 1377 1080 0.4395604 -0.08340667
#> 4: pay_amt3 [12500, Inf) 1587 0.06409273 1201 386 0.2432262 -0.97553295
#> bin_iv total_iv breaks is_special_values
#> 1: 0.0083187700 0.06930934 5000 FALSE
#> 2: 0.0056922797 0.06930934 6000 FALSE
#> 3: 0.0006876199 0.06930934 12500 FALSE
#> 4: 0.0546106750 0.06930934 Inf FALSE
#>
#> $pay_amt4
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt4 [-Inf,800) 8915 0.3600420 4763 4152 0.4657319 0.02225193
#> 2: pay_amt4 [800,4400) 10587 0.4275675 5378 5209 0.4920185 0.12761085
#> 3: pay_amt4 [4400,13800) 3893 0.1572231 2223 1670 0.4289751 -0.12649450
#> 4: pay_amt4 [13800, Inf) 1366 0.0551674 1002 364 0.2664714 -0.85305991
#> bin_iv total_iv breaks is_special_values
#> 1: 0.0001784249 0.04636773 800 FALSE
#> 2: 0.0069887500 0.04636773 4400 FALSE
#> 3: 0.0024997863 0.04636773 13800 FALSE
#> 4: 0.0367007677 0.04636773 Inf FALSE
#>
#> $pay_amt5
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt5 [-Inf,1000) 9550 0.38568717 5170 4380 0.4586387 -0.006284458
#> 2: pay_amt5 [1000,14000) 13928 0.56249748 7205 6723 0.4826967 0.090298760
#> 3: pay_amt5 [14000, Inf) 1283 0.05181535 991 292 0.2275916 -1.062421226
#> bin_iv total_iv breaks is_special_values
#> 1: 0.00001522863 0.0561618 1000 FALSE
#> 2: 0.00459993581 0.0561618 14000 FALSE
#> 3: 0.05154663223 0.0561618 Inf FALSE
#>
#> $pay_amt6
#> variable bin count count_distr neg pos posprob woe
#> 1: pay_amt6 [-Inf,5000) 20048 0.80966035 10340 9708 0.4842378 0.09646993
#> 2: pay_amt6 [5000,9800) 2607 0.10528654 1463 1144 0.4388186 -0.08641872
#> 3: pay_amt6 [9800, Inf) 2106 0.08505311 1563 543 0.2578348 -0.89771350
#> bin_iv total_iv breaks is_special_values
#> 1: 0.0075582201 0.07054034 5000 FALSE
#> 2: 0.0007831196 0.07054034 9800 FALSE
#> 3: 0.0621989972 0.07054034 Inf FALSE
Output hasil woebin
:
variable
: nama variabelbin
: hasil binning kategoricount
: jumlah data pada masing-masing bincount_distr
: distribusi masing-masing bin/total
dataneg
: banyaknya kelas negatif pada bin (default)pos
: banyaknya kelas postif pada binposprob
: pos
/count
masing-masing binwoe
: nilai woe untuk bin tersebut.Mari kita pahami menggunakan variable pay_2
!
Perhitungan manual:
# WOE pay 2
## binning 1
distr_good_1 <- binning$pay_2$pos[1] / sum(binning$pay_2$pos)
distr_bad_1 <- binning$pay_2$neg[1] / sum(binning$pay_2$neg)
log(distr_good_1/distr_bad_1)
#> [1] -0.5559102
## binning 2
distr_good_2 <- binning$pay_2$pos[2] / sum(binning$pay_2$pos)
distr_bad_2 <- binning$pay_2$neg[2] / sum(binning$pay_2$neg)
log(distr_good_2/distr_bad_2)
#> [1] 1.40745
💡 Insight:
Setelah kita mengetahui nilai WOE dan binning pada data, nilai
data/values di data train
maupun test
kita
perlu dikonversi ke nilai WOE sesuai kategori/bins yang sudah dihitung.
Kita bisa gunakan fungsi woebin_ply()
dari library
scorecard
, dengan parameter:
dt
: dataframe yang akan ditransformasi -> data
trainbins
: informasi binning yang dihasilkan dari
woebin
#> ✔ Woe transformating on 24761 rows and 23 columns in 00:00:01
#> [1] 24761
#> ✔ Woe transformating on 6191 rows and 23 columns in 00:00:00
Information Value digunakan untuk melihat feature importance pada data behavior kita. Nilai IV yang tinggi mengindikasikan kekuatan suatu feature/variabel dalam memprediksi target.
📐 Formula IV:
\[ \sum_{i=1}^n (Distr_{Good_i} - Distr_{Bad_i}) \times ln(\frac{Distr_{Good_i}}{Distr_{Bad_i}}) \]
📌 Cara mengeluarkan nilai information value (iv) pada data:
Gunakan fungsi iv()
, dengan parameter:
dt
: data frame train hasil woey
: nama variabel y pada datapositive
: kelas positive (debitur good)Menurut (Siddiqi, Naeem), skor IV dapat dikategorikan menjadi nilai berikut:
❓ Nilai IV yang sudah kita dapat digunakan sebagai feature elimination, variabel apa saja yang akan dieliminasi?
Logistic regression merupakan salah satu metode klasifikasi yang konsepnya hampir mirip dengan regresi linear. Hanya saja, dalam binary logistic regression tidak menghitung secara spesifik nilai prediksi target variabel, namun menghitung kemungkinan pada masing-masing kelas target.
Persamaan linear regression
\[ y = b_0 + b_1x \]
Untuk mengubah nilai linear ke dalam nilai logistic dibutuhkan fungsi logit:
\[ y = log(\frac{p}{1-p}) \]
Nilai y ini merupakan nilai log of odds. Nilai ini tidak bisa diinterpretasikan. Sehingga terkadang harus diubah terlebih dahulu ke dalam nilai odds
\[ odds = \frac{p}{1-p} \]
Selanjutnya elakukan substitusi nilai logit/log of odds ke dalam persamaan linear.
\[ log(\frac{p}{1-p}) = b_0 + b_1x \]
Output dari hasil prediksi model logistic regression adalah probability atau peluang. Dalam bentuk seperti berikut:
\[ p = \frac{1}{1+e^{-y}} = \frac{1}{1+e^{-(b_0 + b_1x)}} \]
#>
#> Call:
#> glm(formula = gb_flag ~ ., family = "binomial", data = train_woe_final)
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -0.09518 0.01826 -5.212 1.87e-07 ***
#> limit_bal_woe 0.79347 0.02492 31.844 < 2e-16 ***
#> education_woe 0.18240 0.06431 2.836 0.004566 **
#> age_woe 0.04454 0.07850 0.567 0.570502
#> pay_1_woe 0.60082 0.02105 28.547 < 2e-16 ***
#> pay_2_woe 0.10380 0.02752 3.772 0.000162 ***
#> pay_3_woe 0.34498 0.02548 13.540 < 2e-16 ***
#> pay_4_woe 0.36329 0.02441 14.882 < 2e-16 ***
#> pay_5_woe 0.28930 0.02651 10.913 < 2e-16 ***
#> pay_6_woe 0.35695 0.02622 13.611 < 2e-16 ***
#> bill_amt1_woe -0.72112 0.07208 -10.005 < 2e-16 ***
#> bill_amt2_woe 0.67595 0.08265 8.179 2.87e-16 ***
#> bill_amt3_woe 0.28239 0.07988 3.535 0.000408 ***
#> bill_amt4_woe 0.31885 0.07642 4.172 3.01e-05 ***
#> bill_amt5_woe 0.17014 0.06859 2.481 0.013116 *
#> bill_amt6_woe 0.55784 0.05549 10.054 < 2e-16 ***
#> pay_amt1_woe 0.57165 0.07975 7.168 7.59e-13 ***
#> pay_amt2_woe 0.49593 0.06866 7.223 5.08e-13 ***
#> pay_amt3_woe 0.12716 0.07939 1.602 0.109212
#> pay_amt4_woe -0.31722 0.08895 -3.566 0.000362 ***
#> pay_amt5_woe 0.26646 0.07771 3.429 0.000606 ***
#> pay_amt6_woe 0.05179 0.07377 0.702 0.482632
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 34169 on 24760 degrees of freedom
#> Residual deviance: 21294 on 24739 degrees of freedom
#> AIC: 21338
#>
#> Number of Fisher Scoring iterations: 5
Nagelkereke R squared
\[ 1- \frac{D}{D_0} \]
#> [1] 0.3768035
Setelah model didapatkan, prediksi dapat dilakukan terhadap data test yang sudah ditransformasi ke dalam WOE. Prediksi ini dilakukan dengan tujuan 2 hal:
Pada tahapan ini dapat menggunakan fungsi predict
.
📌 Syntax:
predict(model, newdata, type = "response")
Keterangan:
model
: model logistic regressionnew_data
: data untuk diprediksi bisa data test ataupun
data barutype = "response"
untuk menghasilkan prediksi berupa
peluang❓ Prediksikan data test hasil transformasi WOE. Simpan pada kolom
baru bernama pred_Risk
# Melakukan prediksi terhadap data test
test_woe_final$pred_Risk <- predict(model,
newdata = test_woe_final,
type = "response")
head(test_woe_final)
📌 Ingat kembali, keluaran dari model logistic regression adalah dalam bentuk peluang.
Sehingga ntuk menyatakan prediksi jatuh ke kelas default(1) atau not default(0) dapat menggunakan threshold/ambang batas.
❓ Klasifikasikan data test_woe_final
berdasarkan
pred_Risk
dan simpan ke dalam kolom baru bernama
pred_label
# case when
test_woe_final <- test_woe_final %>%
mutate(pred_label = case_when(pred_Risk > 0.5 ~ 1,
TRUE ~ 0))
tail(test_woe_final)
Pada model logistic regression, output yang dihasilkan berupa prediksi apakah default atau tidak default dalam bentuk probabilitas.
Untuk mempremudah pengambilan keputusan apakah seorang debitur akan default dan tidak default maka hasil binning dapat dibuat menjadi penilaian dalam bentuk score point untuk setiap bin yang diakumulasikan menjadi scorecard.
Manfaat pembentukan scorecard antara lain:
Rumus umum pembentukan Points score dalam setiap binning (Siddiqi, Naeem)
\[ Points_{ij} = (\frac{pdo}{ln(2)}) \times -Coef_i \times WOE_{ij} \]
Keterangan:
Coef
: Hasil nilai coefficient dari logistic
regressionWOE
: Nilai WOE dari setiap binning untuk setiap
variabelnya📌 Untuk membuat scorecard
dapat menggunakan fungsi
scorecard()
dengan parameter:
bins
: object binning hasil woebin()
model
: object model hasil glm()
pdo
: points to double the odds, ketika
skor kredit suatu individu naik sebesar PDO, itu mengindikasikan bahwa
peluang (odds) untuk individu itu mengalami perubahan sebanyak dua kali
lipat. Sebaliknya, jika skor kredit turun sebesar PDO, peluangnya
mengalami penurunan setengahnya (ditentukan oleh user).odds0
: Tingkat keketatan / target
odds, rasio kemungkinan default terhadap kemungkinan tidak default.
Contoh: 1/19, artinya 1 yg akan default dibanding 19 yang tidak default.
Semakin kecil, semakin ketat (ditentukan oleh user)points0
: Target points ketika
odds0
tercapai. Contoh: points0 = 600 dan odds0 = 1/19,
maka pada poin 600 berarti terdapat 1 debitur default terhadap 19
debitur lain yang tidak default (ditentukan oleh user)pdo = setiap kenaikan pdo, akan meningkatkan peluang berhasil bayar sebanyak 2 kali lipat
Ingin dihasilkan sebuah scorecard sesuai dengan kebutuhan user dengan odds 19:1 pada poin 600 dan ingin peluangnya berlipat ganda setiap kenaikan 20 poin, maka bagaimana pembentukan scorecardnya?
# membentuk scorecard 1
score_card <- scorecard(bins = binning,
model = model,
points0 = 600,
odds0 = 1/19,
pdo = 20)
# percobaan 2 membentuk scorecard dengan pdo 50
score_card_2 <- scorecard(bins = binning,
model = model,
points0 = 600,
odds0 = 1/19,
pdo = 50)
# percobaan 2 membentuk scorecard dengan odds 1/40
score_card_3 <- scorecard(bins = binning,
model = model,
points0 = 600,
odds0 = 1/40,
pdo = 20)
Pembuktian:
\[ Points_{ij} = (\frac{pdo}{ln(2)}) \times -Coef_i \times WOE_{ij} \]
Ingin dicari nilai points untuk bin kedua [50000,150000)
pada variabel limit_bal
#> [1] -8
📌 Untuk mengubah nilai karakteristik debitur menjadi points dan
total points (score) secara otomatis, dapat menggunakan fungsi
scorecard_ply()
dengan parameter:
dt
: dataframe yang berisi karakteristik asli debitur.
Nama kolom harus sama dengan nama kolom data train.card
: Objek hasil fungsi scorecard()
.only_total_score
: TRUE/FALSE, apakah hanya menampilkan
total score atau tidak.Mari kita terapkan scorecard_ply()
pada dataframe
train
.
Bagaimana score bisa didapatkan?
Menurut (Siddqi, Naeem), rumus score adalah sebagai berikut:
\[ factor = \frac{pdo}{ln(2)} \]
\[ offset = points0 - factor*log(odds0) \]
\[ basepoint = offset - factor*\beta_0 \]
\[ score = basepoint + \sum_{i = 1}^n points_n \]
Perlu diketahui bahwa untuk bisa menggunakan pembuktian di atas, kita memerlukan koefisien logistic regression berupa \(\beta_0\) dan \(\beta_1\). Sehingga penggunaan rumus di atas bersifat eksklusif hanya untuk model logistic regression.
Mari kita buktikan score pada baris pertama
factor <- 20/log(2)
offset <- 600 - (factor*log(19))
basepoint <- offset - factor*-0.09518
basepoint
#> [1] 517.7878
Setelah basepoint didapatkan, selanjutnya mari kita hitung score total untuk masing-masing debitur.
score %>%
mutate(total = (limit_bal_points * 1) + (education_points * 1) + (age_points * 1) +
(pay_1_points * 1) + (pay_2_points * 1) + (pay_3_points * 1) + (pay_4_points * 1) + (pay_5_points * 1) +
(pay_6_points * 1) + (bill_amt1_points * 1) + (bill_amt2_points * 1) + (bill_amt3_points * 1) +
(bill_amt4_points * 1) + (bill_amt5_points * 1) + (bill_amt6_points * 1) +
(pay_amt1_points * 1) + (pay_amt2_points * 1) + (pay_amt3_points * 1) + (pay_amt4_points * 1) + (pay_amt5_points * 1) + (pay_amt6_points * 1),
total_again = round(total + (517.7878))) %>%
select(score, total, total_again)
Terbukti 🥰
Selain menggunakan cara pertama yang hanya dapat digunakan apabila kita menggunakan model logistic regression, bagaimana menghitung score dengan cara yang lebih general?
Pada dasarnya, menurut Siddqi Naeem perhitungan general Score dapat dihitung menggunakan rumus berikut
\[ Score = Offset + Factor\times -ln (odds_{default}) \]
\(Odds\) adalah peluang kejadian terjadi dibagi dengan peluang kejadian tidak terjadi. odds adalah besaran perbandingan peluang antara kejadian terjadi dengan kejadian tidak terjadi.
\[ Odds(default) = \frac{P(default)}{1-P(default)} \]
\[ Odds(default) = \frac{P(default)}{P(not.default)} \]
# Melakukan prediksi terhadap data train
train_woe_final$pred_Risk <- predict(model,
newdata = train_woe_final,
type = "response")
head(train_woe_final, 3)
train_woe_final$logodd <- -1*log(
(train_woe_final$pred_Risk + 0.0001) / (1 - train_woe_final$pred_Risk + 0.0001)
)
train_woe_final %>%
select(pred_Risk, logodd)
ketika: pdo = 20, odds0 = 1/19, points0 = 600
#> [1] 28.8539
# offset = points0 - factor * ln_odds # kalo log(19)
offset = points0 - factor * ln_odds # klo log (1/19), rumus yg dipke lib scorecard
offset
#> [1] 515.0414
\[ Score = Offset + Factor\times - ln (odds_{default}) \]
train_woe_final$cscore <- round((offset + factor * train_woe_final$logodd),0)
train_woe_final$cscore %>% head()
#> [1] 489 567 503 481 593 498
#> [1] 489 567 503 480 592 498
Terbukti 🥰