Tugas ini ditujukan untuk untuk menganalisis anomali transaksi tabungan di sebuah bank. Data yang digunakan merupakan data sample 300 transaksi selama 30 hari terakhir. Tujuannya adalah untuk mengidentifikasi transaksi yang tidak biasa atau mencurigakan berdasarkan skor anomali. Untuk tahap awal, Kami melakukan proses pelabelan data menggunakan algoritma Isolation Forest. Nantinya, akan diperoleh sebuah dataset transaksi yang mempunyai variabel dependen: 1=Fraud/Anomali, dan 0=Bukan Fraud/Anomali.
Adapun untuk variabel independen yang digunakan untuk memprediksi variabel dependen tersebut adalah variabel RFM yaitu Recency (R), Frequency (F), dan Monetary (M). Di mana, R adalah lamanya nasabah tidak melakukan transaksi sampai dengan hari ini, dihitung dengan mengurangi tanggal hari ini dengan tanggal terakhir bertransaksi. Sedangkan F dan M berturut-turut adalah banyaknya dan besaran nominal transaksi yang telah dilakukan oleh si nasabah selama 30 bulan terakhir.
Tahap berikutya adalah melakukan prediksi menggunakan Logistic Regression, yang dilakukan dengan beberapa tahapan sebagai berikut:
library(broom)
library(isotree)
library(arm)
library(ISLR)
library(caret)
library(pROC)
library(tidyverse)
library(effects)
Detail dari metode yang
digunakan pada tugas ini dapat dilihat dari gambar di atas, di mana data
yang diakuisisi pertama-tama akan diidentifikasi anomali menggunakan
metode unsupervised-learning berbasis tree yaitu isolation forest.
Setelah mendapatkan data transaksi dengan label anomali, proses
dilanjutkan dengan membagi data menjadi dua kelompok yaitu train dan
test. Data train akan digunakan untuk melatih model regresi logistik,
dan data test digunakan untuk menguji dan mengevaluasi performa dari
model. Langkah terakhir yaitu melakukan interpretasi dari koefisien dan
metrik performance dari model yang sudah dilatih.
Tahapan pertama adalah dengan melakukan import data yang diperoleh dari database sumbernya. Seperti yang dijelaskan sebelumnya, data yang digunakan terdiri dari 3 variabel R-F-M. Proses akuisisi data ini cukup dilakukan dengan import file CSV. Data yang diambil merupakan data transaksi tabungan emas, dan untuk mensederhanakan perhitungan nominal monetary telah dibagi dengan 1000.
df_trans <- read.csv2("~/Documents/DMT/Tugas Statistik dan Analisis Data/tugas_5_logreg/trans3.csv", sep=";")
df_trans$monetary <- as.integer(df_trans$monetary)
df_trans <- df_trans %>% select(recency, frequency, monetary)
head(df_trans,3)
tail(df_trans,3)
Isolation Forest (IS) merupakan salah satu keluarga algoritma berbasis tree yang digunakan untuk melakukan deteksi anomali, dan umum digunakan untuk mendeteksi anomali pada transaksi institusi keuangan. Konsep dasar dari IS ini adalah dengan menyusun semua kemungkinan nilai yang ada menjadi sebuah binary tree. Proses pengenalan data anomali dari pohon yang dibentuk oleh IS ini dinamakan proses split yang dilakukan sampai dengan tercapai sebuah kondisi konvergen, semakin sedikit jumlah split yang dibutuhkan untuk sebuah individu data maka semakin tinggi pula nilai skor anomalinya, dan begitu pula sebaliknya. Output dari proses ini kemudian disimpan kebali ke dalam dataframe pada atribut baru yang dinamakan is_anomaly, di mana nilai yang menandakan sebuah individu adalah anomali=1, dan bukan anomali=0.
model <- isolation.forest(df_trans, ntrees = 100, sample_size = 300)
## Warning in isolation.forest(df_trans, ntrees = 100, sample_size = 300):
## Attempting to use more than 1 thread, but package was compiled without OpenMP
## support. See
## https://github.com/david-cortes/installing-optimized-libraries#4-macos-install-and-enable-openmp
scores <- predict(model, df_trans)
# Ambil score ke dataframe
df_trans$anomaly_score <- scores
# Set threshold misal, > 95% dianggap sebagai anomali
threshold <- quantile(scores, 0.95)
df_trans$is_anomaly <- df_trans$anomaly_score > threshold
hist(df_trans$anomaly_score,
main = "Distribusi anomali score",
xlab = "Score (semakin dekat ke 1 = anomaly)",
col = "red")
# View
df_trans$is_anomaly <- as.integer(df_trans$is_anomaly)
df_trans$monetary <- as.numeric(df_trans$monetary)
write.csv2(df_trans, "~/Documents/DMT/Tugas Statistik dan Analisis Data/tugas_5_logreg/trans_labeled.csv")
print("Score anomaly:")
## [1] "Score anomaly:"
print(threshold)
## 95%
## 0.539943
# Count rows per category
counts <- table(df_trans$is_anomaly)
# Calculate percentage
percentages <- (counts / nrow(df_trans)) * 100
# Combine into a data frame
result <- data.frame(
category = names(counts),
count = as.vector(counts),
percentage = round(percentages, 2) # rounded to 2 decimals
)
result
Dari hasil pembagian data menggunakan threshold quantil 95%, diperoleh bahwa score anomali ada di kisaran 0.6028407, artinya semua transaksi yang memiliki skor di atas angka tersebut akan dilabeli is_anomaly=1, dan sebaliknya jika di bawah skor tersebut, dilabeli is_anomaly=0.
Dari histogram dan view persentase jumlah transaksi anomali di atas, dapat dilihat bahwa terdapat 90% transaksi normal dengan total 270 transaksi, dan 10% transaksi anomali dengan jumlah 30 transaksi, sesuai dengan set-up threshold anomali yang sudah ditetapkan. Langkah berikutnya adalah dengan melakukan pemodelan klasifikasi data menggunakan regresi logistic dengan distribusi data binomial (karena target yang akan diprediksi bernilai biner 0 dan 1).
summary(df_trans)
## recency frequency monetary anomaly_score
## Min. : 1.00 Min. : 1.00 Min. :1.000e+00 Min. :0.3429
## 1st Qu.: 9.00 1st Qu.: 1.00 1st Qu.:8.875e+01 1st Qu.:0.3603
## Median :15.00 Median : 1.00 Median :3.995e+02 Median :0.3792
## Mean :15.74 Mean : 2.21 Mean :1.727e+04 Mean :0.4045
## 3rd Qu.:23.00 3rd Qu.: 2.00 3rd Qu.:4.756e+03 3rd Qu.:0.4260
## Max. :30.00 Max. :101.00 Max. :1.946e+06 Max. :0.8148
## is_anomaly
## Min. :0.00
## 1st Qu.:0.00
## Median :0.00
## Mean :0.05
## 3rd Qu.:0.00
## Max. :1.00
hist(df_trans$recency)
hist(df_trans$frequency)
hist(df_trans$monetary)
Karena skala data yang tidak berimbang, di mana untuk M memiliki rentang antara 1 sampai dengan 1945905.00, sedangkan untuk R dan F hanya sampai puluhan, maka sebenarnya perlu dilakukan preprocessing dengan melakukan mengaplikasikan logaritma basis 10 ke setiap variabel yang ada. Namun demikian setelah beberapa kali percobaan, didapatkan bahwa melakukan transformasi menggunakan logaritma natural malah menurunkan performance dan signifikansi predictive power dari variabel independen. Sehingga transformasi ini tidak akan dilakukan lebih lanjut untuk pemodelan.
Sebelum melakukan pemodelan, langkah awal yang sebaiknya dilakukan adalah membagi data menjadi 2 kelompok yaitu train, dan test. Tujuan utama dari proses ini adalah untuk mendapatkan sebuah model yang cukup fit, yang tidak terlalu confidence atau overfitting, namun juga tidak terlalu buruk akurasinya dalam melakukan prediksi terhadap data baru atau underfitting. Untuk kasus pada tugas ini, diambil proporsi 70% (210 baris) untuk data training dan 30% (90 baris) untuk data testing.
set.seed(123)
train_idx <- createDataPartition(df_trans$is_anomaly, p = 0.7, list = FALSE)
train <- df_trans[train_idx, ]
test <- df_trans[-train_idx, ]
prop.table(table(train$is_anomaly))
##
## 0 1
## 0.94761905 0.05238095
prop.table(table(test$is_anomaly))
##
## 0 1
## 0.95555556 0.04444444
Tahapan berikutnya adalah dengan melakukan training model regresi logistik, dengan menggunakan fungsi general linear model atau glm() yang ada di R, parameter yang perlu dilempar pada fungsi ini adalah formula regresi nya yaitu: is_anomaly <- recency + frequency + monetary dan jenis regresi logistik nya yaitu binomial.
model_lr <- glm(is_anomaly ~ recency + frequency + monetary ,
data = train,
family = binomial(link = "logit"))
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
Langkah berikutnya adalah menginterpretasikan model agar kita bisa mendapat intuisi dari parameter model regresi logistik ini.
summary(model_lr)
##
## Call:
## glm(formula = is_anomaly ~ recency + frequency + monetary, family = binomial(link = "logit"),
## data = train)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -4.941e+00 1.282e+00 -3.853 0.000117 ***
## recency -1.673e-01 8.875e-02 -1.885 0.059450 .
## frequency 5.638e-01 1.748e-01 3.225 0.001260 **
## monetary 6.427e-05 1.697e-05 3.788 0.000152 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 86.296 on 209 degrees of freedom
## Residual deviance: 24.273 on 206 degrees of freedom
## AIC: 32.273
##
## Number of Fisher Scoring iterations: 9
model_ <- glm(is_anomaly ~ recency,
data = train,
family = binomial(link = "logit"))
plot(allEffects(model_),
type = "response",
main = "Probability Curves for R Features",
rows = 1, cols = 1)
model_ <- glm(is_anomaly ~ frequency,
data = train,
family = binomial(link = "logit"))
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
plot(allEffects(model_),
type = "response",
main = "Probability Curves for F Features",
rows = 1, cols = 1)
model_ <- glm(is_anomaly ~ monetary,
data = train,
family = binomial(link = "logit"))
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
plot(allEffects(model_),
type = "response",
main = "Probability Curves for M Features",
rows = 1, cols = 1)
Dari hasil observasi di atas dapat terlihat bahwa selain intercept, variabel yang memiliki predictive power adalah frekuensi dan total jumlah nominal transaksi, dengan p-value masing-masing 0.001260 dan 000152. Dari kurva S untuk masing-masing variabel juga dapat dilihat bahwa yang memiliki bentuk kurva S yang paling ideal adalah variabel Frequency (log F), yang kedua ada Monetary (log M). Untuk R sepintas memiliki kurva S terpotong yang memiliki trend cenderung menurun.
Ketiga variabel ini tentu saja tidak dapat diartikan secara terpisah untuk dapat memprediksi kecenderungan fraud. F dan M memiliki trend naik, sedangkan R memiliki trend turun yang dapat diartikan bahwa, semakin tinggi frekuensi dan total nominal transaksi seseorang, sedangkan transaksi terakhirnya baru-baru saja dilakukan, maka kemungkinan seseorang diduga fraud semakin tinggi. Dan juga sebaliknya, ketika frekuensi dan total nominal transaksi rendah, sedangkan transaksi sudah lama dilakukan maka kecenderungan seseorang melakukan fraud semakin rendah.
Dari hasil pemodelan diperoleh bahwa odds-ratio yang dihasilkan masing-masing adalah:
Nilai recency negatif -1.67 mengindikasikan trend yang sebaliknya dari frequency dan monetary seperti yang dijelaskan sebelumnya, dan juga dapat diartikan bahwa setiap 1 hari yang dilewati oleh seorang nasabah maka kemungkinan nasabah tersebut melakukan fraud berkurang 16%, angka ini diperoleh dari inverse perhitungan odds-ratio yaitu \(1 - (e^{-0.167}) = 0.16\). Untuk variabel frequency, setiap penambahan 1 transaksi, maka kemungkinan fraud akan naik sebesar 75%, angka ini diperoleh dari inverse perhitungan odds-ratio yaitu \(1 - (e^{0.563}) = -0.75\). Terakhir untuk variabel monetary, setiap penambahan 1 rupiah (meskipun ini sulit diinterpretasikan), akan menambah kemungkinan fraud sebesar 0.0006427%, diperoleh dari perhitungan odds-ratio yaitu \(1 - e^{0.000006427}\).
Performa dari model dapat dilihat dari eksekusi perintah di bawah ini, di mana model akan ditest menggunakan 30% yaitu 90 baris data transaksi yang sudah dipilah sebelumnya dari data training. Dari hasil test diperoleh bahwa model cukup akurat, di mana akurasi nya mencapai 98%. Namun demikian, akurasi merupakan -overall- performance test, jika kita melakukan zoom-in terhadap kemampuan prediksi, maka di dapatkan bahwa untuk positivity-rate yang didapat dari specificity, model mampu memprediksi hingga 100% transaksi fraud. Sedangkan untuk transaksi normal, model memiliki sedikit kesalahan sebanyak 3.6% yang salah dalam memprediksi transaksi normal sebagai fraud (false positive).
predicted <- predict(model_lr, test, type = "response")
roc_obj <- roc(test$is_anomaly, predicted)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
optimal <- coords(roc_obj, "best", ret = "threshold", transpose = FALSE)
optimal <- as.numeric(optimal)
test$pred_class <- ifelse(predicted >= optimal, 1, 0)
confusionMatrix(factor(test$pred_class),
factor(test$is_anomaly),
positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 83 0
## 1 3 4
##
## Accuracy : 0.9667
## 95% CI : (0.9057, 0.9931)
## No Information Rate : 0.9556
## P-Value [Acc > NIR] : 0.4290
##
## Kappa : 0.7109
##
## Mcnemar's Test P-Value : 0.2482
##
## Sensitivity : 1.00000
## Specificity : 0.96512
## Pos Pred Value : 0.57143
## Neg Pred Value : 1.00000
## Prevalence : 0.04444
## Detection Rate : 0.04444
## Detection Prevalence : 0.07778
## Balanced Accuracy : 0.98256
##
## 'Positive' Class : 1
##
Dari tugas ini, dapat disimpulkan bahwa model regresi logistik dapat membantu dalam memprediksi kejadian fraud yang nantinya dapat digunakan oleh pihak manajemen dalam memantau tingkat risiko transaksi. Pemodelan ini masih cukup sangat sederhana, di mana data yang diambil hanya 300 transaksi dalam waktu satu bulan, dan dari evaluasi performance model didapatkan model mengalami overfitting untuk memprediksi fraud, sehingga diperlukan re-engineering, dan imputasi data (agar data seimbang antara label fraud dan non-fraud) sehingga performance model bisa lebih baik lagi.