The goal of this R markdown is to predict insurance costs using medical cost personal datasets provided by https://github.com/stedy/Machine-Learning-with-R-datasets/blob/master/insurance.csv.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.2 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ ggplot2 3.4.2 ✔ tibble 3.2.1
## ✔ lubridate 1.9.2 ✔ tidyr 1.3.0
## ✔ purrr 1.0.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(AnalysisLin)
data<-read.csv("/Users/zhiweilin/Downloads/insurance.csv", header =T, na.string=c("","NA"))
desc_stat(data) # desc_stat() is a function from my personal EDA package AnalysisLin. The function is used to generate some key statistics for the dataset.
## Descriptive Statistics Results:
## =========================================================================
## age sex bmi children smoker region
## count 1338 1338 1338 1338 1338 1338
## unique 47 2 548 6 2 4
## duplicate 1 1 1 1 1 1
## null 0 0 0 0 0 0
## null_rate 0 0 0 0 0 0
## type integer character numeric integer character character
## min 18 <NA> 15.96 0 <NA> <NA>
## p25 27 <NA> 26.29625 0 <NA> <NA>
## mean 39.207 <NA> 30.6634 1.0949 <NA> <NA>
## median 39 <NA> 30.4 1 <NA> <NA>
## p75 51 <NA> 34.69375 2 <NA> <NA>
## max 64 <NA> 53.13 5 <NA> <NA>
## sd 14.05 <NA> 6.0982 1.2055 <NA> <NA>
## kurtosis 1.7551 <NA> 2.945 3.1972 <NA> <NA>
## skewness 0.0556 <NA> 0.2837 0.9373 <NA> <NA>
## jarque_pvalue 0 <NA> 0 0 <NA> <NA>
## charges
## count 1338
## unique 1337
## duplicate 1
## null 0
## null_rate 0
## type numeric
## min 1121.8739
## p25 4740.28715
## mean 13270.4223
## median 9382.033
## p75 16639.912515
## max 63770.42801
## sd 12110.0112
## kurtosis 4.5958
## skewness 1.5142
## jarque_pvalue 0
Some data Insights:
data<-distinct(data) # Remove duplicate rows based on all columns
data <- mutate_at(data, vars(sex,smoker,region), as.factor)
The variables ‘sex’, ‘smoker’, and ‘region’ are converted from character variables to factor variables.
numeric_plot(data,prob=T,dens=T) # numeric_plot() is a function from my personal EDA package AnalysisLin. The function is used to generate histogram/line plot for all numerical variables.
## list()
Some takeaways:
categoric_plot(data,n_col = 2,bar_legend = F) # categorical_plot() is a function from my personal EDA package AnalysisLin. The function is used to generate pie/bar plot for all categrical(level) variables.
Some takeaways:
numeric_columns <- sapply(data, is.numeric)
numeric_data <- data[, numeric_columns]
corr_matrix(numeric_data,corrplot=T) #corr_matrix() is a function from my personal EDA package, AnalysisLin. The function is used to generated a table of correlation between variables and a plot of correlations.
## Loading required package: Hmisc
##
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:dplyr':
##
## src, summarize
## The following objects are masked from 'package:base':
##
## format.pval, units
## Loading required package: corrplot
## corrplot 0.92 loaded
## row column cor p
## 4 age charges 0.29830821 0.000000e+00
## 5 bmi charges 0.19840083 2.469136e-13
## 1 age bmi 0.10934361 6.164372e-05
## 6 children charges 0.06738935 1.371703e-02
## 2 age children 0.04153621 1.290128e-01
## 3 bmi children 0.01275466 6.412463e-01
For numerical variables only, age is most importnat variable in determining insurance permium, followed by bmi.
corr_cluster(numeric_data)
This is correlation clustering that tells the relationships between four numerical variables. As can be seen, variables age is closest to variable charges.
library(caret)
## Loading required package: lattice
##
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
##
## lift
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:dplyr':
##
## combine
## The following object is masked from 'package:ggplot2':
##
## margin
set.seed(123)
training.samples <- data$charges %>%
createDataPartition(p = 0.8, list = FALSE)
train.data <- data[training.samples, ]
test.data <- data[-training.samples, ]
split dataset into train and test data, with 80% train data and 20% test data.
set.seed(123)
rf_model <- train(
charges ~., data = train.data, method = "rf",
trControl = trainControl("cv", number = 10),
importance = TRUE
)
10-fold cross-validation is used
rf_model$bestTune #best set of tuning parameter.
## mtry
## 2 5
library(xgboost)
##
## Attaching package: 'xgboost'
## The following object is masked from 'package:dplyr':
##
## slice
xgb_model <- train(
charges ~ ., data = train.data, method = "xgbTree",
trControl = trainControl(method = "cv", number = 10),
verbosity = 0
)
xgb_model$bestTune #best set of tuning parameter.
## nrounds max_depth eta gamma colsample_bytree min_child_weight subsample
## 34 50 2 0.3 0 0.8 1 1
varImp(rf_model)
## rf variable importance
##
## Overall
## smokeryes 100.0000
## bmi 51.1292
## age 50.7188
## children 4.9639
## regionsoutheast 2.0844
## regionsouthwest 0.3861
## regionnorthwest 0.3150
## sexmale 0.0000
In random forest model, smokeryes is the most significant variable in determining insurance charges, followed by BMI and age. Other factors have minor to no impact on the charges
varImpPlot(rf_model$finalModel, type = 1)
rf_predictions <- rf_model %>% predict(test.data)
head(rf_predictions)
## 5 9 14 19 25 28
## 4877.633 7903.622 12120.191 13150.115 6576.011 12628.450
RMSE(rf_predictions, test.data$charges)
## [1] 4806.051
varImp(xgb_model)
## xgbTree variable importance
##
## Overall
## smokeryes 100.00000
## bmi 22.97129
## age 14.01767
## children 1.11484
## regionsouthwest 0.14746
## regionsoutheast 0.10335
## sexmale 0.02424
## regionnorthwest 0.00000
In XGBoost model, smokeryes is the most significant variable in determining insurance charges, followed by BMI and age. Other factors have minor to no impact on the charges
xgb_predictions <- xgb_model %>% predict(test.data)
head(xgb_predictions)
## [1] 5323.405 7166.240 12638.623 10981.429 7134.224 12472.825
RMSE(xgb_predictions, test.data$charges)
## [1] 4787.083
Both the Random Forest and XGBoost models yield the same result, such that “smoker,” “bmi,” and “age” are three crucial variables that determines insurance premiums. However, the XGBoost model outperforms the Random Forest model with a lower Root Mean Squared Error (RMSE). In general, if your dataset is small and you value simplicity, the Random Forest model is a solid choice. Conversely, for larger datasets where predictive accuracy is your goal, the XGBoost model is a better fit.