문제1

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")

문제2

경우 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를 본 데이터셋의 최종 최적 파라미터로 결정하였습니다.

문제 3

앞서 만든 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으로 매우 작으므로, 이 회귀 모형은 매우 적합하다고 할 수 있습니다.

결론적으로

  1. k-NN 모델 (k=9)의 CV RMSE: 436.39

  2. 선형 회귀 모델의 Residual Standard Error: 349.2

RMSE(오차)를 비교하면 선형 회귀 모델이 더 작은 값을 가지므로, k-NN보다 더 좋은 예측 성능을 보인다고 할 수 있습니다.

문제 4

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)인 것으로 판단됩니다. 비록 두 지표가 유사한 흐름을 보이지만, 사용자의 대여 행동에는 체감 수치보다 객관적인 기온 지표가 더 민감하게 작용함을 확인하였습니다.