Perusahaan Alembert merupakan perusahaan yang bergerak di bidang layanan pinjaman usaha bagi sektor UMKM. Karena adanya pandemik ini, perusahaan berusaha memberikan pelayanan berupa keringanan pinjaman bagi pelanggan yang disebut sebagai rekomendasi tindak lanjut. Pemberian rekomendasi tindak lanjut pada pelanggan ini didasari pada kriteria tertentu, dan perlu ditentukan faktor-faktor apa saja yang berpengaruh sehingga pelanggan mendapatkan treatment tertentu yang masuk dalam rekomendasi tindak lanjut program dari perusahaan.
Tujuan Project: Klasifikasi nasabah yang akan dimasukkan pada rekomendasi tindak lanjut. Pada kelas target rekomendasi tindak lanjut ini sendiri terdiri dari beberapa kelas seperti restrukturisasi dan angsuran biasa.
Model: Regresi multinomial.
Referensi model (Ref): Kelas pada rekomendasi tindak lanjut yang memiliki banyak pelanggan.
Data: Data yang digunakan terdiri dari 1000 baris.
data = read.csv("https://storage.googleapis.com/dqlab-dataset/project.csv")
Setelah data berhasil di import, cobalah kamu untuk menginspeksi dataset dengan jalan
# Enam baris teratas data
head(data)
## X NAMA_NASABAH NOMOR_KONTRAK DOMISILI KARAKTER
## 1 0 YOLI SEPINA NAINGGOLAN 0 MASIH TETAP KOOPERATIF
## 2 1 ERWIN NASUTION 1 MASIH TETAP TIDAK KOOPERATIF
## 3 2 HUSIN 2 MASIH TETAP TIDAK KOOPERATIF
## 4 3 HARITSYAH 3 PINDAH PERMANEN KOOPERATIF
## 5 4 HARIRI PANGGABEAN 4 MASIH TETAP TIDAK KOOPERATIF
## 6 5 JHON PREDDY HUTABARAT 5 MASIH TETAP KOOPERATIF
## PROFESI KONDISI_USAHA KONDISI_JAMINAN STATUS PRODUK PYD
## 1 IBU RUMAH TANGGA 2 Baik 2 3 30000000
## 2 NELAYAN 3 Rusak 8 3 10000000
## 3 LAINNYA 3 Baik 8 3 60000000
## 4 PNS 1 Rusak 7 3 90000000
## 5 WIRAUSAHA / PEDAGANG 1 Baik 8 3 150000000
## 6 WIRAUSAHA / PEDAGANG 1 Baik 7 3 40000000
## TENOR OSL KEWAJIBAN KOLEKTIBILITAS COUNT_SURVEY
## 1 24 28750000 4896841 DALAM PENGAWASAN KHUSUS 1
## 2 12 2040693 0 MACET 1
## 3 24 0 0 MACET 1
## 4 18 0 0 MACET 1
## 5 18 19844807 0 MACET 1
## 6 36 27298726 2208516 DALAM PENGAWASAN KHUSUS 1
## REKOMENDASI_TINDAK_LANJUT
## 1 Angsuran Biasa
## 2 Penarikan
## 3 Penarikan
## 4 Angsuran Biasa
## 5 Penarikan
## 6 Restrukturisasi
# Tampilkan tipe data setiap kolomnya
str(data)
## 'data.frame': 1000 obs. of 17 variables:
## $ X : int 0 1 2 3 4 5 6 7 8 9 ...
## $ NAMA_NASABAH : chr "YOLI SEPINA NAINGGOLAN" "ERWIN NASUTION" "HUSIN" "HARITSYAH" ...
## $ NOMOR_KONTRAK : int 0 1 2 3 4 5 6 7 8 9 ...
## $ DOMISILI : chr "MASIH TETAP" "MASIH TETAP" "MASIH TETAP" "PINDAH PERMANEN" ...
## $ KARAKTER : chr "KOOPERATIF" "TIDAK KOOPERATIF" "TIDAK KOOPERATIF" "KOOPERATIF" ...
## $ PROFESI : chr "IBU RUMAH TANGGA" "NELAYAN" "LAINNYA" "PNS" ...
## $ KONDISI_USAHA : int 2 3 3 1 1 1 3 2 2 3 ...
## $ KONDISI_JAMINAN : chr "Baik" "Rusak" "Baik" "Rusak" ...
## $ STATUS : int 2 8 8 7 8 7 7 7 7 3 ...
## $ PRODUK : int 3 3 3 3 3 3 3 3 3 3 ...
## $ PYD : int 30000000 10000000 60000000 90000000 150000000 40000000 60000000 7500000 45000000 50000000 ...
## $ TENOR : int 24 12 24 18 18 36 36 4 24 36 ...
## $ OSL : int 28750000 2040693 0 0 19844807 27298726 19999200 7500000 45000000 6944100 ...
## $ KEWAJIBAN : int 4896841 0 0 0 0 2208516 6946592 0 0 7730984 ...
## $ KOLEKTIBILITAS : chr "DALAM PENGAWASAN KHUSUS" "MACET" "MACET" "MACET" ...
## $ COUNT_SURVEY : int 1 1 1 1 1 1 2 2 1 1 ...
## $ REKOMENDASI_TINDAK_LANJUT: chr "Angsuran Biasa" "Penarikan" "Penarikan" "Angsuran Biasa" ...
summary(data)
## X NAMA_NASABAH NOMOR_KONTRAK DOMISILI
## Min. : 0.0 Length:1000 Min. : 0.0 Length:1000
## 1st Qu.:249.8 Class :character 1st Qu.:249.8 Class :character
## Median :499.5 Mode :character Median :499.5 Mode :character
## Mean :499.5 Mean :499.5
## 3rd Qu.:749.2 3rd Qu.:749.2
## Max. :999.0 Max. :999.0
## KARAKTER PROFESI KONDISI_USAHA KONDISI_JAMINAN
## Length:1000 Length:1000 Min. :1.000 Length:1000
## Class :character Class :character 1st Qu.:2.000 Class :character
## Mode :character Mode :character Median :2.000 Mode :character
## Mean :2.273
## 3rd Qu.:3.000
## Max. :3.000
## STATUS PRODUK PYD TENOR
## Min. :2.000 Min. : 3.00 Min. : 500000 Min. : 3.00
## 1st Qu.:3.000 1st Qu.: 3.00 1st Qu.: 8000000 1st Qu.:12.00
## Median :7.000 Median : 3.00 Median : 15000000 Median :18.00
## Mean :5.379 Mean :12.45 Mean : 38537508 Mean :20.75
## 3rd Qu.:7.000 3rd Qu.: 7.00 3rd Qu.: 50000000 3rd Qu.:24.00
## Max. :8.000 Max. :77.00 Max. :500000000 Max. :48.00
## OSL KEWAJIBAN KOLEKTIBILITAS COUNT_SURVEY
## Min. : 0 Min. : 0 Length:1000 Min. :1.000
## 1st Qu.: 3999950 1st Qu.: 687487 Class :character 1st Qu.:1.000
## Median : 8687350 Median : 2008974 Mode :character Median :1.000
## Mean : 26562373 Mean : 5663981 Mean :1.036
## 3rd Qu.: 32082900 3rd Qu.: 4823198 3rd Qu.:1.000
## Max. :440932336 Max. :400900000 Max. :2.000
## REKOMENDASI_TINDAK_LANJUT
## Length:1000
## Class :character
## Mode :character
##
##
##
Pada data, sebenarnya tidak memerlukan nama pelanggan untuk diberikan rekomendasi. Atau dengan kata lain penanda pelanggan untuk diberikan rekomendasi cukup dengan melihat no_kontrak pelanggan itu saja.
data_reduce = data[-c(1,2)]
colnames(data_reduce)
## [1] "NOMOR_KONTRAK" "DOMISILI"
## [3] "KARAKTER" "PROFESI"
## [5] "KONDISI_USAHA" "KONDISI_JAMINAN"
## [7] "STATUS" "PRODUK"
## [9] "PYD" "TENOR"
## [11] "OSL" "KEWAJIBAN"
## [13] "KOLEKTIBILITAS" "COUNT_SURVEY"
## [15] "REKOMENDASI_TINDAK_LANJUT"
Seperti yang diketahui ketika data ditarik dari suatu sumber terkadang ada kondisi tipe data tidak dengan tepat direpresentasikan. Misalkan semua record/baris pada suatu kolom berisi seharusnya data numerik akan tetapi disajikan didalam suatu karakter angka.
R sendiri memiliki fungsi sapply yang dapat digunakan untuk mengkoversi tipe data. Dalam hal ini fungsi sapply menerima input/argumen fungsi berupa list, vector, atau data frame dan mengembalikan/menghasilkan output berupa vector atau matrix.
Jika tidak perlu di konversi, tidak perlu diubah
Tetapi, jika perlu dirubah kolom “PRODUK”, “PYD”, “TENOR”, dan “OSL” maka perintahnya berikut:
data_reduce[, 8:11] = sapply(data_reduce[, 8:11], as.numeric)
Data kategori dapat dipilih melalui kolom-kolom “KONDISI_USAHA”, “KONDISI_JAMINAN”, “REKOMENDASI_TINDAK_LANJUT”.
Ubah kolom “REKOMENDASI_TINDAK_LANJUT” sebagai faktor (gunakan as.factor).
Gunakan uji chi-square dapat digunakan untuk melihat hubungan antar variabel kategorik berikut:
data_kategorik = data_reduce[, c("KONDISI_USAHA","KONDISI_JAMINAN","REKOMENDASI_TINDAK_LANJUT")]
data_reduce$REKOMENDASI_TINDAK_LANJUT = as.factor(data_reduce$REKOMENDASI_TINDAK_LANJUT)
chisq.test(data_kategorik$KONDISI_USAHA, data_kategorik$REKOMENDASI_TINDAK_LANJUT)
## Warning in chisq.test(data_kategorik$KONDISI_USAHA,
## data_kategorik$REKOMENDASI_TINDAK_LANJUT): Chi-squared approximation may be
## incorrect
##
## Pearson's Chi-squared test
##
## data: data_kategorik$KONDISI_USAHA and data_kategorik$REKOMENDASI_TINDAK_LANJUT
## X-squared = 129.82, df = 6, p-value < 2.2e-16
chisq.test(data_kategorik$KONDISI_JAMINAN, data_kategorik$REKOMENDASI_TINDAK_LANJUT)
## Warning in chisq.test(data_kategorik$KONDISI_JAMINAN,
## data_kategorik$REKOMENDASI_TINDAK_LANJUT): Chi-squared approximation may be
## incorrect
##
## Pearson's Chi-squared test
##
## data: data_kategorik$KONDISI_JAMINAN and data_kategorik$REKOMENDASI_TINDAK_LANJUT
## X-squared = 162.87, df = 9, p-value < 2.2e-16
Berdasarkan uji chi-square diatas dapat disimpulkan bahwa variabel kondisi usaha memiliki hubungan dengan variabel rekomendasi tindak lanjut karena p-value < 0,05(5%). Kondisi jaminan juga memiliki hubungan dengan variabel rekomendasi tindak lanjut karena p-value < 0,05(5%).
Selain melihat hubungan pada data yang bersifat kategorikal, kita juga bisa melihat hubungan antar variabel numerikal. Ya. Kita akan menggunakan korelasi.
library(corrplot)
## corrplot 0.92 loaded
library(ggcorrplot)
## Loading required package: ggplot2
## Warning in register(): Can't find generic `scale_type` in package ggplot2 to
## register S3 method.
M = data_reduce[, 8:11]
# Library corrplot
# Pearson correlation
par(mfrow=c(2,2))
corrplot(cor(M), type="upper",order="hclust")
corrplot(cor(M), method="square",type="upper")
corrplot(cor(M), method="number",type="lower")
corrplot(cor(M), method="ellipse")
# Kendall correlation
par(mfrow=c(2,2))
corrplot(cor(M, method="kendall"), type="upper",order="hclust")
corrplot(cor(M, method="kendall"), method="square",type="upper")
corrplot(cor(M, method="kendall"), method="number",type="lower")
corrplot(cor(M, method="kendall"), method="ellipse")
# Library ggcorrplot
corr = round(cor(M), 1) # Pearson correlation
ggcorrplot(round(cor(M), 1),
hc.order = TRUE,
type = "lower",
lab = TRUE,
lab_size = 3,
method = "circle",
colors = c("tomato2","white","springgreen3"),
title = "Correlogram of Data Nasabah",
ggtheme = theme_bw)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
Metode yang digunakan untuk melihat korelasi adalah dengan metode korelasi pearson dan korelasi kendall. Jika nilai korelasinya bernilai positif maka korelasi antar variabel tersebut berkorelasi kuat.
Dalam melakukan pemodelan tentu kita perlu meninjau variabel-variabel apa saja yang berpengaruh pada model kita, khususnya pada klasifikasi. Pada kesempatan ini kita menggunakan model Regresi Multinomial.
Lalu bagaimana menentukan variabel apa saja yang berpengaruh tersebut?
Ada banyak alternatif, salah satunya ialah Information Gain. Melalui information gain diambil nilai importance variabel yang lebih dari 0.02 (kamu dapat eksplorasi apa yang terjadi apabila kita mengambil nilai yang kurang dari 0.02).
colnames(data_reduce)
## [1] "NOMOR_KONTRAK" "DOMISILI"
## [3] "KARAKTER" "PROFESI"
## [5] "KONDISI_USAHA" "KONDISI_JAMINAN"
## [7] "STATUS" "PRODUK"
## [9] "PYD" "TENOR"
## [11] "OSL" "KEWAJIBAN"
## [13] "KOLEKTIBILITAS" "COUNT_SURVEY"
## [15] "REKOMENDASI_TINDAK_LANJUT"
data_select = data_reduce[, c("KARAKTER","KONDISI_USAHA","KONDISI_JAMINAN","STATUS","KEWAJIBAN","OSL","KOLEKTIBILITAS","REKOMENDASI_TINDAK_LANJUT")]
data_non_na = na.omit(data_select)
Untuk memberikan performa model yang baik, maka pada data kita perlu dilakukan treatment tertentu, misalnya dilakukan scalling atau dilakukan pengelompokan data atau disebut juga bucketing.
data_select_new = data_select
data_select_new$KEWAJIBAN = scale(data_select_new$KEWAJIBAN)[, 1]
data_select_new$OSL = scale(data_select_new$OSL)[, 1]
data_select_new$KEWAJIBAN = cut(data_select_new$KEWAJIBAN, breaks = c(-0.354107,5,15,30))
data_select_new$KEWAJIBAN = as.factor(data_select_new$KEWAJIBAN)
data_select_new$OSL = cut(data_select_new$OSL, breaks= c(-0.60383,3,10,15))
data_select_new$OSL = as.factor(data_select_new$OSL)
data_select_new = na.omit(data_select_new)
Sebelum masuk pada pemodelan, kita perlu memisahkan data kita menjadi training dan testing (ada pula yang membaginya menjadi training, testing, dan validasi).
Tujuan dari pemisahan data ini ialah untuk melihat kemampuan model kita untuk melakukan prediksi sebagaimana tujuan dari pemodelan kita.
library(caret)
## Loading required package: lattice
index = createDataPartition(data_select_new$REKOMENDASI_TINDAK_LANJUT, p= .95, list = FALSE)
train = data_select_new[index,]
test = data_select_new[-index,]
Sekarang kita siap untuk masuk pada pemodelan.
train2 = train
# Setting the reference
train2$REKOMENDASI_TINDAK_LANJUT = relevel(train2$REKOMENDASI_TINDAK_LANJUT, ref = "Angsuran Biasa")
# training model
require(nnet)
## Loading required package: nnet
# training the multinomial mode
multinom_model = multinom(REKOMENDASI_TINDAK_LANJUT ~ ., data=train2)
## # weights: 64 (45 variable)
## initial value 1319.752232
## iter 10 value 740.291636
## iter 20 value 617.611445
## iter 30 value 612.855316
## iter 40 value 612.594366
## iter 50 value 612.580478
## iter 60 value 612.579999
## final value 612.579988
## converged
# checking model
summary(multinom_model)
## Warning in sqrt(diag(vc)): NaNs produced
## Call:
## multinom(formula = REKOMENDASI_TINDAK_LANJUT ~ ., data = train2)
##
## Coefficients:
## (Intercept) KARAKTERTIDAK KOOPERATIF KONDISI_USAHA
## Diskon Pelunasan -3.244546 1.813299 -0.7478194
## Penarikan -7.509439 3.719067 0.7555596
## Restrukturisasi -4.131078 -2.212290 0.9824744
## KONDISI_JAMINANHilang KONDISI_JAMINANPindah Tangan
## Diskon Pelunasan -26.4258559 -29.2016536
## Penarikan 0.8105445 0.5137542
## Restrukturisasi 0.5165099 -13.9274117
## KONDISI_JAMINANRusak STATUS KEWAJIBAN(5,15]
## Diskon Pelunasan -26.9436152 0.09293497 0
## Penarikan -0.9777548 0.09720577 0
## Restrukturisasi 0.1654408 0.32285928 0
## KEWAJIBAN(15,30] OSL(3,10] OSL(10,15]
## Diskon Pelunasan -3.996223 2.9621134 0
## Penarikan 1.154449 -27.2107637 0
## Restrukturisasi 28.897700 0.3346525 0
## KOLEKTIBILITASDIRAGUKAN KOLEKTIBILITASKURANG LANCAR
## Diskon Pelunasan 2.7430892 0.3192599
## Penarikan 4.4002729 1.2262682
## Restrukturisasi -0.2622012 -0.2150021
## KOLEKTIBILITASLANCAR KOLEKTIBILITASMACET
## Diskon Pelunasan 0.1967404 0.5590025
## Penarikan -32.9222221 1.7765398
## Restrukturisasi 0.9205171 -3.3061588
##
## Std. Errors:
## (Intercept) KARAKTERTIDAK KOOPERATIF KONDISI_USAHA
## Diskon Pelunasan 1.0967879 0.9002311 0.3829643
## Penarikan 1.2687458 0.6261935 0.3821727
## Restrukturisasi 0.3569305 0.7833132 0.1041433
## KONDISI_JAMINANHilang KONDISI_JAMINANPindah Tangan
## Diskon Pelunasan 4.812955e-13 4.648411e-14
## Penarikan 1.140597e+00 6.705835e-01
## Restrukturisasi 1.179181e+00 5.370705e-07
## KONDISI_JAMINANRusak STATUS KEWAJIBAN(5,15]
## Diskon Pelunasan 3.550206e-13 0.14047443 2.118292e-15
## Penarikan 1.286119e+00 0.10499286 NaN
## Restrukturisasi 1.193795e+00 0.03731573 0.000000e+00
## KEWAJIBAN(15,30] OSL(3,10] OSL(10,15]
## Diskon Pelunasan 9.725372e-16 1.054768e+00 3.486689e-16
## Penarikan 3.365132e-16 2.482327e-14 1.768231e-16
## Restrukturisasi 9.198348e-14 6.310624e-01 0.000000e+00
## KOLEKTIBILITASDIRAGUKAN KOLEKTIBILITASKURANG LANCAR
## Diskon Pelunasan 0.9190928 0.7412557
## Penarikan 0.8904435 0.7010523
## Restrukturisasi 0.5843858 0.1937207
## KOLEKTIBILITASLANCAR KOLEKTIBILITASMACET
## Diskon Pelunasan 9.598497e-01 1.2183108
## Penarikan 1.968480e-15 0.7345576
## Restrukturisasi 2.168910e-01 1.0783981
##
## Residual Deviance: 1225.16
## AIC: 1303.16
# converting the coefficients to odds by taking the exponential of the coefficients
exp(coef(multinom_model))
## (Intercept) KARAKTERTIDAK KOOPERATIF KONDISI_USAHA
## Diskon Pelunasan 0.0389862439 6.1306419 0.4733977
## Penarikan 0.0005478883 41.2259141 2.1288024
## Restrukturisasi 0.0160655478 0.1094497 2.6710572
## KONDISI_JAMINANHilang KONDISI_JAMINANPindah Tangan
## Diskon Pelunasan 3.337311e-12 2.079136e-13
## Penarikan 2.249132e+00 1.671555e+00
## Restrukturisasi 1.676167e+00 8.941326e-07
## KONDISI_JAMINANRusak STATUS KEWAJIBAN(5,15] KEWAJIBAN(15,30]
## Diskon Pelunasan 1.988550e-12 1.097390 1 1.838494e-02
## Penarikan 3.761547e-01 1.102087 1 3.172275e+00
## Restrukturisasi 1.179913e+00 1.381071 1 3.549045e+12
## OSL(3,10] OSL(10,15] KOLEKTIBILITASDIRAGUKAN
## Diskon Pelunasan 1.933880e+01 1 15.5349009
## Penarikan 1.522353e-12 1 81.4731015
## Restrukturisasi 1.397455e+00 1 0.7693562
## KOLEKTIBILITASKURANG LANCAR KOLEKTIBILITASLANCAR
## Diskon Pelunasan 1.3761090 1.217428e+00
## Penarikan 3.4084859 5.035709e-15
## Restrukturisasi 0.8065398 2.510588e+00
## KOLEKTIBILITASMACET
## Diskon Pelunasan 1.74892715
## Penarikan 5.90937368
## Restrukturisasi 0.03665671
head(round(fitted(multinom_model), 2))
## Angsuran Biasa Diskon Pelunasan Penarikan Restrukturisasi
## 1 0.81 0.01 0.00 0.18
## 2 0.48 0.00 0.51 0.01
## 3 0.26 0.02 0.72 0.00
## 4 0.98 0.00 0.01 0.02
## 5 0.49 0.20 0.30 0.00
## 6 0.69 0.02 0.00 0.28
# predicting the values for train dataset
train2$ClassPredicted = predict(multinom_model, newdata = train2, "class")
train_prob = predict(multinom_model, newdata = train2, "probs")
df = train_prob
df$max = apply(df,1,max)
## Warning in df$max = apply(df, 1, max): Coercing LHS to a list
train2$score = df$max
test_prob = predict(multinom_model, newdata = test, "probs")
df2 = test_prob
df2$max = apply(df2,1,max)
## Warning in df2$max = apply(df2, 1, max): Coercing LHS to a list
# Building classification table
tab_train = table(train2$REKOMENDASI_TINDAK_LANJUT, train2$ClassPredicted)
round((sum(diag(tab_train))/sum(tab_train))*100,4)
## [1] 67.1218
test$ClassPredicted = predict(multinom_model, newdata = test, "class")
test$score = df2$max
tab_test = table(test$REKOMENDASI_TINDAK_LANJUT, test$ClassPredicted)
round((sum(diag(tab_test))/sum(tab_test))*100,4)
## [1] 68.75
Akurasi model untuk training dan testing sudah cukup baik untuk diaplikasikan.
Sekian dan terima kasih sudah melihat project ini. Jika terdapat kesalahan mohon koreksinya.