1-1 데이터 로드 및 초기 설정
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
setwd("C:/Users/kyeeu/OneDrive/Desktop")
bike_data <- read.csv("day.csv")
raw_data <- read.csv("day.csv")
raw_data <- raw_data[-1]
bike_data <- raw_data[1:80, c("temp", "atemp", "hum", "windspeed", "cnt")]
rsample 패키지를 사용해서 training set과 testing set으로 나눕니다.
#install.packages("rsample")
library(rsample)
##
## Attaching package: 'rsample'
## The following object is masked from 'package:caret':
##
## calibration
set.seed(1)
bike_split <- initial_split(bike_data, prop = 0.7, strata = "cnt")
bike_train <- training(bike_split)
bike_test <- testing(bike_split)
두 set에서의 target 변수의 분포를 비교
par(mfrow=c(1,2))
hist(bike_train$cnt, main="Training Set", xlab="cnt", col="lightblue")
hist(bike_test$cnt, main="Test Set", xlab="cnt", col="orange")
경우 1) repeats = 1
cv_1 <- trainControl(method = "repeatedcv", number = 5, repeats = 1)
tune_grid <- expand.grid(k = seq(1, 39, 2))
set.seed(1)
knn_fit_1 <- train(cnt ~ ., data = bike_train, method = "knn",
trControl = cv_1, tuneGrid = tune_grid,
preProcess = c("center", "scale"))
# 그래프 확인
plot(knn_fit_1, main = "k-NN RMSE (Repeats = 1)")
knn_fit_1$bestTune
## k
## 4 7
경우 2) repeats = 5
cv_5 <- trainControl(method = "repeatedcv", number = 5, repeats = 5)
tune_grid <- expand.grid(k = seq(1, 39, 2))
set.seed(1)
knn_fit_5 <- train(cnt ~ ., data = bike_train, method = "knn",
trControl = cv_5, tuneGrid = tune_grid,
preProcess = c("center", "scale"))
# 그래프 확인
plot(knn_fit_5, main = "k-NN RMSE (Repeats = 5)")
knn_fit_5$bestTune
## k
## 5 9
경우 3) repeats = 10
cv_10 <- trainControl(method = "repeatedcv", number = 5, repeats = 10)
tune_grid <- expand.grid(k = seq(1, 39, 2))
set.seed(1)
knn_fit_10 <- train(cnt ~ ., data = bike_train, method = "knn",
trControl = cv_10, tuneGrid = tune_grid,
preProcess = c("center", "scale"))
# 그래프 확인
plot(knn_fit_10, main = "k-NN RMSE (Repeats = 10)")
knn_fit_10$bestTune
## k
## 4 7
경우 4) repeats = 20
cv_20 <- trainControl(method = "repeatedcv", number = 5, repeats = 20)
tune_grid <- expand.grid(k = seq(1, 39, 2))
set.seed(1)
knn_fit_20 <- train(cnt ~ ., data = bike_train, method = "knn",
trControl = cv_20, tuneGrid = tune_grid,
preProcess = c("center", "scale"))
# 그래프 확인
plot(knn_fit_20, main = "k-NN RMSE (Repeats = 20)")
knn_fit_20$bestTune
## k
## 5 9
경우 5) repeats = 30
cv_30 <- trainControl(method = "repeatedcv", number = 5, repeats = 30)
tune_grid <- expand.grid(k = seq(1, 39, 2))
set.seed(1)
knn_fit_30 <- train(cnt ~ ., data = bike_train, method = "knn",
trControl = cv_30, tuneGrid = tune_grid,
preProcess = c("center", "scale"))
# 그래프 확인
plot(knn_fit_30, main = "k-NN RMSE (Repeats = 30)")
knn_fit_30$bestTune
## k
## 5 9
각 코드 청크를 실행할 때마다 그래프 모양과 Best k가 어떻게 변하는 지 확인한 결과, repeats=1일 때는 그래프가 뾰족하거나 거칠어 보이지만, 30으로 갈수록 곡선이 매끄러워집니다.
# 각 실험별로 가장 좋았던(최소) RMSE와 그때의 k값 모으기
comparison_summary <- data.frame(
반복횟수 = c(1, 5, 10, 20, 30),
최적_k = c(knn_fit_1$bestTune$k,
knn_fit_5$bestTune$k,
knn_fit_10$bestTune$k,
knn_fit_20$bestTune$k,
knn_fit_30$bestTune$k),
최소_RMSE = c(min(knn_fit_1$results$RMSE),
min(knn_fit_5$results$RMSE),
min(knn_fit_10$results$RMSE),
min(knn_fit_20$results$RMSE),
min(knn_fit_30$results$RMSE))
)
# 표 출력
print(comparison_summary)
## 반복횟수 최적_k 최소_RMSE
## 1 1 7 450.1220
## 2 5 9 442.4757
## 3 10 7 441.7672
## 4 20 9 436.6678
## 5 30 9 436.3938
각 경우에 따라 Best K값과 RMSE값을 표시하였습니다. 반복 횟수가 증가함에 따라 RMSE가 450.12에서 436.39로 줄어들며 안정화되었습니다. 따라서 통계적으로 가장 낮은 오차와 높은 신뢰도를 보여준 k=9를 본 데이터셋의 최종 최적 파라미터로 결정하였습니다.
앞서 만든 cv_30(5-fold, 30 repeats) 설정을 그대로 사용하여 선형 회귀 모델을 학습시켰습니다.
set.seed(1)
lm_fit <- train(cnt ~ temp + atemp + hum + windspeed,
data = bike_train,
method = "lm",
trControl = cv_30)
summary(lm_fit$finalModel)
##
## Call:
## lm(formula = .outcome ~ ., data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1093.38 -188.02 61.64 234.20 727.77
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2631.2 357.2 7.365 1.44e-09 ***
## temp 19816.2 5681.8 3.488 0.00101 **
## atemp -15421.0 5714.7 -2.698 0.00942 **
## hum -1947.3 337.4 -5.771 4.67e-07 ***
## windspeed -4542.5 1028.3 -4.418 5.22e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 349.2 on 51 degrees of freedom
## Multiple R-squared: 0.6176, Adjusted R-squared: 0.5876
## F-statistic: 20.59 on 4 and 51 DF, p-value: 3.783e-10
결과요약을 확인해보면, 모든 독립변수(temp, atemp, hum, windspeed)의 p-value가 0.01 미만으로 매우 낮습니다. 이는 모든 변수가 자전거 대여량(cnt)을 예측하는 데 통계적으로 유의미한 영향을 미침을 의미합니다.
F-statistic에 대한 p-value가 3.783e-10으로 매우 작으므로, 이 회귀 모형은 매우 적합하다고 할 수 있습니다.
결론적으로
k-NN 모델 (k=9)의 CV RMSE: 436.39
선형 회귀 모델의 Residual Standard Error: 349.2
RMSE(오차)를 비교하면 선형 회귀 모델이 더 작은 값을 가지므로, k-NN보다 더 좋은 예측 성능을 보인다고 할 수 있습니다.
4-1. k-NN 모델(k=9)을 Test set에 적용하여 예측 및 RMSE 계산
knn_pred <- predict(knn_fit_30, newdata = bike_test)
knn_test_rmse <- sqrt(mean((knn_pred - bike_test$cnt)^2))
4-2. 선형 회귀 모델을 Test set에 적용하여 예측 및 RMSE 계산
lm_pred <- predict(lm_fit, newdata = bike_test)
lm_test_rmse <- sqrt(mean((lm_pred - bike_test$cnt)^2))
4-3. 결과 확인
results_comp <- data.frame(
Model = c("k-NN", "Linear Regression"),
CV_RMSE = c(min(knn_fit_30$results$RMSE), lm_fit$results$RMSE),
Test_RMSE = c(knn_test_rmse, lm_test_rmse)
)
print(results_comp)
## Model CV_RMSE Test_RMSE
## 1 k-NN 436.3938 505.9265
## 2 Linear Regression 358.8649 574.0495
분석 결과, 선형 회귀 모델은 CV RMSE(358.86) 기준으로는 k-NN보다 우수하여 훈련 데이터에 대한 설명력이 높았으나 , 실제 Test set에 적용했을 때는 k-NN(k=9) 모델의 RMSE(505.93)가 선형 회귀(574.05)보다 낮게 나타났습니다. 이는 선형 회귀 모델이 훈련 데이터에 다소 과적합되어 새로운 데이터에 대한 일반화 성능이 떨어진 반면, k-NN 모델은 상대적으로 안정적인 예측력을 유지했음을 의미합니다.
결론적으로 모델 선택은 CV 기준을 따르되, 최종적인 실제 데이터 예측 성능 면에서는 k-NN 모델이 더 우수함을 확인하였습니다.
강의 시간에 학습한 내용을 바탕으로, 스스로 하나의 분석 질문을 자유롭게 설정하고, 현재 데이터셋을 활용한 실습을 통해 이를 분석해보자.
(분석 질문 정의 -> 분석 방법 선택 -> 분석 결과 제시 -> 결과 해석)
“실제 온도(temp)와 체감 온도(atemp) 중 자전거 대여량(cnt)에 더 강력한 영향을 미치는 요소는 무엇인가?”
자전거의 사용량은 기상에도 어느 정도 영향을 받습니다. 이에 따라 온도와 자전거 대여량의 상관관계를 알아보려고 합니다. 나아가, 체감온도 데이터까지 사용하여 실제 온도와 체감 온도 중 어떤 요소가 더 영향력이 있는지 알아보고자 합니다.
Lec05 수업자료 12, 14페이지지에 나와있는 lm() 분석 방식을 활용하여 두 변수의 영향력을 비교하겠습니다.
# 온도와 체감온도만 사용한 회귀 모델 생성
temp_comp_model <- lm(cnt ~ temp + atemp, data = bike_train)
# 결과 확인
summary(temp_comp_model)
##
## Call:
## lm(formula = cnt ~ temp + atemp, data = bike_train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1370.3 -199.5 105.1 285.8 825.5
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 719.9 209.9 3.430 0.00118 **
## temp 3869.5 4088.2 0.947 0.34819
## atemp -550.3 4324.0 -0.127 0.89922
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 455.6 on 53 degrees of freedom
## Multiple R-squared: 0.3237, Adjusted R-squared: 0.2981
## F-statistic: 12.68 on 2 and 53 DF, p-value: 3.157e-05
lm() 함수를 이용하여 두 온도 변수를 독립변수로 설정한 다중 회귀 모델을 생성하고, 각 변수의 계수와 p-value를 검토했습니다
# 두 변수의 관계 시각화 (산점도 행렬)
pairs(~ cnt + temp + atemp, data = bike_train, main = "Correlation between Temp, Atemp and Cnt")
pairs() 함수를 활용하여 각 변수 간의 산점도 행렬을 생성하고, 변수 간의
선형 관계 강도를 시각화했습니다.
시각화 결과, 실제 온도와 체감 온도는 서로 매우 강한 양의 상관관계를 보이며 이는 상식적으로 당연한 결과입니다. 또한 두 변수 모두 온도가 상승함에 따라 자전거 대여량도 함께 증가하는 뚜렷한 우상향 선형 추세를 보입니다.
회귀 분석 결과, 실제 온도(temp)의 계수값(3869.5)이 체감 온도(atemp)의 계수값(-550.3)보다 압도적으로 크게 나타났습니다.실제 온도의 p-value(0.348)가 체감 온도의 p-value(0.899)보다 낮게 측정되어, 상대적으로 실제 온도가 대여량 변동을 더 많이 설명하는 경향이 있습니다.
결론적으로, 자전거 대여량과 더 높은 상관관계를 가지며 예측에 더 주도적인 역할을 하는 변수는 실제 온도(temp)인 것으로 판단됩니다. 비록 두 지표가 유사한 흐름을 보이지만, 사용자의 대여 행동에는 체감 수치보다 객관적인 기온 지표가 더 민감하게 작용함을 확인하였습니다.