Medical Insurance Charges

Smoking, Age and BMI - MATH1324 Assignment 2

Syed Nawaz - S4239096

Last updated: 13 October, 2026

Introduction

Introduction Cont.

Problem Statement

Data

# insurance dataset
insurance_csv_dt <- read.csv("insurance.csv", stringsAsFactors = TRUE)
dim(insurance_csv_dt)
## [1] 1338    7

Data Cont.

Descriptive Statistics and Visualisation

# missing values
colSums(is.na(insurance_csv_dt))
##      age      sex      bmi children   smoker   region  charges 
##        0        0        0        0        0        0        0
# duplicate rows
duplicate_rows <- sum(duplicated(insurance_csv_dt))
# remove duplicates
insurance_csv_dt <- distinct(insurance_csv_dt)
# non-smoker
insurance_csv_dt$smoker <- relevel(insurance_csv_dt$smoker, ref = "no")  
# dataset summary
summary(insurance_csv_dt[, c("age", "bmi", "children", "charges")])
##       age             bmi           children        charges     
##  Min.   :18.00   Min.   :15.96   Min.   :0.000   Min.   : 1122  
##  1st Qu.:27.00   1st Qu.:26.29   1st Qu.:0.000   1st Qu.: 4746  
##  Median :39.00   Median :30.40   Median :1.000   Median : 9386  
##  Mean   :39.22   Mean   :30.66   Mean   :1.096   Mean   :13279  
##  3rd Qu.:51.00   3rd Qu.:34.70   3rd Qu.:2.000   3rd Qu.:16658  
##  Max.   :64.00   Max.   :53.13   Max.   :5.000   Max.   :63770
# Medical charges basedon smoker
ggplot(insurance_csv_dt, aes(x = charges)) +
  geom_histogram(bins = 30, fill = "steelblue", colour = "white") +
  facet_wrap(~ smoker, labeller = label_both, scales = "free_y") +
  labs(x = "Medical charges", y = "Count") +
  theme_minimal()

ggplot(insurance_csv_dt, aes(x = smoker, y = charges, fill = smoker)) +
  geom_boxplot(show.legend = FALSE) +
  labs(x = "Smoker", y = "Medical charges") +
  theme_minimal()

# charges based on ages
ggplot(insurance_csv_dt, aes(age, charges, colour = smoker)) +
  geom_point(alpha = .5) + theme_minimal() + labs(x = "Age", y = "Charges")

# charges based on BMI
ggplot(insurance_csv_dt, aes(bmi, charges, colour = smoker)) +
  geom_point(alpha = .5) + theme_minimal() + labs(x = "BMI", y = "Charges")

Decsriptive Statistics Cont.

# descriptive statistics
insurance_csv_dt %>%
  group_by(smoker) %>%
  summarise(n = n(),Min = min(charges),Q1 = quantile(charges, .25),
            Median = median(charges),
            Q3 = quantile(charges, .75),
            Max = max(charges),
            Mean = mean(charges),
            SD = sd(charges)) -> table1
kable(table1, digits = 0)
smoker n Min Q1 Median Q3 Max Mean SD
no 1063 1122 3989 7346 11363 36911 8441 5993
yes 274 12829 20826 34456 41019 63770 32050 11542
# mean charges based on regions
insurance_csv_dt %>%
  group_by(region, smoker) %>%
  summarise(mean_charges = mean(charges), .groups = "drop") %>%
  ggplot(aes(x = region, y = mean_charges, fill = smoker)) +
  geom_col(position = "dodge") +
  labs(x = "Region", y = "Mean charges") +
  theme_minimal()

# charges by BMI category and smoking status
insurance_csv_dt %>%
  mutate(bmi_group = cut(bmi, breaks = c(0, 18.5, 25, 30, Inf),
                         labels = c("Underweight", "Normal", "Overweight", "Obese"),
                         right = FALSE)) %>%
  ggplot(aes(x = bmi_group, y = charges, fill = smoker)) +
  geom_boxplot() +
  labs(x = "BMI category", y = "Charges") +
  theme_minimal()

Hypothesis Testing

par(mfrow = c(1, 2))
for (g in c("no", "yes")) {
  x <- insurance_csv_dt$charges[insurance_csv_dt$smoker == g]
  qqnorm(x, main = paste("Smoker:", g)); qqline(x)
}

par(mfrow = c(1, 1))
smk <- insurance_csv_dt$charges[insurance_csv_dt$smoker == "yes"]
non_smk <- insurance_csv_dt$charges[insurance_csv_dt$smoker == "no"]
tt_results <- t.test(smk, non_smk, alternative = "two.sided", conf.level = 0.95)
tt_results
## 
##  Welch Two Sample t-test
## 
## data:  smk and non_smk
## t = 32.742, df = 311.88, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  22190.79 25028.35
## sample estimates:
## mean of x mean of y 
##  32050.23   8440.66
# robustness check on log scale
t.test(log(charges) ~ smoker, data = insurance_csv_dt)$p.value
## [1] 6.784987e-234

Regression Model

lm_mdl <- lm(log(charges) ~ age + bmi + smoker, data = insurance_csv_dt)
summary(lm_mdl)
## 
## Call:
## lm(formula = log(charges) ~ age + bmi + smoker, data = insurance_csv_dt)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.22166 -0.21732 -0.03983  0.10344  2.07125 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 7.0761244  0.0715858   98.85  < 2e-16 ***
## age         0.0350930  0.0009117   38.49  < 2e-16 ***
## bmi         0.0107853  0.0020982    5.14 3.15e-07 ***
## smokeryes   1.5451927  0.0315190   49.02  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.465 on 1333 degrees of freedom
## Multiple R-squared:  0.7443, Adjusted R-squared:  0.7438 
## F-statistic:  1294 on 3 and 1333 DF,  p-value: < 2.2e-16
# Model Diagnostics
par(mfrow = c(2, 2)); plot(lm_mdl); par(mfrow = c(1, 1))

# percentage chnage in charges per unit increase
round((exp(coef(lm_mdl)[-1]) - 1) * 100, 1)
##       age       bmi smokeryes 
##       3.6       1.1     368.9

Discussion

References