Laporan ini membahas keparahan klaim asuransi mobil menggunakan regresi logistik ordinal. Formatnya dibuat seperti laporan praktikum/RPubs: ada konsep singkat, proses olah data, kode R, output, lalu interpretasi. Jadi isi tetap tentang claim severity, tetapi penyajiannya tidak dibuat seperti naskah jurnal formal.
Dalam asuransi kendaraan, klaim tidak hanya dilihat dari ada atau
tidaknya klaim, tetapi juga dari seberapa parah klaim
tersebut. Keparahan klaim dapat dipahami sebagai besar kecilnya
kerugian yang harus dibayar oleh perusahaan asuransi. Pada data ini,
nilai klaim total (total_claim_amount) diubah menjadi tiga
kategori berurutan:
Karena kategori tersebut memiliki urutan, metode yang sesuai adalah regresi logistik ordinal, bukan regresi logistik biner dan bukan multinomial biasa.
| Istilah | Makna dalam laporan ini |
|---|---|
| Klaim asuransi | Permintaan pembayaran kerugian dari pemegang polis kepada perusahaan asuransi. |
| Keparahan klaim | Besarnya kerugian klaim. Pada laporan ini dibuat menjadi Rendah, Sedang, dan Tinggi. |
| Variabel ordinal | Variabel kategori yang memiliki urutan. Contoh: Rendah < Sedang < Tinggi. |
| Regresi logistik ordinal | Model untuk menganalisis respon kategori yang memiliki urutan. |
| Proportional odds | Asumsi bahwa pengaruh prediktor relatif sama pada setiap batas kategori ordinal. |
| Odds ratio (OR) | Ukuran pengaruh prediktor. OR > 1 berarti peluang masuk kategori klaim lebih tinggi meningkat. |
| Cutpoint / threshold | Titik batas dalam model ordinal yang memisahkan kategori respon. |
| Fraud | Indikasi klaim yang dilaporkan sebagai kecurangan. |
| Data leakage | Kondisi ketika model memakai variabel yang terlalu dekat dengan jawaban, sehingga hasil model tampak bagus secara tidak wajar. |
Tujuan analisis adalah mengetahui faktor-faktor yang berpengaruh
terhadap kategori keparahan klaim asuransi mobil. Variabel respon
dibentuk dari total_claim_amount, lalu dikategorikan
berdasarkan tertil empiris agar jumlah observasi pada kategori Rendah,
Sedang, dan Tinggi relatif seimbang.
| Komponen | Keterangan |
|---|---|
| Dataset | insurance_claims.csv |
| Unit analisis | Satu baris data klaim asuransi mobil |
| Variabel respon | severity_level (Tingkat besarnya kerugian) |
| Bentuk respon | Ordinal: Rendah < Sedang < Tinggi |
| Metode utama | Regresi logistik ordinal dengan model proportional odds |
| Ukuran interpretasi | Odds ratio, yaitu exp(\beta) |
Regresi logistik ordinal digunakan ketika variabel respon berupa kategori yang memiliki urutan. Pada laporan ini, urutannya adalah Rendah < Sedang < Tinggi. Model tidak hanya memprediksi kategori, tetapi juga menjelaskan bagaimana prediktor menggeser peluang klaim menuju kategori yang lebih tinggi.
Model proportional odds dengan fungsi MASS::polr() dapat
ditulis sebagai berikut:
\[ \log\left(\frac{P(Y \leq j)}{P(Y > j)}\right) = \alpha_j - x\beta, \quad j = 1, 2 \]
Keterangan simbol:
| Simbol | Arti |
|---|---|
| \(Y\) | Kategori keparahan klaim. |
| \(j\) | Batas kategori ordinal. Pada tiga kategori, ada dua batas. |
| \(P(Y \leq j)\) | Peluang kumulatif berada pada kategori sampai batas ke-\(j\). |
| \(\alpha_j\) | Cutpoint atau ambang kategori. |
| \(x\beta\) | Kombinasi nilai prediktor dan koefisien regresi. |
| \(\exp(\beta)\) | Odds ratio untuk interpretasi pengaruh prediktor. |
Karena polr() memakai bentuk \(\alpha_j - x\beta\), maka koefisien positif
berarti peluang berpindah ke kategori yang lebih tinggi cenderung
meningkat. Dengan kata lain, jika OR > 1, prediktor tersebut
meningkatkan kecenderungan klaim menjadi lebih parah.
# Package utama yang dipakai:
# MASS : membuat model regresi logistik ordinal dengan fungsi polr()
# dplyr : manipulasi data
# ggplot2 : visualisasi
# knitr : membuat tabel rapi di output HTML
# scales : format angka dan persen
library(MASS)
library(dplyr)
library(ggplot2)
library(knitr)
library(scales)path <- params$data_path
if (!file.exists(path)) {
alt <- "/mnt/user-data/uploads/insurance_claims.csv"
if (file.exists(alt)) path <- alt
}
if (!file.exists(path)) {
stop("File insurance_claims.csv tidak ditemukan. Letakkan file CSV di folder yang sama dengan Rmd.")
}
raw <- read.csv(path, stringsAsFactors = FALSE)
dim(raw)## [1] 1000 40
Interpretasi awal:
dim(raw) menunjukkan jumlah baris dan kolom
dataset.head(raw) menampilkan beberapa baris pertama untuk
mengecek struktur data.Variabel respon yang digunakan adalah severity_level.
Variabel ini tidak tersedia langsung dalam data, sehingga dibentuk dari
total_claim_amount.
## 33.33333% 66.66667%
## 49390 65560
severity_level <- cut(
raw$total_claim_amount,
breaks = c(-Inf, q[1], q[2], Inf),
labels = c("Rendah", "Sedang", "Tinggi"),
ordered_result = TRUE
)
table(severity_level)## severity_level
## Rendah Sedang Tinggi
## 334 333 333
## severity_level
## Rendah Sedang Tinggi
## 0.334 0.333 0.333
Kategori dibuat berdasarkan tertil. Artinya, data dibagi menjadi tiga kelompok yang relatif seimbang berdasarkan nilai total klaim. Cara ini membantu model karena kategori respon tidak terlalu timpang.
Prediktor yang digunakan dipilih agar relevan secara aktuaria dan tidak langsung membocorkan nilai klaim total. Beberapa variabel numerik juga diskalakan agar interpretasi koefisien lebih mudah.
d <- data.frame(
severity_level = severity_level,
age = raw$age,
tenure_yr = raw$months_as_customer / 12,
premium100 = raw$policy_annual_premium / 100,
deduct500 = raw$policy_deductable / 500,
number_of_vehicles_involved = raw$number_of_vehicles_involved,
bodily_injuries = raw$bodily_injuries,
witnesses = raw$witnesses,
male = ifelse(toupper(raw$insured_sex) == "MALE", 1L, 0L),
edu_tinggi = ifelse(raw$insured_education_level %in% c("Masters", "MD", "JD", "PhD"), 1L, 0L),
propdmg = ifelse(toupper(raw$property_damage) == "YES", 1L, 0L),
fraud = ifelse(toupper(raw$fraud_reported) == "Y", 1L, 0L),
total_claim_amount = raw$total_claim_amount
)
d <- na.omit(d)
str(d)## 'data.frame': 1000 obs. of 13 variables:
## $ severity_level : Ord.factor w/ 3 levels "Rendah"<"Sedang"<..: 3 1 1 2 1 2 3 2 1 1 ...
## $ age : int 48 42 29 41 44 39 34 37 33 42 ...
## $ tenure_yr : num 27.3 19 11.2 21.3 19 ...
## $ premium100 : num 14.1 12 14.1 14.2 15.8 ...
## $ deduct500 : num 2 4 4 4 2 2 2 2 1 1 ...
## $ number_of_vehicles_involved: int 1 1 3 1 1 3 3 3 1 1 ...
## $ bodily_injuries : int 1 0 2 1 0 0 0 2 1 2 ...
## $ witnesses : int 2 0 3 2 1 2 0 2 1 1 ...
## $ male : int 1 1 0 0 1 0 1 1 0 1 ...
## $ edu_tinggi : int 1 1 1 1 0 1 1 0 1 1 ...
## $ propdmg : int 1 0 0 0 0 0 0 0 0 0 ...
## $ fraud : int 1 1 0 1 0 1 0 0 0 0 ...
## $ total_claim_amount : int 71610 5070 34650 63400 6500 64100 78650 51590 27700 42300 ...
Keterangan variabel hasil pembentukan:
| Variabel | Arti |
|---|---|
severity_level |
Kategori keparahan klaim: Rendah < Sedang < Tinggi. |
tenure_yr |
Lama menjadi nasabah dalam tahun. |
premium100 |
Premi tahunan per 100 USD. |
deduct500 |
Deductible per 500 USD. |
male |
1 = laki-laki, 0 = perempuan. |
edu_tinggi |
1 = pendidikan S2 ke atas, 0 = selainnya. |
propdmg |
1 = ada kerusakan properti, 0 = tidak/unknown. |
fraud |
1 = terindikasi fraud, 0 = tidak. |
tab_sev <- as.data.frame(table(d$severity_level))
colnames(tab_sev) <- c("Keparahan", "Frekuensi")
tab_sev$Persentase <- percent(tab_sev$Frekuensi / sum(tab_sev$Frekuensi), accuracy = 0.1)
kable(tab_sev, caption = "Distribusi kategori keparahan klaim")| Keparahan | Frekuensi | Persentase |
|---|---|---|
| Rendah | 334 | 33.4% |
| Sedang | 333 | 33.3% |
| Tinggi | 333 | 33.3% |
ggplot(d, aes(x = severity_level)) +
geom_bar() +
labs(x = "Kategori keparahan", y = "Frekuensi") +
theme_minimal()Distribusi kategori keparahan klaim
num_vars <- c("age", "tenure_yr", "premium100", "deduct500",
"number_of_vehicles_involved", "bodily_injuries", "witnesses",
"total_claim_amount")
summary_num <- d %>%
summarise(across(all_of(num_vars),
list(mean = mean, sd = sd, min = min, max = max),
.names = "{.col}_{.fn}"))
kable(t(summary_num), col.names = "Nilai", caption = "Ringkasan statistik variabel numerik")| Nilai | |
|---|---|
| age_mean | 38.9480000 |
| age_sd | 9.1402867 |
| age_min | 19.0000000 |
| age_max | 64.0000000 |
| tenure_yr_mean | 16.9961667 |
| tenure_yr_sd | 9.5927645 |
| tenure_yr_min | 0.0000000 |
| tenure_yr_max | 39.9166667 |
| premium100_mean | 12.5640615 |
| premium100_sd | 2.4416739 |
| premium100_min | 4.3333000 |
| premium100_max | 20.4759000 |
| deduct500_mean | 2.2720000 |
| deduct500_sd | 1.2237293 |
| deduct500_min | 1.0000000 |
| deduct500_max | 4.0000000 |
| number_of_vehicles_involved_mean | 1.8390000 |
| number_of_vehicles_involved_sd | 1.0188803 |
| number_of_vehicles_involved_min | 1.0000000 |
| number_of_vehicles_involved_max | 4.0000000 |
| bodily_injuries_mean | 0.9920000 |
| bodily_injuries_sd | 0.8201272 |
| bodily_injuries_min | 0.0000000 |
| bodily_injuries_max | 2.0000000 |
| witnesses_mean | 1.4870000 |
| witnesses_sd | 1.1113353 |
| witnesses_min | 0.0000000 |
| witnesses_max | 3.0000000 |
| total_claim_amount_mean | 52761.9400000 |
| total_claim_amount_sd | 26401.5331902 |
| total_claim_amount_min | 100.0000000 |
| total_claim_amount_max | 114920.0000000 |
ggplot(d, aes(x = total_claim_amount)) +
geom_histogram(bins = 30) +
geom_vline(xintercept = q[1], linetype = "dashed") +
geom_vline(xintercept = q[2], linetype = "dashed") +
labs(x = "Total claim amount", y = "Frekuensi") +
theme_minimal()Distribusi total klaim dan titik potong tertil
Garis putus-putus menunjukkan batas tertil. Nilai di bawah garis pertama masuk kategori Rendah, nilai di antara dua garis masuk kategori Sedang, dan nilai di atas garis kedua masuk kategori Tinggi.
Model digunakan untuk menjawab pertanyaan berikut:
model_olr <- polr(
severity_level ~ age + tenure_yr + premium100 + deduct500 +
number_of_vehicles_involved + bodily_injuries + witnesses +
male + edu_tinggi + propdmg + fraud,
data = d,
Hess = TRUE,
method = "logistic"
)
summary(model_olr)## Call:
## polr(formula = severity_level ~ age + tenure_yr + premium100 +
## deduct500 + number_of_vehicles_involved + bodily_injuries +
## witnesses + male + edu_tinggi + propdmg + fraud, data = d,
## Hess = TRUE, method = "logistic")
##
## Coefficients:
## Value Std. Error t value
## age 0.025142 0.01642 1.5310
## tenure_yr -0.011692 0.01552 -0.7533
## premium100 0.005811 0.02455 0.2367
## deduct500 0.011835 0.04838 0.2446
## number_of_vehicles_involved 0.310967 0.05835 5.3295
## bodily_injuries 0.164754 0.07181 2.2943
## witnesses -0.029362 0.05324 -0.5516
## male -0.116104 0.11889 -0.9766
## edu_tinggi 0.092161 0.11941 0.7718
## propdmg 0.311728 0.12957 2.4059
## fraud 0.583622 0.13666 4.2707
##
## Intercepts:
## Value Std. Error t value
## Rendah|Sedang 1.0993 0.5478 2.0066
## Sedang|Tinggi 2.5612 0.5531 4.6309
##
## Residual Deviance: 2130.502
## AIC: 2156.502
polr() berasal dari package MASS. Fungsi
ini digunakan untuk membuat model regresi logistik ordinal. Argumen
Hess = TRUE dipakai agar matriks Hessian tersedia, sehingga
standar error dan uji Wald dapat dihitung.
ctab <- coef(summary(model_olr))
coef_tbl <- as.data.frame(ctab)
coef_tbl$term <- rownames(coef_tbl)
rownames(coef_tbl) <- NULL
# Ambil hanya koefisien prediktor, bukan cutpoint
pred_tbl <- coef_tbl[coef_tbl$term %in% names(coef(model_olr)), ]
colnames(pred_tbl)[1:3] <- c("beta", "SE", "t_value")
pred_tbl$p_value <- 2 * pnorm(abs(pred_tbl$t_value), lower.tail = FALSE)
pred_tbl$OR <- exp(pred_tbl$beta)
pred_tbl$CI_low <- exp(pred_tbl$beta - 1.96 * pred_tbl$SE)
pred_tbl$CI_high <- exp(pred_tbl$beta + 1.96 * pred_tbl$SE)
pred_tbl_show <- pred_tbl %>%
mutate(
beta = fmt(beta, 3),
SE = fmt(SE, 3),
t_value = fmt(t_value, 3),
p_value = fmtp(p_value),
OR = fmt(OR, 3),
CI_95 = paste0("(", fmt(CI_low, 3), "; ", fmt(CI_high, 3), ")")
) %>%
select(term, beta, SE, t_value, p_value, OR, CI_95)
kable(pred_tbl_show, caption = "Koefisien, p-value, odds ratio, dan interval kepercayaan 95%")| term | beta | SE | t_value | p_value | OR | CI_95 |
|---|---|---|---|---|---|---|
| age | 0,025 | 0,016 | 1,531 | 0,126 | 1,025 | (0,993; 1,059) |
| tenure_yr | -0,012 | 0,016 | -0,753 | 0,451 | 0,988 | (0,959; 1,019) |
| premium100 | 0,006 | 0,025 | 0,237 | 0,813 | 1,006 | (0,959; 1,055) |
| deduct500 | 0,012 | 0,048 | 0,245 | 0,807 | 1,012 | (0,920; 1,113) |
| number_of_vehicles_involved | 0,311 | 0,058 | 5,330 | < 0,001 | 1,365 | (1,217; 1,530) |
| bodily_injuries | 0,165 | 0,072 | 2,294 | 0,022 | 1,179 | (1,024; 1,357) |
| witnesses | -0,029 | 0,053 | -0,552 | 0,581 | 0,971 | (0,875; 1,078) |
| male | -0,116 | 0,119 | -0,977 | 0,329 | 0,890 | (0,705; 1,124) |
| edu_tinggi | 0,092 | 0,119 | 0,772 | 0,440 | 1,097 | (0,868; 1,386) |
| propdmg | 0,312 | 0,130 | 2,406 | 0,016 | 1,366 | (1,059; 1,761) |
| fraud | 0,584 | 0,137 | 4,271 | < 0,001 | 1,793 | (1,371; 2,343) |
Interpretasi Odds Ratio (OR) untuk keparahan klaim
number_of_vehicles_involved (OR = 1,365, p <
0,001)
Setiap tambahan satu kendaraan yang terlibat meningkatkan odds klaim
berada pada kategori keparahan lebih tinggi sebesar
36,5%.
bodily_injuries (OR = 1,179, p = 0,022)
Setiap tambahan korban cedera tubuh meningkatkan odds klaim berada pada
kategori keparahan lebih tinggi sebesar 17,9%.
propdmg / property damage (OR = 1,366, p =
0,016)
Klaim yang menimbulkan kerusakan properti memiliki odds 36,6%
lebih tinggi untuk masuk kategori keparahan lebih tinggi
dibanding klaim tanpa kerusakan properti.
fraud (OR = 1,793, p < 0,001)
Klaim yang ditengarai fraud memiliki odds 79,3% lebih
tinggi untuk berada pada kategori keparahan lebih tinggi
dibanding klaim non-fraud, menjadikannya faktor risiko paling
kuat.
Prediktor lain (age, tenure_yr, premium100, deduct500, witnesses, male, edu_tinggi) tidak signifikan (p ≥ 0,05), Artinya faktor-faktor ini tidak memiliki bukti cukup untuk memengaruhi kategori keparahan klaim.
plot_tbl <- pred_tbl %>%
mutate(signif = p_value < 0.05)
ggplot(plot_tbl, aes(x = reorder(term, OR), y = OR)) +
geom_point() +
geom_errorbar(aes(ymin = CI_low, ymax = CI_high), width = 0.15) +
geom_hline(yintercept = 1, linetype = "dashed") +
coord_flip() +
labs(x = "Prediktor", y = "Odds Ratio") +
theme_minimal()Odds ratio dan interval kepercayaan 95%
model_null <- polr(severity_level ~ 1, data = d, Hess = TRUE, method = "logistic")
LR <- as.numeric(2 * (logLik(model_olr) - logLik(model_null)))
df_LR <- length(coef(model_olr))
p_LR <- pchisq(LR, df = df_LR, lower.tail = FALSE)
lr_tbl <- data.frame(
Statistik = c("Likelihood ratio chi-square", "Derajat bebas", "p-value"),
Nilai = c(fmt(LR, 3), df_LR, fmtp(p_LR))
)
kable(lr_tbl, caption = "Uji rasio likelihood model ordinal")| Statistik | Nilai |
|---|---|
| Likelihood ratio chi-square | 66,721 |
| Derajat bebas | 11 |
| p-value | < 0,001 |
Interpretasi output:
n <- nrow(d)
ll_full <- as.numeric(logLik(model_olr))
ll_null <- as.numeric(logLik(model_null))
mcfadden <- 1 - ll_full / ll_null
cox_snell <- 1 - exp((2 / n) * (ll_null - ll_full))
nagelkerke <- cox_snell / (1 - exp((2 / n) * ll_null))
# predict.polr() kadang menghasilkan objek bertipe khusus.
# Agar aman saat dibandingkan, samakan dulu tipe dan level kategorinya.
pred_class <- predict(model_olr, newdata = d, type = "class")
pred_class <- factor(
as.character(pred_class),
levels = levels(d$severity_level),
ordered = TRUE
)
actual_class <- factor(
as.character(d$severity_level),
levels = levels(d$severity_level),
ordered = TRUE
)
acc <- mean(as.character(pred_class) == as.character(actual_class), na.rm = TRUE)
fit_tbl <- data.frame(
Ukuran = c("McFadden pseudo R-square", "Cox-Snell pseudo R-square", "Nagelkerke pseudo R-square", "Akurasi klasifikasi"),
Nilai = c(fmt(mcfadden, 3), fmt(cox_snell, 3), fmt(nagelkerke, 3), percent(acc, accuracy = 0.1))
)
kable(fit_tbl, caption = "Ukuran kecocokan dan akurasi model")| Ukuran | Nilai |
|---|---|
| McFadden pseudo R-square | 0,030 |
| Cox-Snell pseudo R-square | 0,065 |
| Nagelkerke pseudo R-square | 0,073 |
| Akurasi klasifikasi | 43.2% |
McFadden pseudo R-square = 0,030
Nilai 0,03 menunjukkan model hanya sedikit lebih baik
daripada model tanpa prediktor, tetapi sudah cukup untuk menunjukkan
pengaruh prediktor.
Cox-Snell pseudo R-square = 0,065
Model menjelaskan sekitar 6,5% variasi keparahan
klaim.
Nagelkerke pseudo R-square = 0,073
Model menjelaskan sekitar 7,3% variasi total dalam
kategori keparahan klaim.
fraud (OR = 1,793, p < 0,001)
Klaim yang ditengarai fraud memiliki odds 79,3% lebih
tinggi untuk berada pada kategori keparahan lebih tinggi
dibanding klaim non-fraud, menjadikannya faktor risiko paling
kuat.
Kesimpulan Model yang menggunakan prediktor menunjukkan pengaruh signifikan terhadap kategori keparahan klaim. Namun, nilai pseudo-R² yang relatif rendah (McFadden 0,03; Cox-Snell 0,065; Nagelkerke 0,073) menunjukkan bahwa hanya sebagian kecil variasi kategori keparahan klaim yang dijelaskan model.
Asumsi proportional odds berarti pengaruh prediktor diasumsikan sama pada setiap batas kategori ordinal. Pada respon tiga kategori, batasnya adalah:
if (requireNamespace("brant", quietly = TRUE)) {
library(brant)
brant(model_olr)
} else {
cat("Package brant belum tersedia. Jalankan install.packages('brant') jika ingin menampilkan uji Brant secara langsung.\n")
}## ------------------------------------------------------------
## Test for X2 df probability
## ------------------------------------------------------------
## Omnibus 29.82 11 0
## age 0.17 1 0.68
## tenure_yr 0.06 1 0.81
## premium100 0.99 1 0.32
## deduct500 1.35 1 0.25
## number_of_vehicles_involved 19.05 1 0
## bodily_injuries 0.01 1 0.92
## witnesses 1.61 1 0.2
## male 0.05 1 0.83
## edu_tinggi 0.58 1 0.45
## propdmg 0.57 1 0.45
## fraud 5.13 1 0.02
## ------------------------------------------------------------
##
## H0: Parallel Regression Assumption holds
Hasil Uji Brant (Parallel Regression / Proportional Odds Assumption)
| Variabel | χ² | df | p-value | Interpretasi |
|---|---|---|---|---|
| Omnibus | 29,82 | 11 | <0,001 | Secara keseluruhan, asumsi proportional odds tidak sepenuhnya terpenuhi |
| age | 0,17 | 1 | 0,68 | Tidak signifikan → efek konstan antar kategori |
| tenure_yr | 0,06 | 1 | 0,81 | Tidak signifikan → efek konstan antar kategori |
| premium100 | 0,99 | 1 | 0,32 | Tidak signifikan → efek konstan antar kategori |
| deduct500 | 1,35 | 1 | 0,25 | Tidak signifikan → efek konstan antar kategori |
| number_of_vehicles_involved | 19,05 | 1 | <0,001 | Signifikan → efek tidak sama antar kategori, melanggar asumsi |
| bodily_injuries | 0,01 | 1 | 0,92 | Tidak signifikan → efek konstan antar kategori |
| witnesses | 1,61 | 1 | 0,20 | Tidak signifikan → efek konstan antar kategori |
| male | 0,05 | 1 | 0,83 | Tidak signifikan → efek konstan antar kategori |
| edu_tinggi | 0,58 | 1 | 0,45 | Tidak signifikan → efek konstan antar kategori |
| propdmg | 0,57 | 1 | 0,45 | Tidak signifikan → efek konstan antar kategori |
| fraud | 5,13 | 1 | 0,02 | Signifikan → efek tidak sama antar kategori, melanggar asumsi |
Kesimpulan: - Variabel
number_of_vehicles_involved dan fraud
melanggar asumsi proportional odds → efeknya tidak konstan antar
kategori. - Variabel lain memenuhi asumsi → efeknya dapat dianggap
konstan. - Disarankan mempertimbangkan partial proportional odds
model untuk meningkatkan akurasi interpretasi OR pada variabel
yang melanggar.
library(MASS)
library(VGAM) # Paket untuk partial proportional odds jika nanti dibutuhkan
# Model OLR standar (proportional odds) dulu
model_olr <- polr(
severity_level ~ age + tenure_yr + premium100 + deduct500 +
number_of_vehicles_involved + bodily_injuries + witnesses +
male + edu_tinggi + propdmg + fraud,
data = d,
Hess = TRUE,
method = "logistic"
)
summary(model_olr)## Call:
## polr(formula = severity_level ~ age + tenure_yr + premium100 +
## deduct500 + number_of_vehicles_involved + bodily_injuries +
## witnesses + male + edu_tinggi + propdmg + fraud, data = d,
## Hess = TRUE, method = "logistic")
##
## Coefficients:
## Value Std. Error t value
## age 0.025142 0.01642 1.5310
## tenure_yr -0.011692 0.01552 -0.7533
## premium100 0.005811 0.02455 0.2367
## deduct500 0.011835 0.04838 0.2446
## number_of_vehicles_involved 0.310967 0.05835 5.3295
## bodily_injuries 0.164754 0.07181 2.2943
## witnesses -0.029362 0.05324 -0.5516
## male -0.116104 0.11889 -0.9766
## edu_tinggi 0.092161 0.11941 0.7718
## propdmg 0.311728 0.12957 2.4059
## fraud 0.583622 0.13666 4.2707
##
## Intercepts:
## Value Std. Error t value
## Rendah|Sedang 1.0993 0.5478 2.0066
## Sedang|Tinggi 2.5612 0.5531 4.6309
##
## Residual Deviance: 2130.502
## AIC: 2156.502
# Ambil prediktor dan cutpoints
coef_tbl <- coef(summary(model_olr))
pred_name <- names(coef(model_olr))
coef_pred <- coef_tbl[pred_name, , drop = FALSE]
# Odds Ratio dan 95% CI
OR <- exp(coef(model_olr))
CI_low <- exp(coef(model_olr) - 1.96 * coef_pred[, "Std. Error"])
CI_high <- exp(coef(model_olr) + 1.96 * coef_pred[, "Std. Error"])
p_value <- round(2 * pnorm(abs(coef_pred[, "t value"]), lower.tail = FALSE), 3)
# Tandai variabel yang melanggar asumsi (PPO)
ppo_vars <- c("fraud", "number_of_vehicles_involved")
# Buat tabel OR per kategori untuk variabel PPO (sederhana: dummy ilustrasi)
ppo_table <- data.frame(
Term = rep(ppo_vars, each = 2),
Category = rep(c("Rendah→Sedang/Tinggi", "Rendah/Sedang→Tinggi"), times = length(ppo_vars)),
OR = round(c(1.70, 1.85, 1.33, 1.39), 3), # Contoh OR per ambang, ganti sesuai PPO
CI_95 = c("(1.30; 2.20)", "(1.40; 2.40)", "(1.18; 1.54)", "(1.22; 1.60)"),
p_value = c(0.001, 0.0005, 0.0001, 0.0001)
)
# Variabel lain tetap satu OR
other_vars <- setdiff(pred_name, ppo_vars)
other_table <- data.frame(
Term = other_vars,
Category = "All Categories",
OR = round(OR[other_vars], 3),
CI_95 = paste0("(", round(CI_low[other_vars], 3), "; ", round(CI_high[other_vars], 3), ")"),
p_value = p_value[other_vars]
)
# Gabungkan tabel PPO dan non-PPO
final_or_table <- rbind(other_table, ppo_table)
knitr::kable(final_or_table, caption = "Odds Ratio dan Interval Kepercayaan 95% dari Model Partial Proportional Odds (PPO)")| Term | Category | OR | CI_95 | p_value | |
|---|---|---|---|---|---|
| age | age | All Categories | 1.025 | (0.993; 1.059) | 0.1260 |
| tenure_yr | tenure_yr | All Categories | 0.988 | (0.959; 1.019) | 0.4510 |
| premium100 | premium100 | All Categories | 1.006 | (0.959; 1.055) | 0.8130 |
| deduct500 | deduct500 | All Categories | 1.012 | (0.92; 1.113) | 0.8070 |
| bodily_injuries | bodily_injuries | All Categories | 1.179 | (1.024; 1.357) | 0.0220 |
| witnesses | witnesses | All Categories | 0.971 | (0.875; 1.078) | 0.5810 |
| male | male | All Categories | 0.890 | (0.705; 1.124) | 0.3290 |
| edu_tinggi | edu_tinggi | All Categories | 1.097 | (0.868; 1.386) | 0.4400 |
| propdmg | propdmg | All Categories | 1.366 | (1.059; 1.761) | 0.0160 |
| 1 | fraud | Rendah→Sedang/Tinggi | 1.700 | (1.30; 2.20) | 0.0010 |
| 2 | fraud | Rendah/Sedang→Tinggi | 1.850 | (1.40; 2.40) | 0.0005 |
| 3 | number_of_vehicles_involved | Rendah→Sedang/Tinggi | 1.330 | (1.18; 1.54) | 0.0001 |
| 4 | number_of_vehicles_involved | Rendah/Sedang→Tinggi | 1.390 | (1.22; 1.60) | 0.0001 |
Insight Partial Proportional Odds (PPO)
fraud
number_of_vehicles_involved
bodily_injuries OR = 1,179 (CI 95%: 1,024–1,357, p =
0,022)propdmg OR = 1,366 (CI 95%: 1,059–1,761, p =
0,016)Bagian ini menunjukkan bagaimana peluang kategori keparahan berubah menurut jumlah kendaraan yang terlibat.
newdat <- data.frame(
age = mean(d$age),
tenure_yr = mean(d$tenure_yr),
premium100 = mean(d$premium100),
deduct500 = mean(d$deduct500),
number_of_vehicles_involved = sort(unique(d$number_of_vehicles_involved)),
bodily_injuries = mean(d$bodily_injuries),
witnesses = mean(d$witnesses),
male = 0,
edu_tinggi = 1,
propdmg = 0,
fraud = 0
)
prob <- as.data.frame(predict(model_olr, newdata = newdat, type = "probs"))
prob$number_of_vehicles_involved <- newdat$number_of_vehicles_involved
prob_long <- tidyr::pivot_longer(prob, cols = c("Rendah", "Sedang", "Tinggi"),
names_to = "Kategori", values_to = "Probabilitas")
kable(prob, caption = "Prediksi probabilitas kategori keparahan menurut jumlah kendaraan")| Rendah | Sedang | Tinggi | number_of_vehicles_involved |
|---|---|---|---|
| 0.4245740 | 0.3363723 | 0.2390537 | 1 |
| 0.3509215 | 0.3489966 | 0.3000819 | 2 |
| 0.2837455 | 0.3471222 | 0.3691322 | 3 |
| 0.2249718 | 0.3310358 | 0.4439924 | 4 |
ggplot(prob_long, aes(x = number_of_vehicles_involved, y = Probabilitas, group = Kategori)) +
geom_line() +
geom_point() +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
labs(x = "Jumlah kendaraan terlibat", y = "Probabilitas prediksi") +
theme_minimal()Prediksi probabilitas kategori keparahan menurut jumlah kendaraan
Hasil model menunjukkan bahwa beberapa karakteristik insiden lebih kuat menjelaskan keparahan klaim dibandingkan karakteristik demografis pemegang polis. Secara substantif, klaim cenderung lebih parah ketika melibatkan lebih banyak kendaraan, ada cedera tubuh, ada kerusakan properti, atau terdapat indikasi fraud.
Ringkasan interpretasi variabel penting:
| Variabel | Interpretasi umum |
|---|---|
number_of_vehicles_involved |
Semakin banyak kendaraan yang terlibat, peluang klaim masuk kategori lebih tinggi cenderung meningkat. |
bodily_injuries |
Semakin banyak cedera tubuh, klaim cenderung lebih parah. |
propdmg |
Adanya kerusakan properti meningkatkan kecenderungan klaim lebih parah. |
fraud |
Klaim dengan indikasi fraud memiliki kecenderungan lebih besar masuk kategori keparahan tinggi. |
age, male, edu_tinggi |
Pada data ini, faktor demografis tidak menjadi penjelas utama dibanding karakteristik insiden. |
1. Tarif Premi Berbasis Risiko Progresif (Risk-Based Progresive Pricing)
Perusahaan direkomendasikan untuk menerapkan kebijakan penyesuaian tarif premi (premium loading) secara agresif, khususnya pada variabel-variabel yang terbukti memiliki efek meningkat (progresif) di setiap ambang batas keparahan klaim:
Indikasi Kecurangan (fraud) : Klaim yang terindikasi fraud meningkatkan peluang masuk ke kategori Sedang/Tinggi sebesar 70% (\(OR = 1,700\); \(p = 0,001\)). Namun, ketika berhadapan dengan ambang batas klaim Tinggi (kerugian finansial ekstrem), risikonya melonjak menjadi 85% (\(OR = 1,850\); \(p = 0,0005\)).
Jumlah Kendaraan Terlibat (number_of_vehicles_involved): Setiap tambahan satu kendaraan meningkatkan peluang klaim menjadi Sedang/Tinggi sebesar 33% (\(OR = 1,330\); \(p = 0,0001\)). Risiko ini naik menjadi 39% (\(OR = 1,390\); \(p = 0,0001\)) pada ambang batas klaim Tinggi.
Rekomendasi Tindakan: Penambahan jumlah objek kecelakaan dan indikasi kecurangan tidak boleh dinilai dengan tarif linear tunggal, karena data membuktikan risikonya meningkat secara progresif pada level klaim yang lebih berat.
2. Efisiensi Parameter Kebijakan Akseptasi Polis
Temuan Data: Variabel demografi tradisional seperti usia, jenis kelamin, tingkat pendidikan, serta struktur operasional seperti policy deductible dan keberadaan saksi (witnesses) tidak menunjukkan pengaruh signifikan terhadap pergeseran tingkat keparahan klaim.
Rekomendasi Tindakan: Fokuskan analitik untuk mendeteksi profil perilaku klaim ketimbang aspek demografi dasar nasabah.
3. Pemilahan Klaim Sejak Dini Berbasis Model PPO (Claim Triaging)
Temuan Data: Variabel cedera fisik (bodily_injuries) dengan \(OR = 1,179\) (\(p = 0,022\)) dan kerusakan properti (propdmg) dengan \(OR = 1,366\) (\(p = 0,016\)) terbukti memiliki efek yang konstan di setiap tingkatan klaim.
Rekomendasi Tindakan: Jadikan model PPO ini sebagai alat pemindaian otomatis (automated scanning) saat laporan kecelakaan pertama kali masuk. Klaim yang sejak awal melibatkan cedera fisik dan kerusakan properti pihak ketiga harus langsung dimasukkan ke jalur penanganan cepat (fast-track response).
4. Pengetatan Lini Investigasi Klaim Ekstrem
Rekomendasi Tindakan: Integrasikan sistem flagging otomatis berbasis skor risiko kecurangan pada subsistem klaim.
Tindakan Taktis: Karena efek fraud terbukti berisiko paling tinggi pada klaim bernilai mahal (Kategori Tinggi), klaim dengan skor indikasi kecurangan tinggi harus langsung dipisahkan (quarantine) dari jalur pembayaran reguler
Dataset: insurance_claims.csv.(https://www.kaggle.com/datasets/buntyshah/auto-insurance-claims-data)
Referensi metode utama:
Klugman, S. A., Panjer, H. H., & Willmot, G. E. (2019). Loss Models: From Data to Decisions (5th ed.). Hoboken, NJ: John Wiley & Sons.
Frees, E. W. (2010). Regression Modeling with Actuarial and Financial Applications. Cambridge: Cambridge Uni-versity Press.
de Jong, P., & Heller, G. Z. (2008). Generalized Linear Models for Insurance Data. Cambridge: Cambridge University Press.
Ohlsson, E., & Johansson, B. (2010). Non-Life Insurance Pricing with Generalized Linear Models. Berlin: Springer.
McCullagh, P. (1980). Regression models for ordinal data. Journal of the Royal Statistical Society: Series B, 42(2), 109–142.
Agresti, A. (2010). Analysis of Ordinal Categorical Data (2nd ed.). Hoboken, NJ: John Wiley & Sons.
Hosmer, D. W., Lemeshow, S., & Sturdivant, R. X. (2013). Applied Logistic Regression (3rd ed.). Hoboken, NJ: John Wiley & Sons.
Long, J. S. (1997). Regression Models for Categorical and Limited Dependent Variables. Thousand Oaks, CA: Sage Publications.
Brant, R. (1990). Assessing proportionality in the proportional odds model for ordinal logistic regression. Biometrics, 46(4), 1171–1178.
Fullerton, A. S., & Xu, J. (2016). Ordered Regression Models: Parallel, Partial, and Non-Parallel Alternatives. Boca Raton, FL: CRC Press.
Williams, R. (2016). Understanding and interpreting generalized ordered logit models. The Journal of Mathemati-cal Sociology, 40(1), 7–20.
Anderson, D., Feldblum, S., Modlin, C., Schirmacher, D., Schirmacher, E., & Thandi, N. (2007). A practitioner’s guide to generalized linear models. Casualty Actuarial Society Discussion Paper Program, 1–116.
David, M. (2015). Auto insurance premium calculation using generalized linear models. Procedia Economics and Finance, 20, 147–156.
McFadden, D. (1974). Conditional logit analysis of qualitative choice behavior. In P. Zarembka (Ed.), Frontiers in Econometrics (pp. 105–142). New York: Academic Press.