#install.packages('reader')
#install.packages("lattice")
#install.packages("reshape2")
#install.packages('ggplot2')
library(reader)
## Loading required package: NCmisc
##
## Attaching package: 'reader'
## The following objects are masked from 'package:NCmisc':
##
## cat.path, get.ext, rmv.ext
# Đọc file CSV trực tiếp từ đường link
diabetes <- read.csv("https://drive.google.com/uc?id=15mjrv0LV2T6GWNdAuW-QhXVdtMkQALlh")
# Hiển thị dữ liệu
head(diabetes)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 0 33.6
## 2 1 85 66 29 0 26.6
## 3 8 183 64 0 0 23.3
## 4 1 89 66 23 94 28.1
## 5 0 137 40 35 168 43.1
## 6 5 116 74 0 0 25.6
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 5 2.288 33 1
## 6 0.201 30 0
Bộ dữ liệu này chứa thông tin về 769 phụ nữ và bao gồm nhiều thuộc tính liên quan đến sức khỏe. Dưới đây là tổng quan ngắn gọn về các cột:
Mang thai: Số lần người phụ nữ đã mang thai.
Glucose: Nồng độ glucose trong huyết tương của người phụ nữ.
Huyết áp: Đo huyết áp.
Độ dày của da: Độ dày của nếp gấp da ở cơ tam đầu.
Insulin: Nồng độ insulin trong máu.
BMI (Chỉ số khối cơ thể): Thước đo lượng mỡ trong cơ thể dựa trên chiều cao và cân nặng.
Chức năng phả hệ bệnh tiểu đường: Một chức năng cho thấy khả năng mắc bệnh tiểu đường dựa trên tiền sử gia đình.
Tuổi: Tuổi của người phụ nữ.
Kết quả: Biến mục tiêu cho biết người phụ nữ có mắc bệnh tiểu đường hay không (1 đối với bệnh nhân tiểu đường, 0 đối với người không mắc bệnh tiểu đường).
str(diabetes)
## 'data.frame': 768 obs. of 9 variables:
## $ Pregnancies : int 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : int 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : int 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : int 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : int 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : int 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : int 1 0 1 0 1 0 1 0 1 1 ...
Ta có thể thấy:
Chỉ có 2 thuộc tính BMI và DiabetesPedigreeFunction là kiểu số thực, còn lại là int
Tổng có 768 dòng và 9 cột
summary(diabetes)
## Pregnancies Glucose BloodPressure SkinThickness
## Min. : 0.000 Min. : 0.0 Min. : 0.00 Min. : 0.00
## 1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 62.00 1st Qu.: 0.00
## Median : 3.000 Median :117.0 Median : 72.00 Median :23.00
## Mean : 3.845 Mean :120.9 Mean : 69.11 Mean :20.54
## 3rd Qu.: 6.000 3rd Qu.:140.2 3rd Qu.: 80.00 3rd Qu.:32.00
## Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00
## Insulin BMI DiabetesPedigreeFunction Age
## Min. : 0.0 Min. : 0.00 Min. :0.0780 Min. :21.00
## 1st Qu.: 0.0 1st Qu.:27.30 1st Qu.:0.2437 1st Qu.:24.00
## Median : 30.5 Median :32.00 Median :0.3725 Median :29.00
## Mean : 79.8 Mean :31.99 Mean :0.4719 Mean :33.24
## 3rd Qu.:127.2 3rd Qu.:36.60 3rd Qu.:0.6262 3rd Qu.:41.00
## Max. :846.0 Max. :67.10 Max. :2.4200 Max. :81.00
## Outcome
## Min. :0.000
## 1st Qu.:0.000
## Median :0.000
## Mean :0.349
## 3rd Qu.:1.000
## Max. :1.000
Ta có thể thấy:
Cột SkinThickness, Insulin có 1% điểm dữ liệu bằng 0 (vì Min=0)
Cột Outcome có 75% điểm dữ liệu < 1, chứng tỏ tỉ lệ mắc bệnh tiểu đường thấp
#install.packages("cowplot")
#install.packages("gridExtra")
library(ggplot2)
library(cowplot)
library(gridExtra)
par(mfrow=c(1,4))
# Boxplot cho Age
boxplot(diabetes$Age,
col="#ff0066",
main="Boxplot for Age")
# Boxplot cho Pregnancies
boxplot(diabetes$Pregnancies,
col="yellow",
main="Boxplot for Pregnancy")
# Boxplot cho Pregnancies
barplot(table(diabetes$Outcome), main = "Barplot of Out come", xlab = "Outcome", ylab="Count")
plot(density(diabetes$Glucose),
col="yellow",
main="Density Plot for Glucose",
xlab="Glucose",
ylab="Density")
polygon(density(diabetes$Glucose),
col="#ccff66")
box(which = "outer", lty = "solid")
Ta có thể thấy:
Từ boxplot về độ tuổi, cho thấy rằng phụ nữ trong tập dữ liệu tập trung chủ yếu ở khoảng 25-41 tuổi
Từ boxplot về số lần mang thai, cho thấy rằng phụ nữ chủ yếu có số lần mang thai từ 1-6 lần
Từ boxplot giữa nồng độ glucose và kết quả, những người không bệnh sẽ có nồng độ glucose thấp hơn nhóm bị bệnh; nhóm không bệnh có nồng độ glucose tập trung khoảng 99-130 mg/dl, còn nhóm bị bệnh khoảng 130-170 mg/dl
par(mfrow=c(1,2))
plot(diabetes$Insulin, diabetes$Glucose, xlab="Insulin", ylab="Glucose", type='p', col=c("red"), pch=20, main="Tương quan giữa Glucose và Insulin")
# Boxplot cho Outcome và Glucose theo người mắc bệnh và không
diabetes_groups <- cut(diabetes$Outcome, c(-Inf, 0, Inf), labels = c("Không bệnh", "Bệnh"))
boxplot(diabetes$Glucose~diabetes_groups, xlab="Outcome", ylab="Glucose (mg/dl)", main="Tỉ lệ giữa Glucose và Outcome", col="pink")
box(which="outer", lty="solid")
Ta có thể thấy:
Lượng Insulin càng ít thì lượng đường Glucose càng cao, do Insulin có khả nào chuyển hóa đường nhưng những người mắc bệnh tiểu đường thì cơ thể họ sẽ khó khắn trong việc sản xuất Insulin
Lượng đường Glucose trong tập data chủ yếu trong khoảng 90 - 150
Cách tính đường hồi quy tuyến tính:
\[ sum_{min} = d_1^2+d_2^2+d_3^2+... \\ \]
\[ d(M, \Delta) = \sqrt{\dfrac{|ax_0+by_0+c|}{a^2+b^2}} \\ \]
p1 <- ggplot(data = diabetes, aes(x = Glucose, y = Outcome)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Mối quan hệ giữa nồng độ glucose và kết quả bệnh tiểu đường",
x = "Nồng độ glucose (mg/dL)",
y = "Kết quả (1 = mắc bệnh, 0 = không mắc bệnh)")
p2 <- ggplot(data = diabetes, aes(x = SkinThickness, y = Outcome)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Mối quan hệ giữa độ dày của da và kết quả bệnh tiểu đường",
x = "độ dày da (mm)",
y = "Kết quả (1 = mắc bệnh, 0 = không mắc bệnh)")
p3 <- ggplot(data = diabetes, aes(x = Pregnancies, y = Outcome)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Mối quan hệ giữa số lần mang thai và kết quả bệnh tiểu đường",
x = "Số lần mang thai (lần)",
y = "Kết quả (1 = mắc bệnh, 0 = không mắc bệnh)")
grid.arrange(p1, p2, p3, ncol = 3)
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
Ta có thể thấy
Theo đường hồi quy tuyến tính dương trên, số lần mang thai hoặc nồng độ glucose càng tăng thì tỉ lệ mắc bệnh tiểu đường càng cao
Ngược lại, độ dày của da không ảnh hưởng quá mạnh vào khả năng mắc bệnh tiểu đường
sum_of_nan <- sum(is.na(diabetes))
paste("Tổng số giá trị rỗng trong dataset:", sum_of_nan)
## [1] "Tổng số giá trị rỗng trong dataset: 0"
for (col in colnames(diabetes)) {
nan_of_col <- sum(is.na(diabetes[, col]))
print(paste("Số giá trị rỗng trong cột", col, ":", nan_of_col))
}
## [1] "Số giá trị rỗng trong cột Pregnancies : 0"
## [1] "Số giá trị rỗng trong cột Glucose : 0"
## [1] "Số giá trị rỗng trong cột BloodPressure : 0"
## [1] "Số giá trị rỗng trong cột SkinThickness : 0"
## [1] "Số giá trị rỗng trong cột Insulin : 0"
## [1] "Số giá trị rỗng trong cột BMI : 0"
## [1] "Số giá trị rỗng trong cột DiabetesPedigreeFunction : 0"
## [1] "Số giá trị rỗng trong cột Age : 0"
## [1] "Số giá trị rỗng trong cột Outcome : 0"
find_outliers <- function(inp, na.rm = TRUE) {
i.qnt <- quantile(inp, probs = c(0.25, 0.75), na.rm = na.rm)
i.max <- 1.5 * IQR(inp, na.rm = na.rm)
outliers <- inp < (i.qnt[1] - i.max) | inp > (i.qnt[2] + i.max)
return(outliers)
}
calculate_rate <- function(inp) {
num_outliers <- sum(find_outliers(inp))
num_regular <- length(inp) - num_outliers
outlier_rate <- num_outliers / length(inp) * 100
regular_rate <- num_regular / length(inp) * 100
rates <- list(outlier=outlier_rate, regular=regular_rate)
return (rates)
}
for (col in colnames(diabetes)) {
rates <- calculate_rate(unlist(diabetes[, col]))
# Liệt kê tỉ lệ giá trị ngoại lại trong từng cột
print(paste(col, "-", "Total Outliers (%):", round(rates$outlier, 2), ", Regular Values (%):", round(rates$outlier, 2)))
}
## [1] "Pregnancies - Total Outliers (%): 0.52 , Regular Values (%): 0.52"
## [1] "Glucose - Total Outliers (%): 0.65 , Regular Values (%): 0.65"
## [1] "BloodPressure - Total Outliers (%): 5.86 , Regular Values (%): 5.86"
## [1] "SkinThickness - Total Outliers (%): 0.13 , Regular Values (%): 0.13"
## [1] "Insulin - Total Outliers (%): 4.43 , Regular Values (%): 4.43"
## [1] "BMI - Total Outliers (%): 2.47 , Regular Values (%): 2.47"
## [1] "DiabetesPedigreeFunction - Total Outliers (%): 3.78 , Regular Values (%): 3.78"
## [1] "Age - Total Outliers (%): 1.17 , Regular Values (%): 1.17"
## [1] "Outcome - Total Outliers (%): 0 , Regular Values (%): 0"
pie_outlier <- function(outlier_col) {for (col in outlier_col) {
rates <- calculate_rate(unlist(diabetes[, col]))
pie_data <- data.frame(
type = c("Outliers", "Regular"),
rate = c(rates$outlier, rates$regular)
)
# Vẽ trực quan những cột có tỉ lệ ngoại lai >= 2.3%
return(ggplot(pie_data, aes(x="", y = rate, fill=type)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
labs(title = paste(col),
caption = paste0("Total Outliers (%):", round(rates$outlier, 2), ", Regular Values (%):", round(rates$regular, 2))))
}}
grid.arrange(pie_outlier("BloodPressure"), pie_outlier("Insulin"), pie_outlier("BMI"), pie_outlier("DiabetesPedigreeFunction"), ncol=2)
library(lattice)
library(reshape2)
# creating correlation matrix
corr_mat <- round(cor(diabetes),2)
# reduce the size of correlation matrix
melted_corr_mat <- melt(corr_mat)
# head(melted_corr_mat)
# plotting the correlation heatmap
library(ggplot2)
ggplot(data = melted_corr_mat, aes(x=Var1, y=Var2, fill=value)) + geom_tile() +
geom_text(aes(Var2, Var1, label = value), color = "white", size = 4)
Ta có thể thấy:
Tất cả thuộc tính đều có quan hệ thuận biến với Outcome (kết quả)
Glucose, BMI và Age ảnh hưởng tới kết quả mắc bệnh lớn nhất với 0.47, 0.29, 0.24
SkinThickness, BloodPressure ảnh hưởng tới kết quả mắc bệnh yếu nhất với 0.7
#install.packages("readr")
#install.packages("dplyr")
#install.packages("tidyr")
#install.packages("ggplot2")
library(readr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following object is masked from 'package:gridExtra':
##
## combine
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyr)
##
## Attaching package: 'tidyr'
## The following object is masked from 'package:reshape2':
##
## smiths
library(ggplot2)
Kiểm tra các giá trị bị thiếu của từng cột
na_count<-colSums(sapply(diabetes,is.na))
na_count
## Pregnancies Glucose BloodPressure
## 0 0 0
## SkinThickness Insulin BMI
## 0 0 0
## DiabetesPedigreeFunction Age Outcome
## 0 0 0
Bộ dữ liệu này không tồn tại giá trị bị thiếu
Giới thiệu 3 phương pháp xử lí giá trị bị thiếu :
Điền giá trị trung vị.
diabetes_median <- diabetes %>%
mutate_all(~ ifelse(is.na(.), median(., na.rm = TRUE), .))
Điền giá trị trung bình.
diabetes_mean <- diabetes %>%
mutate_all(~ ifelse(is.na(.), mean(., na.rm = TRUE), .))
Điền giá trị phổ biến nhất.
fill_mode <- function(x) {
mode_val <- names(sort(table(x), decreasing = TRUE))[1]
x[is.na(x)] <- mode_val
return(x)
}
diabetes_mode <- diabetes %>%
mutate_all(fill_mode)
Bước 1:Tìm Z-score cho tất cả các điểm trong tập dữ liệu
Bước 2: Vẽ biểu đồ Scatter Plots biểu diễn ma trận Z-score
Bước 3: Quan sát biểu đồ Z-score,tìm Z-score ngưỡng
Bước 4: Dựa trên Z-score ngưỡng và loại các giá trị vượt qua Z-score ngưỡng đó
\[ Z = \frac{{X - \bar{X}}}{{\sigma}} \]
Trong đó:
\(X\) là giá trị của một điểm dữ liệu cụ thể trong một thuộc tính nào đó.
\(\bar{X}\) là giá trị trung bình của thuộc tính đó trong tập dữ liệu.
\(\sigma\) là độ lệch chuẩn của thuộc tính đó trong tập dữ liệu.
Áp dụng công thức trên để biến đổi bộ dữ liệu thành ma trận Z-score
diabetes_z <- (diabetes - colMeans(diabetes)) / apply(diabetes, 2, sd)
head(diabetes_z)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 0.6395305 7.99033370 215.8827363 9.2459493 -1.2873733 99.985707
## 2 -3.7499128 0.04512617 2.7855842 -2.8741635 -0.6924393 -0.564690
## 3 -3.1569579 19.15326626 133.4539425 -3.5702706 -4.0578295 48.120296
## 4 -1.2246861 267.19131711 18.4459142 0.1544326 282.2820762 7.198215
## 5 -0.6924393 8.82288025 -2.5301191 -0.3887359 11.4588828 -2.433161
## 6 -3.4236465 242.47973103 0.2528715 -4.0578295 -0.7316434 -2.247670
## DiabetesPedigreeFunction Age Outcome
## 1 -0.9550312 1.8469872005 1.593957
## 2 -3.7702115 -0.4234448489 -2.826550
## 3 -3.5355523 0.0009413653 1.365006
## 4 -1.2769045 61.9569937727 -1.141108
## 5 -0.6725858 -0.0204830505 -3.749913
## 6 -4.0323353 62.1678499768 -3.570271
Biểu đồ này cho phép chúng ta quan sát được các giá trị ngoại lai
melted_diabetes <- gather(diabetes_z, key = "Variable", value = "Z-score")
# Vẽ scatter plot cho tất cả các cột
ggplot(melted_diabetes, aes(x = Variable, y = `Z-score`, color = `Z-score`)) +
geom_point() +
labs(x = "Variable", y = "Z-score", title = "Scatter Plots of Z-scores") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # Điều chỉnh góc và vị trí của chú thích trên trục x
Ta chọn Z-score ngưỡng là 700 và loại các giá trị Z-score mà vượt qua ngưỡng đó
# Lọc các giá trị Z-score mà tất cả đều nhỏ hơn 700
diabetes_filter <- diabetes[rowSums(apply(diabetes_z, 2, function(x) abs(x) < 700)) == ncol(diabetes_z), ]
# Hiển thị kích thước của bộ dữ liệu sau khi loại bỏ
cat("Còn lại số lượng hàng và cột là:", dim(diabetes_filter))
## Còn lại số lượng hàng và cột là: 760 9
attach(diabetes_filter)
par(mfrow=c(2,4))
boxplot(Pregnancies~Outcome, main="No. of Pregnancies vs. Diabetes",
xlab="Outcome", ylab="Pregnancies",col="red")
boxplot(Glucose~Outcome, main="Glucose vs. Diabetes",
xlab="Outcome", ylab="Glucose",col="pink")
boxplot(BloodPressure~Outcome, main="Blood Pressure vs. Diabetes",
xlab="Outcome", ylab="Blood Pressure",col="green")
boxplot(SkinThickness~Outcome, main="Skin Thickness vs. Diabetes",
xlab="Outcome", ylab="Skin Thickness",col="orange")
boxplot(Insulin~Outcome, main="Insulin vs. Diabetes",
xlab="Outcome", ylab="Insulin",col="yellow")
boxplot(BMI~Outcome, main="BMI vs. Diabetes",
xlab="Outcome", ylab="BMI",col="purple")
boxplot(DiabetesPedigreeFunction~Outcome, main="Diabetes Pedigree Function vs. Diabetes", xlab="Outcome", ylab="DiabetesPedigreeFunction",col="lightgreen")
boxplot(Age~Outcome, main="Age vs. Diabetes",
xlab="Outcome", ylab="Age",col="lightblue")
box(which = "outer", lty = "solid")
diabetes_f<-diabetes_filter
diabetes_f $BloodPressure <- NULL
diabetes_f $SkinThickness <- NULL
dim(diabetes_f)
## [1] 760 7
Đảm bảo rằng các biến có thang đo đồng nhất.
Miền giá trị nằm trong Khoảng (0-1).
Giúp mô hình học tốt hơn.
Giảm ảnh hưởng của biên độ lớn giữa các biến.
\[ \text{Scaled Value} = \frac{{X - \text{min}(X)}}{{\text{max}(X) - \text{min}(X)}} \]
min_max_scaler <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
diabetes_scale <- as.data.frame(lapply(diabetes_f, min_max_scaler))
head(diabetes_scale)
## Pregnancies Glucose Insulin BMI DiabetesPedigreeFunction Age
## 1 0.35294118 0.7437186 0.0000000 0.5007452 0.23441503 0.4833333
## 2 0.05882353 0.4271357 0.0000000 0.3964232 0.11656704 0.1666667
## 3 0.47058824 0.9195980 0.0000000 0.3472429 0.25362938 0.1833333
## 4 0.05882353 0.4472362 0.1111111 0.4187779 0.03800171 0.0000000
## 5 0.00000000 0.6884422 0.1985816 0.6423249 0.94363792 0.2000000
## 6 0.29411765 0.5829146 0.0000000 0.3815201 0.05251921 0.1500000
## Outcome
## 1 1
## 2 0
## 3 1
## 4 0
## 5 1
## 6 0
train <- diabetes_scale [1:530,]
test <- diabetes_scale [531:760,]
model <-glm(Outcome ~.,family=binomial(link='logit'),data=train)
summary(model)
##
## Call:
## glm(formula = Outcome ~ ., family = binomial(link = "logit"),
## data = train)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -8.31656 0.79380 -10.477 < 2e-16 ***
## Pregnancies 2.17795 0.64605 3.371 0.000748 ***
## Glucose 6.28234 0.85292 7.366 1.76e-13 ***
## Insulin -0.77826 0.83942 -0.927 0.353857
## BMI 5.84760 1.10552 5.289 1.23e-07 ***
## DiabetesPedigreeFunction 2.68500 0.82830 3.242 0.001189 **
## Age 0.08529 0.65216 0.131 0.895952
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 686.91 on 529 degrees of freedom
## Residual deviance: 512.27 on 523 degrees of freedom
## AIC: 526.27
##
## Number of Fisher Scoring iterations: 5
anova(model, test="Chisq")
## Analysis of Deviance Table
##
## Model: binomial, link: logit
##
## Response: Outcome
##
## Terms added sequentially (first to last)
##
##
## Df Deviance Resid. Df Resid. Dev Pr(>Chi)
## NULL 529 686.91
## Pregnancies 1 25.416 528 661.49 4.621e-07 ***
## Glucose 1 102.956 527 558.54 < 2.2e-16 ***
## Insulin 1 0.089 526 558.45 0.7656248
## BMI 1 34.931 525 523.52 3.416e-09 ***
## DiabetesPedigreeFunction 1 11.233 524 512.29 0.0008037 ***
## Age 1 0.017 523 512.27 0.8960336
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitted.results <- predict(model,newdata=test,type='response')
fitted.results <- ifelse(fitted.results > 0.5,1,0)
(conf_matrix_logi<-table(fitted.results, test$Outcome))
##
## fitted.results 0 1
## 0 136 33
## 1 16 45
TP <- conf_matrix_logi[2, 2] # True Positive
TN <- conf_matrix_logi[1, 1] # True Negative
FP <- conf_matrix_logi[1, 2] # False Positive
FN <- conf_matrix_logi[2, 1] # False Negative
Accuracy<-(TP+TN)/(TP+TN+FP+FN)
cat("Độ chính xác:",Accuracy,"\n")
## Độ chính xác: 0.7869565
#trong số bệnh nhân thực tế bị bệnh tiểu đường,bao nhiêu phần trăm được dự báo bị bệnh
Sensitivity<-TP/(TP+FN)
cat("Độ nhạy:",Sensitivity,"\n")
## Độ nhạy: 0.7377049
#trong số bệnh nhân thực tế không bị bệnh, bao nhiêu phần trăm được dự báo không bị bệnh
Specificity<-TN/(TN+FP)
cat("Độ đặc hiệu",Specificity)
## Độ đặc hiệu 0.8047337
#install.packages("corrgram")
library(corrgram)
##
## Attaching package: 'corrgram'
## The following object is masked from 'package:lattice':
##
## panel.fill
corrgram(diabetes_filter)
Xét biểu đồ tương quan, ta thấy:
- Pregnancies và Age tương quan
cao
- SkinThickness và Insulin tương quan cao
Do đó: Khi xây dựng mô hình dự đoán, với mỗi cặp thuộc tính, chỉ sử dụng 1 trong 2 để hạn chế tình trạng đa cộng tuyến (multicollinearity)
library(caret)
## Warning: package 'caret' was built under R version 4.3.2
Chi tập dữ liệu thành 2 phần, tập train để xây dựng mô hình và tập test để kiểm tra mô hình
set.seed(1)
split <- createDataPartition(diabetes_filter$Outcome,p = 0.75,list = FALSE)
dfTrain <- diabetes_filter[split,]
dfTest <- diabetes_filter[-split,]
head(dfTrain)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 0 33.6
## 2 1 85 66 29 0 26.6
## 3 8 183 64 0 0 23.3
## 4 1 89 66 23 94 28.1
## 5 0 137 40 35 168 43.1
## 6 5 116 74 0 0 25.6
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 5 2.288 33 1
## 6 0.201 30 0
head(dfTest)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 9 2 197 70 45 543 30.5
## 10 8 125 96 0 0 0.0
## 12 10 168 74 0 0 38.0
## 13 10 139 80 0 0 27.1
## 14 1 189 60 23 846 30.1
## 23 7 196 90 0 0 39.8
## DiabetesPedigreeFunction Age Outcome
## 9 0.158 53 1
## 10 0.232 54 1
## 12 0.537 34 1
## 13 1.441 57 0
## 14 0.398 59 1
## 23 0.451 41 1
Xây dựng các hàm kiểm tra kết quả của mô hình
Hàm xây dựng ma trận nhầm lẫn để xem kết quả dự đoán và tính độ chính xác bằng ma trận nhầm lẫn
# Dùng ma trận nhầm lẫn
accuracy <- function() {
predictions <- predict(model, newdata = dfTest, type = "class")
# So sánh giữa giá trị dự đoán và giá trị thực tế
confusion_matrix <- table(predictions, dfTest$Outcome)
print(confusion_matrix)
# Tính toán độ chính xác từ ma trận nhầm lẫn
accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
return (accuracy)
}
Hàm tính độ chính xác, độ nhạy và độ đặc hiệu của mô hình
thongso <- function() {
predictions <- predict(model, newdata = dfTest, type = "class")
confusion_matrix <- table(predictions, dfTest$Outcome)
TP <- confusion_matrix[2, 2] # True Positive
TN <- confusion_matrix[1, 1] # True Negative
FP <- confusion_matrix[1, 2] # False Positive
FN <- confusion_matrix[2, 1] # False Negative
Accuracy<-(TP+TN)/(TP+TN+FP+FN)
cat("Độ chính xác:",Accuracy,"\n")
#trong số bệnh nhân thực tế bị bệnh tiểu đường,bao nhiêu phần trăm được dự báo bị bệnh
Sensitivity<-TP/(TP+FN)
cat("Độ nhạy:",Sensitivity,"\n")
#trong số bệnh nhân thực tế không bị bệnh, bao nhiêu phần trăm được dự báo không bị bệnh
Specificity<-TN/(TN+FP)
cat("Độ đặc hiệu",Specificity)
}
Xây dựng mô hình cây quyết định ban đầu. Với thuộc tính mục tiêu là Outcome, và biến thành phần và các thuộc tính còn lại của tập dữ liệu
# Huấn luyện mô hình cây quyết định
library(rpart)
model <- rpart(Outcome ~ Pregnancies + Glucose + BloodPressure + SkinThickness + Insulin + BMI + DiabetesPedigreeFunction + Age, data = dfTrain, method = "class")
# In cây quyết định
print(model)
## n= 570
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 570 202 0 (0.64561404 0.35438596)
## 2) Glucose< 127.5 369 75 0 (0.79674797 0.20325203)
## 4) Age< 29.5 216 23 0 (0.89351852 0.10648148) *
## 5) Age>=29.5 153 52 0 (0.66013072 0.33986928)
## 10) BMI< 26.95 33 1 0 (0.96969697 0.03030303) *
## 11) BMI>=26.95 120 51 0 (0.57500000 0.42500000)
## 22) Glucose< 106.5 62 17 0 (0.72580645 0.27419355) *
## 23) Glucose>=106.5 58 24 1 (0.41379310 0.58620690)
## 46) BloodPressure>=85 12 3 0 (0.75000000 0.25000000) *
## 47) BloodPressure< 85 46 15 1 (0.32608696 0.67391304)
## 94) SkinThickness>=13.5 27 12 1 (0.44444444 0.55555556)
## 188) DiabetesPedigreeFunction< 0.5485 15 4 0 (0.73333333 0.26666667) *
## 189) DiabetesPedigreeFunction>=0.5485 12 1 1 (0.08333333 0.91666667) *
## 95) SkinThickness< 13.5 19 3 1 (0.15789474 0.84210526) *
## 3) Glucose>=127.5 201 74 1 (0.36815920 0.63184080)
## 6) BMI< 30.2 54 20 0 (0.62962963 0.37037037)
## 12) Age>=56 10 0 0 (1.00000000 0.00000000) *
## 13) Age< 56 44 20 0 (0.54545455 0.45454545)
## 26) Age< 26.5 14 2 0 (0.85714286 0.14285714) *
## 27) Age>=26.5 30 12 1 (0.40000000 0.60000000) *
## 7) BMI>=30.2 147 40 1 (0.27210884 0.72789116)
## 14) Glucose< 157.5 88 33 1 (0.37500000 0.62500000)
## 28) BMI< 41.65 69 32 1 (0.46376812 0.53623188)
## 56) Age< 31 26 7 0 (0.73076923 0.26923077) *
## 57) Age>=31 43 13 1 (0.30232558 0.69767442) *
## 29) BMI>=41.65 19 1 1 (0.05263158 0.94736842) *
## 15) Glucose>=157.5 59 7 1 (0.11864407 0.88135593) *
# Biểu đồ cây quyết định
library(rpart.plot)
rpart.plot(model)
Xem kết quả dự đoán từ mô hình cây quyết định
thongso()
## Độ chính xác: 0.7473684
## Độ nhạy: 0.6206897
## Độ đặc hiệu 0.8030303
Quan sát biểu đồ mức độ quan trọng của các thuộc tính trong mô hình để thay đổi thuộc tính thành phần
library(vip)
##
## Attaching package: 'vip'
## The following object is masked from 'package:utils':
##
## vi
var_importance <- vip::vip(model, num_features = 10)
print(var_importance)
Quan sát tác động của của các thuộc tính riêng lẻ lên quyết định của mô hình
library(pdp)
library(ggplot2)
# Lặp qua từng cột trong dfTrain (loại bỏ cột mục tiêu)
for (col in names(dfTrain)[-ncol(dfTrain)]) {
# Tạo Partial Dependence Plot cho từng cột và lưu vào biến pdp_plot
pdp_plot <- partial(model, pred.var = col)
# Tạo biểu đồ Partial Dependence Plot và hiển thị
plot(pdp_plot, main = paste("Partial Dependence Plot for", col))
}
Dựa vào biểu đồ mức độ quan trọng của các thuộc tính trong mô
hình ta thấy rằng:
- Các thuộc tính Pregnancies, BloodPressure,
SkinThickness, Insulin có trọng số thấp nhất.
Dựa vào Partial
Dependence Plot (PDP) - Biểu đồ phụ thuộc một phần thấy rằng:
- Các
thuộc tính Pregnancies, BloodPressure, SkinThickness, Insulin tăng lên
nhưng không ảnh hưởng đến kết quả dự đoán của mô hình.
Loại bỏ thuộc tính Insulin, SkinThickness và Pregnancies thấy mô hình tăng độ chính xác, độ nhạy và độ đặc hiệu. Quyết định chấp nhận mô hình với độ chính xác ~76% và độ nhạy ~0.64, độ đặc hiệu ~0.82
model <- rpart(Outcome ~ Glucose + BloodPressure + BMI + DiabetesPedigreeFunction + Age, data = dfTrain, method = "class")
rpart.plot(model)
Thông số của mô hình sau khi loại bỏ các thuộc tính có độ quan trọng
thấp
accuracy()
##
## predictions 0 1
## 0 106 23
## 1 22 39
## [1] 0.7631579
thongso()
## Độ chính xác: 0.7631579
## Độ nhạy: 0.6393443
## Độ đặc hiệu 0.8217054
Xem quy luật của mô hình cây quyết định
rules <- rpart.rules(model)
print(rules)
## Outcome
## 0.00 when Glucose >= 128 & BMI < 30 & Age >= 56
## 0.03 when Glucose < 128 & BMI < 27 & Age >= 30
## 0.11 when Glucose < 128 & Age < 30
## 0.14 when Glucose >= 128 & BMI < 30 & Age < 27
## 0.25 when Glucose is 107 to 128 & BMI >= 27 & Age >= 30 & BloodPressure >= 85
## 0.27 when Glucose is 128 to 158 & BMI is 30 to 42 & Age < 31
## 0.27 when Glucose < 107 & BMI >= 27 & Age >= 30
## 0.60 when Glucose >= 128 & BMI < 30 & Age is 27 to 56
## 0.67 when Glucose is 107 to 128 & BMI >= 27 & Age >= 30 & BloodPressure < 85
## 0.70 when Glucose is 128 to 158 & BMI is 30 to 42 & Age >= 31
## 0.88 when Glucose >= 158 & BMI >= 30
## 0.95 when Glucose is 128 to 158 & BMI >= 42
-> Hai mô hình cho thấy độ chính xác và độ đặc hiệu khá giống nhau -> Nhưng độ nhạy lại khác nhau.Mô hình hồi quy logistic có độ nhạy lớn hơn cho thấy mô hình nhận dạng các trường hợp bị bệnh chính xác hơn
-> Đối với các bài toán dự báo bệnh thì độ nhạy lớn sẽ tốt hơn. Điều này là do độ nhạy là thước đo về mức độ tốt của mô hình trong việc dự đoán các trường hợp dương tính. Trong các bài toán dự báo bệnh, việc xác định chính xác các trường hợp mắc bệnh là rất quan trọng. Nếu mô hình có độ nhạy thấp, thì mô hình sẽ có xu hướng bỏ sót các trường hợp mắc bệnh, điều này có thể dẫn đến việc điều trị sai hoặc không điều trị.
The World Health Organization (WHO) classifies adults based on the following BMI ranges:
Underweight: < 18.5 kg/m^2
Normal weight: 18.5 - 24.9 kg/m^2
Overweight: 25.0 - 29.9 kg/m^2
Obese: ≥ 30.0 kg/m^2
library(ggplot2)
diabetes_filter$BMI_Category <- cut(diabetes_filter$BMI,
breaks = c(-Inf, 18.5, 24.9, 29.9, Inf),
labels = c("Underweight", "Normal", "Overweight", "Obese"))
Trực quan phân bố của từng loại cân nặng
ggplot(diabetes_filter, aes(x = BMI_Category)) +
geom_bar(stat = "count") +
labs(title = "Distribution of BMI Categories", x = "BMI Category", y = "Count") +
theme_bw()
Tạo một dataframe bản sao với BMI_Category đã được số hóa
num_diabetes <- diabetes_filter
num_diabetes$BMI_Category <- as.integer(num_diabetes$BMI_Category)
str(num_diabetes)
## 'data.frame': 760 obs. of 10 variables:
## $ Pregnancies : int 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : int 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : int 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : int 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : int 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : int 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : int 1 0 1 0 1 0 1 0 1 1 ...
## $ BMI_Category : int 4 3 2 3 4 3 4 4 4 1 ...
Tìm các biến độc lập so với biến phụ thuộc BMI_Category
library(lattice)
library(reshape2)
# creating correlation matrix
corr_mat <- round(cor(num_diabetes),2)
# reduce the size of correlation matrix
melted_corr_mat <- melt(corr_mat)
# head(melted_corr_mat)
# plotting the correlation heatmap
library(ggplot2)
ggplot(data = melted_corr_mat, aes(x=Var1, y=Var2, fill=value)) + geom_tile() +
geom_text(aes(Var2, Var1, label = value), color = "white", size = 4)
Ta có thể thấy:
So với BMI_Category, các thuộc tính BMI, Insulin, SkinThickness, BloodPressure và Glucose đều tương quan mạnh (5 thuộc tính)
Vậy thuộc tính BMI_Category sẽ phụ thuộc nhiều vào chúng, ta có thể xem chúng là các biến độc lập
set.seed(456)
samp <- sample(nrow(diabetes_filter), 0.7 * nrow(diabetes_filter))
train <- diabetes_filter[samp, ]
test <- diabetes_filter[-samp, ]
#Kiểm tra kích thước của tập dữ liệu huấn luyện và kiểm tra
str(train)
## 'data.frame': 532 obs. of 10 variables:
## $ Pregnancies : int 7 8 1 0 12 7 7 8 0 5 ...
## $ Glucose : int 137 194 84 119 92 136 161 196 93 115 ...
## $ BloodPressure : int 90 80 64 66 62 74 86 76 60 76 ...
## $ SkinThickness : int 41 0 23 27 7 26 0 29 0 0 ...
## $ Insulin : int 0 0 115 0 258 135 0 280 0 0 ...
## $ BMI : num 32 26.1 36.9 38.8 27.6 26 30.4 37.5 35.3 31.2 ...
## $ DiabetesPedigreeFunction: num 0.391 0.551 0.471 0.259 0.926 0.647 0.165 0.605 0.263 0.343 ...
## $ Age : int 39 67 28 22 44 51 47 57 25 44 ...
## $ Outcome : int 0 0 0 0 1 0 1 1 0 1 ...
## $ BMI_Category : Factor w/ 4 levels "Underweight",..: 4 3 4 4 3 3 4 4 4 4 ...
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:gridExtra':
##
## combine
## The following object is masked from 'package:ggplot2':
##
## margin
rf_fit <- tuneRF(
x = train[, 1:8],
y = train[, 10],
metric = "OOBAcc",
ntreeTry = 100,
mtryStart = 3,
stepFactor = 1.5,
improve = 0.01,
trace = T,
plot = T
)
## mtry = 3 OOB error = 1.13%
## Searching left ...
## mtry = 2 OOB error = 2.07%
## -0.8333333 0.01
## Searching right ...
## mtry = 4 OOB error = 0.56%
## 0.5 0.01
## mtry = 6 OOB error = 0.38%
## 0.3333333 0.01
## mtry = 8 OOB error = 0%
## 1 0.01
# Lặp lại quá trình huấn luyện và đánh giá trên các giá trị khác nhau của ntree
error_out_of_bag <- c()
for (ntree in 1:100) {
# Huấn luyện mô hình
model <- randomForest(BMI_Category ~ . -Age -Pregnancies, data = train, ntree = ntree, mtry=3)
# Tạo tập oob
oob <- sample(nrow(train), 0.3 * nrow(train))
# Tính lỗi out_of_bag
error_out_of_bag[ntree] <- mean(predict(model, train[oob,]) != train[oob,]$BMI_Category)
}
# Vẽ đường cong lỗi
plot(error_out_of_bag, type = "l", col = "red", ylab = "OOB error", xlab="Số cây trong rừng")
model <- randomForest(BMI_Category ~ . -Age -Pregnancies, data = train, ntree = 100, mtry=3)
model
##
## Call:
## randomForest(formula = BMI_Category ~ . - Age - Pregnancies, data = train, ntree = 100, mtry = 3)
## Type of random forest: classification
## Number of trees: 100
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 0.75%
## Confusion matrix:
## Underweight Normal Overweight Obese class.error
## Underweight 7 3 0 0 0.30000000
## Normal 0 70 1 0 0.01408451
## Overweight 0 0 128 0 0.00000000
## Obese 0 0 0 323 0.00000000
Ta có thể thấy:
OOB error tỉ lệ số lần phân loại sai trên tập OOB ở các cây trong rừng, OOB error càng thấp thì độ chính xác càng cao
class error là tỉ lệ phân loại sai ở các nhãn trên tập train đã tách OOB
Các chỉ số trên có thể khác nhau ở mỗi lần train, vì tập OOB sẽ lấy ngẫu nhiên
prediction_test <- predict(model, newdata = test)
prediction_train <- predict(model, newdata = train)
table(prediction_train, train$BMI_Category)
##
## prediction_train Underweight Normal Overweight Obese
## Underweight 10 0 0 0
## Normal 0 71 0 0
## Overweight 0 0 128 0
## Obese 0 0 0 323
table(prediction_test, test$BMI_Category)
##
## prediction_test Underweight Normal Overweight Obese
## Underweight 3 0 0 0
## Normal 2 30 0 0
## Overweight 0 0 51 0
## Obese 0 0 0 142
results<-cbind(prediction_test, test$BMI_Category)
head(results)
## prediction_test
## 2 3 3
## 5 4 4
## 6 3 3
## 7 4 4
## 11 4 4
## 14 4 4
colnames(results)<-c('pred','real')
results<-as.data.frame(results)
View(results)
accuracy_test <- mean(prediction_test == test$BMI_Category)
accuracy_train <- mean(prediction_train == train$BMI_Category)
paste("độ chính xác trên tập train: ", accuracy_train)
## [1] "độ chính xác trên tập train: 1"
paste("độ chính xác trên tập test: ", accuracy_test)
## [1] "độ chính xác trên tập test: 0.991228070175439"
Bạn có thể thấy rằng độ chính xác của mô hình này là khoảng 99-100 (%), điều này thật tuyệt vời. Bây giờ chúng tôi đã tự động hóa quá trình dự đoán loại cân nặng. Điều này đưa chúng ta đến khả năng theo dõi tình trạng sức khỏa của bệnh nhân và đưa ra chuẩn đoán sớm
library(knitr)
# Vẽ biểu đồ histogram
ggplot(diabetes_filter, aes(x = BMI)) +
geom_histogram(binwidth = 2, fill = "lightblue", color = "black") +
labs(x = "BMI", title = "Histogram of BMI") + theme_minimal()
# summary statistics
kable(summary(select(diabetes_filter, BMI)), format = "markdown")
| BMI | |
|---|---|
| Min. : 0.00 | |
| 1st Qu.:27.30 | |
| Median :32.00 | |
| Mean :31.96 | |
| 3rd Qu.:36.52 | |
| Max. :67.10 |
\[ H_0: bmi = 34 \]
\[ H_a: bmi \neq 34 \]
\[ t = \frac{\bar{x} - \mu_0}{s/\sqrt{n}} \\ \bar{x} \text{: là giá trị trung bình của mẫu.} \\ \mu_0 \text{: là giá trị trung bình đã biết đến hoặc giả định.} \\ s \text{: là độ lệch chuẩn của mẫu.} \\ n \text{: là kích thước mẫu.} \\ \]
# Tính độ lệch chuẩn của mẫu dữ liệu
standard_deviation <- sd(diabetes_filter$BMI)
print(standard_deviation)
## [1] 7.902608
t.test(num_diabetes[num_diabetes$Outcome==1,6], mu=34)
##
## One Sample t-test
##
## data: num_diabetes[num_diabetes$Outcome == 1, 6]
## t = 2.4563, df = 263, p-value = 0.01469
## alternative hypothesis: true mean is not equal to 34
## 95 percent confidence interval:
## 34.21895 35.98862
## sample estimates:
## mean of x
## 35.10379
# Vẽ biểu đồ histogram
ggplot(diabetes_filter, aes(x = BMI_Category)) +
geom_bar(stat = "count") +
labs(title = "Distribution of BMI Categories", x = "BMI Category", y = "Count") +
theme_bw()
\[ H_0: bmi_0 = bmi_1 \]
\[ H_a: bmi_0 \neq bmi_1 \]
\[ t = \frac{\bar{x}_A - \bar{x}_B}{\sqrt{\frac{s_A^2}{n_A} + \frac{s_B^2}{n_B}}} \\ (\bar{x}_A) \text{: là giá trị trung bình của nhóm A.} \\ (\bar{x}_B) \text{: là giá trị trung bình của nhóm B.} \\ (s_A) \text{: là độ lệch chuẩn của nhóm A.} \\ (s_B) \text{: là độ lệch chuẩn của nhóm B.} \\ (n_A) \text{: là kích thước của nhóm A.} \\ (n_B) \text{: là kích thước của nhóm B.} \\ \]
#lấy ra những người bệnh và không bệnh có chỉ số BMI_Category >= 3
sick <- num_diabetes[num_diabetes$Outcome==1 & num_diabetes$BMI_Category >= 3,6]
normal <- num_diabetes[num_diabetes$Outcome==0 & num_diabetes$BMI_Category >= 3,6]
length(sick)
## [1] 255
length(normal)
## [1] 389
# Tính độ lệch chuẩn của mỗi mẫu dữ liệu
sa <- sd(sick)
print(sa)
## [1] 6.447191
sb <- sd(normal)
print(sb)
## [1] 5.58764
# Kiểm tra đồng nhất về phương sai
result <- var.test(sick, normal)
print(result)
##
## F test to compare two variances
##
## data: sick and normal
## F = 1.3313, num df = 254, denom df = 388, p-value = 0.01127
## alternative hypothesis: true ratio of variances is not equal to 1
## 95 percent confidence interval:
## 1.066823 1.670255
## sample estimates:
## ratio of variances
## 1.331325
t.test(normal,sick,var.equal=FALSE)
##
## Welch Two Sample t-test
##
## data: normal and sick
## t = -5.5659, df = 488.23, p-value = 4.307e-08
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -3.714332 -1.776138
## sample estimates:
## mean of x mean of y
## 32.94653 35.69176
# Vẽ biểu đồ histogram
ggplot(num_diabetes, aes(x = Age)) +
geom_histogram(binwidth = 2, fill = "lightblue", color = "black") +
labs(x = "Age", title = "Histogram of Age") +
theme_minimal()
# summary statistics
kable(summary(select(diabetes, Age)), format = "markdown")
| Age | |
|---|---|
| Min. :21.00 | |
| 1st Qu.:24.00 | |
| Median :29.00 | |
| Mean :33.24 | |
| 3rd Qu.:41.00 | |
| Max. :81.00 |
###giả thuyết độ tuổi trung bình mắc bệnh tiểu đường là 35
\[ H_0: age = 35 \]
\[ H_a: age \neq 35 \]
t.test(num_diabetes[num_diabetes$Outcome==1,8], mu=35)
##
## One Sample t-test
##
## data: num_diabetes[num_diabetes$Outcome == 1, 8]
## t = 3.1548, df = 263, p-value = 0.001793
## alternative hypothesis: true mean is not equal to 35
## 95 percent confidence interval:
## 35.80157 38.46359
## sample estimates:
## mean of x
## 37.13258