rm(list = ls())
를 이용해 R의 environment를 초기화 할 수
있습니다.rm(list = ls())
library(dplyr) # 데이터 처리
##
## 다음의 패키지를 부착합니다: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(ggcorrplot) # 상관관계 시각화
## Warning: 패키지 'ggcorrplot'는 R 버전 4.3.3에서 작성되었습니다
## 필요한 패키지를 로딩중입니다: ggplot2
library(ggplot2) # 시각화
library(rpart) # 결정나무
library(rpart.plot) # 결정나무 시각화
library(randomForest) # 배깅 및 랜덤포레스트
## Warning: 패키지 'randomForest'는 R 버전 4.3.3에서 작성되었습니다
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## 다음의 패키지를 부착합니다: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:dplyr':
##
## combine
library(neuralnet) # 인공신경망
## Warning: 패키지 'neuralnet'는 R 버전 4.3.3에서 작성되었습니다
##
## 다음의 패키지를 부착합니다: 'neuralnet'
## The following object is masked from 'package:dplyr':
##
## compute
library(caret) # 데이터 분리 및 모형 평가
## Warning: 패키지 'caret'는 R 버전 4.3.3에서 작성되었습니다
## 필요한 패키지를 로딩중입니다: lattice
library(tidyr) # 데이터 전처리
library(pROC) # ROC 계산
## Warning: 패키지 'pROC'는 R 버전 4.3.3에서 작성되었습니다
## Type 'citation("pROC")' for a citation.
##
## 다음의 패키지를 부착합니다: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
head()
함수를 통해 불러온 데이터의 앞부분을 확인
가능하며, n = 5
와 같이 표시할 행의 수를 명시할 수
있습니다.hn19 <- read.csv("HN19_lec3.csv")
head(hn19, n = 5) # 데이터의 첫 5개 행 확인
## age HE_BMI HE_HP HE_DM_HbA1c
## 1 61 25.98739 3 3
## 2 28 16.90094 1 1
## 3 53 19.78183 2 1
## 4 50 26.63165 1 2
## 5 16 NA NA NA
head()
와 동일한 방법으로, tail()
을 이용해
데이터의 뒷부분을 확인할 수 있으며, n = 3
과 같이 표시할
행의 수를 명시할 수 있습니다.hn19 <- hn19 %>%
mutate(
bmi = HE_BMI,
hp = as.numeric(HE_HP %in% c(2, 3)),
diabetes = as.numeric(HE_DM_HbA1c %in% c(2, 3))
) %>%
filter(!is.na(hp), !is.na(bmi), !is.na(diabetes)) %>%
select(age, bmi, hp, diabetes)
tail(hn19, n = 3) # 데이터의 마지막 5개 행 확인
## age bmi hp diabetes
## 7668 43 15.37515 0 0
## 7669 16 14.84497 0 0
## 7670 8 14.52800 0 0
변수 간 상관관계를 확인하면 데이터 구조와 변수 간 관계를 이해하는 데 도움이 됩니다.
상관계수(correlation coefficient)는 두 변수 간 선형 관계를 -1 과 1 사이의 값으로 나타냅니다.
R에서 상관관계는 수치형 설명변수끼리만 구할 수 있습니다.
우선 반응변수 y
와 수치형 설명변수 age
,
bmi
, hp
, diabetes
의 상관계수를
구하고 이를 시각화합니다. hp
, diabetes
는
실질적으로는 범주형이긴 하지만 as.factor()
변환을 하지 않은
상태이므로 수치형으로 인식돼 상관계수를 구할 수 있습니다.
상관계수를 구할 변수들인 y
, age
,
bmi
, hp
, diabetes
를
select()
함수를 이용해서 뽑아내고, cor()
함수를
이용해 상관계수 행렬을 만듭니다.
cor_mat <- hn19 %>%
select(age, bmi, hp, diabetes) %>%
cor()
ggcorrplot
패키지의
ggcorrplot()
함수를 이용합니다.ggcorrplot(
cor_mat,
lab = TRUE,
lab_size = 5,
tl.cex = 10,
colors = c("#6D9EC1", "white", "#E46726")
)
ggplot2
를 이용해 반응변수와 설명변수에 대한 산점도를
그립니다.BMI
를 선택해 x축으로 하고, 반응변수인
diabetes
를 y축으로 하는 산점도를 그립니다.diabetes
가 0 또는 1이므로 그냥
geom_point
를 사용하면 y=0
또는
y=1
에 겹쳐지게 됩니다. 따라서 geom_jitter
를
사용해 점들이 y=0
또는 y=1
주변으로 흩뿌려지게
합니다.ggplot(hn19, aes(x = bmi, y = diabetes)) +
geom_jitter(
color = "#6D9EC1",
height = 0.05,
width = 0,
alpha = 0.4
) +
labs(
title = "BMI vs 당뇨병",
x = "BMI",
y = "당뇨병"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
로지스틱 회귀는 종속변수가 이항형(0/1)일 때 확률을 예측하는데 사용됩니다.
주어진 데이터에서 우선 설명변수를 2개 사용하여 age
,
bmi
를 설명변수로, diabetes
를 반응변수로 하는
로지스틱 회귀모형을 적합합니다.
R에서 로지스틱 회귀모형을 적합하는 경우 glm()
함수를
이용합니다. 문법은 lm
과 대동소이합니다. 함수 호출 시
전달해야 하는 인자는 다음과 같습니다.
diabetes
, 설명변수는
age
, bmi
이므로 다음과 같이 인자를 전달할 수
있습니다. formula = diabetes ~ age + bmi
hn19
라는 변수명으로 저장하였으므로
data = hn19
와 같이 인자를 전달합니다."binomial"
을
사용합니다.glm()
함수를 이용해 선형회귀모형 적합을 완료한 후,
summary()
함수를 이용하여 회귀모형 적합 결과를 확인할 수
있습니다.
summary()
의 결과를 통해 개별 회귀계수 추정치와 이에
대한 t-검정 경과는 물론 전체 로지스틱 회귀모형의 유의성에 대한 F-검정
결과, R-squared와 adjusted R-squared를 모두 확인할 수 있습니다.
model_logistic_2d <- glm(
formula = diabetes ~ age + bmi,
data = hn19,
family = "binomial"
)
summary(model_logistic_2d)
##
## Call:
## glm(formula = diabetes ~ age + bmi, family = "binomial", data = hn19)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.994652 0.217239 -32.20 <2e-16 ***
## age 0.062048 0.001672 37.11 <2e-16 ***
## bmi 0.160854 0.008059 19.96 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 10488.7 on 7669 degrees of freedom
## Residual deviance: 7472.3 on 7667 degrees of freedom
## AIC: 7478.3
##
## Number of Fisher Scoring iterations: 5
grid
에 생성합니다.pred_logistic_2d
에
저장합니다.ggplot()
을 이용해 색상으로 시각화합니다.age_seq <- seq(min(hn19$age), max(hn19$age), length.out = 100)
bmi_seq <- seq(min(hn19$bmi), max(hn19$bmi), length.out = 100)
grid <- expand.grid(age = age_seq, bmi = bmi_seq)
pred_logistic_2d <- predict(
model_logistic_2d,
newdata = grid,
type = "response"
)
ggplot() +
geom_tile(
aes(
x = grid[["age"]],
y = grid[["bmi"]],
fill = pred_logistic_2d
),
alpha = 0.4
) +
scale_fill_gradient(low = "lightblue", high = "red") +
labs(
title = "로지스틱 회귀의 당뇨병 예측 확률",
x = "나이 (Age)",
y = "BMI",
fill = "P(당뇨병=1)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
age
, bmi
,
hp
를 설명변수로, diabetes
를 반응변수로 하는
로지스틱 회귀모형을 적합합니다.model_logistic <- glm(
formula = diabetes ~ age + bmi + hp,
data = hn19,
family = "binomial"
)
summary(model_logistic)
##
## Call:
## glm(formula = diabetes ~ age + bmi + hp, family = "binomial",
## data = hn19)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.741318 0.220841 -30.526 < 2e-16 ***
## age 0.057853 0.001831 31.589 < 2e-16 ***
## bmi 0.150998 0.008249 18.304 < 2e-16 ***
## hp 0.341577 0.063894 5.346 8.99e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 10489 on 7669 degrees of freedom
## Residual deviance: 7444 on 7666 degrees of freedom
## AIC: 7452
##
## Number of Fisher Scoring iterations: 5
model_logistic
에 함수
plot()
을 적용함으로써 잔차 및 진단 그래프를 확인할 수
있습니다.plot(model_logistic)
결정나무(Decision Tree)는 변수 공간을 여러 영역으로 나누어 각 영역마다 같은 예측값을 갖도록 하는 모델입니다.
해석이 쉽고 비선형 관계를 잡아낼 수 있지만, 깊게 성장하면 과적합(overfitting)이 발생할 수 있습니다.
이를 방지하기 위해 가지치기(pruning) 를 수행하여 최적의 복잡도를 선택합니다
결정 나무의 경우에는 분류로 사용하려면 반응변수에
factor
형태의 변수를 넣어야 합니다.
as.factor()
를 사용하여 당뇨병 여부를 factor
로
변환하고 이를 diabetes_factor
에 저장합니다.
hn19 <- hn19 %>% mutate(diabetes_factor = as.factor(diabetes))
주어진 데이터에서 우선 설명변수를 2개 사용하여 age
,
bmi
를 설명변수로, diabetes
를 반응변수로 하는
분류 결정나무를 적합합니다.
R에서 결정나무를 적합하는 경우 rpart
패키지의
rpart
()함수를 이용합니다. 문법은
lm`과
대동소이합니다. 함수 호출 시 전달해야 하는 인자는 다음과 같습니다.
diabetes
, 설명변수는
age
, bmi
이므로 다음과 같이 인자를 전달할 수
있습니다. formula = diabetes ~ age + bmi
hn19
라는 변수명으로 저장하였으므로
data = hn19
와 같이 인자를 전달합니다."class"
을
사용합니다.rpart.plot()
함수를 사용해 적합한 결정나무를
시각화합니다.
model_tree_2d <- rpart(
diabetes_factor ~ age + bmi,
data = hn19,
method = "class"
)
rpart.plot(model_tree_2d, type = 2, extra = 106, cex = 1.2)
grid
를 이용합니다.pred_tree_2d
에
저장합니다.ggplot()
을 이용해 색상으로 시각화합니다.pred_tree_2d <-
predict(model_tree_2d, newdata = grid, type = "prob")[, 2]
ggplot() +
geom_tile(
aes(
x = grid[["age"]],
y = grid[["bmi"]],
fill = pred_tree_2d
),
alpha = 0.4
) +
scale_fill_gradient(low = "lightblue", high = "red") +
labs(
title = "결정 나무의 영역별 당뇨병 예측 확률",
x = "나이(Age)",
y = "BMI",
fill = "P(당뇨병=1)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
age
, bmi
,
hp
를 설명변수로, diabetes
를 반응변수로 하는
분류 결정나무를 적합하고 가지치기(pruning)을 합니다.control
은 결정나무를 적합하는 데 추가적으로 사용하는
옵션을 지정해 줍니다. 나중에 가지치기를 하기 위해, 우선
cp = 0.001
로 설정하여 우선 결정나무를 충분히 복잡하게
만듭니다.model_tree <- rpart(
diabetes_factor ~ age + bmi + hp,
data = hn19,
method = "class",
control = rpart.control(cp = 0.001)
)
printcp()
함수로 각 단계에서의 cp 값을 출력합니다.best_cp
에는 교차검증 오차가 최소가 되는 cp 값이
저장되게 됩니다.prune()
함수로 가지치기(pruning)을 해서 최적의
결정나무를 model_ptree
에 저장합니다.rpart.plot()
함수를 사용해 가지치기한 결정나무를
시각화합니다.printcp(model_tree)
##
## Classification tree:
## rpart(formula = diabetes_factor ~ age + bmi + hp, data = hn19,
## method = "class", control = rpart.control(cp = 0.001))
##
## Variables actually used in tree construction:
## [1] age bmi hp
##
## Root node error: 3310/7670 = 0.43155
##
## n= 7670
##
## CP nsplit rel error xerror xstd
## 1 0.3927492 0 1.00000 1.00000 0.013105
## 2 0.0253776 1 0.60725 0.60906 0.011647
## 3 0.0021148 3 0.55650 0.56979 0.011394
## 4 0.0015710 8 0.54411 0.56949 0.011392
## 5 0.0013595 15 0.53263 0.56284 0.011346
## 6 0.0012085 17 0.52991 0.55559 0.011296
## 7 0.0010070 32 0.50906 0.55529 0.011294
## 8 0.0010000 35 0.50604 0.55498 0.011292
best_cp <- model_tree$cptable[which.min(model_tree$cptable[, "xerror"]), "CP"]
model_ptree <- prune(model_tree, cp = best_cp)
rpart.plot(model_ptree, type = 2, extra = 106, cex = 1)
배깅(Bagging) 은 여러 부트스트랩 샘플에 대해 모델(결정나무)을 학습하고 예측을 평균/다수결로 결합합니다.
랜덤 포레스트(Random Forest) 는 배깅에 변수 무작위 선택을 추가하여 상관성을 줄이고 성능을 향상시킵니다.
배깅과 랜덤 포레스트는 모두 randomForest
패키지의
randomForest()
함수로 적합할 수 있습니다. 이는 랜덤
포레스트에서 변수의 개수를 전부 다 쓰면 그게 배깅이 되기
때문입니다.
앞에서와 마찬가지로 설명변수를 3개 사용하여 age
,
bmi
, hp
를 설명변수로, diabetes
를
반응변수로 하는 앙상블 모형을 생각합니다.
우선 배깅 모형을 적합합니다. 이는 randomForest()
함수에서 사용하는 설명변수의 개수를 전체 설명변수의 개수인 3으로
지정하면 됩니다 (mtry=3).
print()
함수로 적합한 배깅 모형의 요약 정보를
출력합니다.
plot()
함수로 적합한 트리 수에 따른 오차 변화를
확인합니다.
model_bag <- randomForest(
diabetes_factor ~ age + bmi + hp,
data = hn19,
mtry = 3,
ntree = 100,
importance = TRUE
)
print(model_bag)
##
## Call:
## randomForest(formula = diabetes_factor ~ age + bmi + hp, data = hn19, mtry = 3, ntree = 100, importance = TRUE)
## Type of random forest: classification
## Number of trees: 100
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 28.45%
## Confusion matrix:
## 0 1 class.error
## 0 3220 1140 0.2614679
## 1 1042 2268 0.3148036
plot(model_bag)
randomForest()
함수에서 사용하는 설명변수의 개수를 전체
설명변수의 개수보다 적게 지정하면 됩니다. (mtry=2).print()
함수로 적합한 배깅 모형의 요약 정보를
출력합니다.plot()
함수로 적합한 트리 수에 따른 오차 변화를
확인합니다.model_rf <- randomForest(
diabetes_factor ~ age + bmi + hp,
data = hn19,
mtry = 2,
ntree = 100,
importance = TRUE
)
print(model_rf)
##
## Call:
## randomForest(formula = diabetes_factor ~ age + bmi + hp, data = hn19, mtry = 2, ntree = 100, importance = TRUE)
## Type of random forest: classification
## Number of trees: 100
## No. of variables tried at each split: 2
##
## OOB estimate of error rate: 27.46%
## Confusion matrix:
## 0 1 class.error
## 0 3222 1138 0.2610092
## 1 968 2342 0.2924471
plot(model_rf)
신경망(Neural Network)은 입력층, 은닉층, 출력층으로 구성되며, 비선형 관계 학습에 강점이 있습니다.
충분한 데이터와 계산 자원이 필요하지만, 복잡한 패턴을 학습할 수 있습니다.
앞에서와 마찬가지로 설명변수를 3개 사용하여 age
,
bmi
, hp
를 설명변수로, diabetes
를
반응변수로 하는, 은닉층(hidden layer)이 하나인 신경망 모형을
생각합니다.
신경망 모형을 적합하는 패키지는 여러 가지 있지만, 여기서는 가장
간단하게 할 수 있는 neuralnet
패키지의
neuralnet()
함수를 소개합니다. 다만 이 패키지는 느린
편이어서, 더 복잡한 자료에서는 keras
패키지 내지는
torch
패키지를 사용하면 좀 더 빠르게 실행할 수
있습니다.
속도가 느린 관계로 hn19[1:100, ]
명령어로 처음
100개의 행만 골라냅니다.
hidden
은 은닉층(hidden layer)의 뉴런 수입니다.
아래에서는 hidden = 2
를 사용했습니다.
출력층은 로지스틱 회귀처럼 0~1 범위의 확률을 출력하도록
linear.output = FALSE
사용합니다.
plot()
함수로 신경망 구조를 시각화합니다.
model_nn <- neuralnet(
diabetes ~ age + bmi + hp,
data = hn19[1:100, ],
hidden = 2,
linear.output = FALSE
)
plot(model_nn)
혼동행렬, 민감도, 특이도(Slide 60~64)로 모델 성능을 평가합니다.
threshold 변화에 따른 민감도·특이도 변화(ROC Curve)를 시각화하고, AUC를 계산해 분류기 성능을 종합적으로 판단합니다.
학습용/테스트용 데이터 분리를 통해 과대적합을 방지합니다.
우선 학습용/테스트용 데이터(training data/test data)를 10% 대
90%로 분리합니다. caret
패키지의
createDataPartition()
함수를 사용합니다.
set.seed(2025)
id_train <- createDataPartition(hn19[["diabetes"]], p = 0.1, list = FALSE)
hn19_train <- hn19[id_train, ]
hn19_test <- hn19[-id_train, ]
thresholds
에는 threshold 의 값이 벡터로
들어옵니다.actual
은 실제 반응변수의 값, predicted
는
예측된 반응변수의 값이 들어옵니다. 단, 여기서 predicted
는
확률로 값이 들어가야 합니다.calc_metrics <- function(thresholds, actual, predicted) {
# threshold 하나에 대해 평가 지표를 계산
metrics_one <- function(threshold, actual, predicted) {
# 예측 확률이 threshold보다 크면 1(양성), 작으면 0(음성)
pred_class <- ifelse(predicted > threshold, 1, 0)
# 혼동 행렬 요소 계산
TP <- sum(pred_class == 1 & actual == 1) # 참 양성
TN <- sum(pred_class == 0 & actual == 0) # 참 음성
FP <- sum(pred_class == 1 & actual == 0) # 거짓 양성
FN <- sum(pred_class == 0 & actual == 1) # 거짓 음성
# 평가 지표 계산
accuracy <- (TP + TN) / (TP + TN + FP + FN)
sensitivity <- TP / (TP + FN) # 재현율
specificity <- TN / (TN + FP) # 특이도
c(accuracy, sensitivity, specificity)
}
# 모든 threshold에 대해 metrics_one 계산
results <- data.frame(
threshold = thresholds,
t(sapply(thresholds, function(t) metrics_one(t, actual, predicted)))
)
colnames(results) <- c("threshold", "Accuracy", "Sensitivity", "Specificity")
results
}
thresholds <- seq(0, 1, by = 0.01)
id_05 <- which(thresholds == 0.5)
age
, bmi
,
hp
를 설명변수로, diabetes
를 반응변수로 하는
모형을 생각합니다.model_logistic <- glm(
diabetes ~ age + bmi + hp,
data = hn19_test,
family = "binomial"
)
pred_logistic <- predict(model_logistic, newdata = hn19_test, type = "response")
calc_metrics()
함수로 threshold 변화 시
accuracy, sensitivity, specificity 를 계산합니다. 특히나, accuracy 에서
threshold = 0.5
에 해당하는 값이 우리가 일반적으로 생각하는
정확도입니다.metric_logistic <- calc_metrics(thresholds, hn19_test[["diabetes"]], pred_logistic)
cat("Logistic Accuracy:", round(metric_logistic[["Accuracy"]][id_05], 3), "\n")
## Logistic Accuracy: 0.756
metric_logistic_long <- metric_logistic %>%
pivot_longer(
cols = c("Accuracy", "Sensitivity", "Specificity"),
names_to = "Metric",
values_to = "Value"
)
ggplot(metric_logistic_long, aes(x = threshold, y = Value, color = Metric)) +
geom_line(size = 1.2) +
labs(title = "Accuracy / Sensitivity / Specificity", x = "Threshold", y = "Metric Value") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
pROC
패키지의 roc()
함수를 이용하면 됩니다.roc_obj <- roc(hn19_test[["diabetes"]], pred_logistic)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj, main = paste("ROC Curve (AUC =", round(roc_obj$auc, 3), ")"))
bmi
를 설명변수로,
diabetes
를 반응변수로 하는 로지스틱 회귀모형을
적합합니다.geom_line(aes(y = pred_logistic))
과 같은 형태로 할 수
있습니다.p <- ggplot(hn19, aes(x = bmi, y = diabetes)) +
geom_jitter(
color = "#6D9EC1",
height = 0.05,
width = 0,
alpha = 0.4
) +
labs(
title = "BMI vs 당뇨병",
x = "BMI",
y = "당뇨병"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
p
bmi
를 설명변수로, diabetes
를
반응변수로 하는 로지스틱 회귀모형을 적합합니다.model_logistic_bmi <- glm(
formula = diabetes ~ bmi,
data = hn19,
family = "binomial"
)
bmi
에 해당하는 격자를 grid_bmi
로
생성합니다.grid_bmi[["pred_logistic"]]
에 저장합니다.ggplot()
의 geom_line()
을 이용해 회귀곡선을
시각화합니다. grid_bmi
의 bmi
에 격자가,
pred_logistic
에 당뇨 확률이 계산돼 있으므로
aes(x = bmi, y = pred_logistic)
같은 식으로 코드를 작성하면
됩니다.grid_bmi <- data.frame(
bmi = seq(min(hn19[["bmi"]]), max(hn19[["bmi"]]), length.out = 100))
grid_bmi[["pred_logistic"]] <- predict(
model_logistic_bmi,
newdata = grid_bmi,
type = "response"
)
p <- p +
geom_line(data = grid_bmi, aes(x = bmi, y = pred_logistic),
color = "blue", size = 1.2)
p
bmi
를 설명변수로,
diabetes
를 반응변수로 하는 분류 결정나무를 적합합니다.
가지치기는 따로 안해도 됩니다.geom_line(aes(y = pred_tree))
과 같은 형태로 할 수
있습니다.p <- ggplot(hn19, aes(x = bmi, y = diabetes)) +
geom_jitter(
color = "#6D9EC1",
height = 0.05,
width = 0,
alpha = 0.4
) +
labs(
title = "BMI vs 당뇨병",
x = "BMI",
y = "당뇨병"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
p
bmi
를 설명변수로, diabetes
를
반응변수로 하는 분류 결정나무를 적합합니다.model_tree_bmi <- rpart(
diabetes_factor ~ bmi,
data = hn19,
method = "class"
)
grid_bmi
를 bmi
에 해당하는 격자로
활용합니다.grid_bmi[["pred_tree"]]
에 저장합니다.ggplot()
의 geom_line()
을 이용해 회귀곡선을
시각화합니다. grid_bmi
의 bmi
에 격자가,
pred_tree
에 당뇨 확률이 계산돼 있으므로
aes(x = bmi, y = pred_tree)
같은 식으로 코드를 작성하면
됩니다.grid_bmi[["pred_tree"]] <- predict(
model_tree_bmi,
newdata = grid_bmi,
type = "prob"
)[, 2]
p <- p +
geom_line(data = grid_bmi, aes(x = bmi, y = pred_tree),
color = "blue", size = 1.2)
p
age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 배깅 모형을
적합합니다.age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 배깅 모형을
적합합니다.model_bag_2d <- randomForest(
diabetes_factor ~ age + bmi,
data = hn19,
mtry = 2,
ntree = 100,
importance = TRUE
)
grid
에 생성합니다.pred_bag_2d
에
저장합니다.ggplot()
을 이용해 색상으로 시각화합니다.age_seq <- seq(min(hn19$age), max(hn19$age), length.out = 100)
bmi_seq <- seq(min(hn19$bmi), max(hn19$bmi), length.out = 100)
grid <- expand.grid(age = age_seq, bmi = bmi_seq)
pred_bag_2d <- predict(
model_bag_2d,
newdata = grid,
type = "prob"
)[, 2]
ggplot() +
geom_tile(
aes(
x = grid[["age"]],
y = grid[["bmi"]],
fill = pred_bag_2d
),
alpha = 0.4
) +
scale_fill_gradient(low = "lightblue", high = "red") +
labs(
title = "배깅의 당뇨병 예측 확률",
x = "나이 (Age)",
y = "BMI",
fill = "P(당뇨병=1)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 랜덤 포레스트 모형을
적합합니다.age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 랜덤 포레스트 모형을
적합합니다.model_rf_2d <- randomForest(
diabetes_factor ~ age + bmi,
data = hn19,
mtry = 1,
ntree = 100,
importance = TRUE
)
grid
를 격자로 활용합니다.pred_bag_2d
에
저장합니다.ggplot()
을 이용해 색상으로 시각화합니다.age_seq <- seq(min(hn19$age), max(hn19$age), length.out = 100)
bmi_seq <- seq(min(hn19$bmi), max(hn19$bmi), length.out = 100)
grid <- expand.grid(age = age_seq, bmi = bmi_seq)
pred_rf_2d <- predict(
model_rf_2d,
newdata = grid,
type = "prob"
)[, 2]
ggplot() +
geom_tile(
aes(
x = grid[["age"]],
y = grid[["bmi"]],
fill = pred_rf_2d
),
alpha = 0.4
) +
scale_fill_gradient(low = "lightblue", high = "red") +
labs(
title = "랜덤 포레스트의 당뇨병 예측 확률",
x = "나이 (Age)",
y = "BMI",
fill = "P(당뇨병=1)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 인공 신경망을
적합합니다.age
, bmi
를
설명변수로, diabetes
를 반응변수로 하는 인공 신경망을
적합합니다.model_nn_2d <- neuralnet(
diabetes ~ age + bmi,
data = hn19[1:100, ],
hidden = 2,
linear.output = FALSE
)
plot(model_nn_2d)
grid
를 격자로 활용합니다.pred_bag_2d
에
저장합니다.ggplot()
을 이용해 색상으로 시각화합니다.pred_nn_2d <- predict(
model_nn_2d,
newdata = grid,
type = "prob"
)
ggplot() +
geom_tile(
aes(
x = grid[["age"]],
y = grid[["bmi"]],
fill = pred_nn_2d
),
alpha = 0.4
) +
scale_fill_gradient(low = "lightblue", high = "red") +
labs(
title = "인공 신경망의 당뇨병 예측 확률",
x = "나이 (Age)",
y = "BMI",
fill = "P(당뇨병=1)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
age
, bmi
,
hp
를 설명변수로, diabetes
를 반응변수로 하는
모형을 생각합니다.caret
패키지의
createDataPartition()
함수를 사용합니다.set.seed(2025)
id_train <- createDataPartition(hn19[["diabetes"]], p = 0.1, list = FALSE)
hn19_train <- hn19[id_train, ]
hn19_test <- hn19[-id_train, ]
calc_metrics
함수를
가져옵니다.calc_metrics <- function(thresholds, actual, predicted) {
# threshold 하나에 대해 평가 지표를 계산
metrics_one <- function(threshold, actual, predicted) {
# 예측 확률이 threshold보다 크면 1(양성), 작으면 0(음성)
pred_class <- ifelse(predicted > threshold, 1, 0)
# 혼동 행렬 요소 계산
TP <- sum(pred_class == 1 & actual == 1) # 참 양성
TN <- sum(pred_class == 0 & actual == 0) # 참 음성
FP <- sum(pred_class == 1 & actual == 0) # 거짓 양성
FN <- sum(pred_class == 0 & actual == 1) # 거짓 음성
# 평가 지표 계산
accuracy <- (TP + TN) / (TP + TN + FP + FN)
sensitivity <- TP / (TP + FN) # 재현율
specificity <- TN / (TN + FP) # 특이도
c(accuracy, sensitivity, specificity)
}
# 모든 threshold에 대해 metrics_one 계산
results <- data.frame(
threshold = thresholds,
t(sapply(thresholds, function(t) metrics_one(t, actual, predicted)))
)
colnames(results) <- c("threshold", "Accuracy", "Sensitivity", "Specificity")
results
}
plot_metrics <- function(thresholds, actual, predicted) {
metric <- calc_metrics(thresholds, actual, predicted)
metric_long <- metric %>%
pivot_longer(
cols = c("Accuracy", "Sensitivity", "Specificity"),
names_to = "Metric",
values_to = "Value"
)
ggplot(metric_long, aes(x = threshold, y = Value, color = Metric)) +
geom_line(size = 1.2) +
labs(title = "Accuracy / Sensitivity / Specificity", x = "Threshold", y = "Metric Value") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
}
plot_roc <- function(actual, predicted) {
roc_obj <- roc(actual, predicted)
plot(roc_obj, main = paste("ROC Curve (AUC =", round(roc_obj[["auc"]], 3), ")"))
}
thresholds <- seq(0, 1, by = 0.01)
id_05 <- which(thresholds == 0.5)
model_tree <- rpart(
diabetes_factor ~ age + bmi + hp,
data = hn19,
method = "class"
)
pred_tree <- predict(model_tree, newdata = hn19_test, type = "prob")[, 2]
plot_metrics()
함수로 threshold 변화 시
accuracy, sensitivity, specificity 를 시각화합니다.plot_metrics(thresholds, hn19_test[["diabetes"]], pred_tree)
plot_roc(hn19_test[["diabetes"]], pred_tree)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
model_bag <- randomForest(
diabetes_factor ~ age + bmi + hp,
data = hn19_train,
mtry = 3,
ntree = 100,
importance = TRUE
)
pred_bag <- predict(model_bag, newdata = hn19_test, type = "prob")[, 2]
plot_metrics()
함수로 threshold 변화 시
accuracy, sensitivity, specificity 를 시각화합니다.plot_metrics(thresholds, hn19_test[["diabetes"]], pred_bag)
plot_roc(hn19_test[["diabetes"]], pred_bag)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
model_rf <- randomForest(
diabetes_factor ~ age + bmi + hp,
data = hn19_train,
mtry = 2,
ntree = 100,
importance = TRUE
)
pred_rf <- predict(model_rf, newdata = hn19_test, type = "prob")[, 2]
plot_metrics()
함수로 threshold 변화 시
accuracy, sensitivity, specificity 를 시각화합니다.plot_metrics(thresholds, hn19_test[["diabetes"]], pred_rf)
plot_roc(hn19_test[["diabetes"]], pred_rf)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
model_nn <- neuralnet(
diabetes ~ age + bmi + hp,
data = hn19_train,
hidden = 2,
linear.output = FALSE
)
pred_nn <- predict(model_nn, newdata = hn19_test, type = "prob")
plot_metrics()
함수로 threshold 변화 시
accuracy, sensitivity, specificity 를 시각화합니다.plot_metrics(thresholds, hn19_test[["diabetes"]], pred_nn)
plot_roc(hn19_test[["diabetes"]], pred_nn)
## Setting levels: control = 0, case = 1
## Warning in roc.default(actual, predicted): Deprecated use a matrix as
## predictor. Unexpected results may be produced, please pass a numeric vector.
## Setting direction: controls < cases