아이티윌_데이터 분석 55기/문제풀이_통계 및 분석

#16-2. 16일차 퀴즈에 대한 문제풀이

ecosso 2026. 5. 7. 17:32

# [ 연습문제 - Boston_na.csv 데이터에 대한 결측치 대치 ]

# 1. 데이터 로딩
df <- read.csv('Boston_na.csv')

# 변수 설명
# crim       범죄율
# zn          25,000 sq.ft 이상 주거지 비율
# indus     비소매 상업지구 비율
# chas      찰스강 인접 여부 (1=인접)
# nox        일산화질소 농도
# rm          평균 방 개수
# age        1940년 이전 건축 비율
# dis         고용센터까지 거리
# rad         고속도로 접근성
# tax         재산세율
# ptratio    학생/교사 비율
# black     흑인 인구 비율
# lstat       하위계층 비율
# medv     주택   중앙값 (단위: $1000)

# 컬럼별 결측치 수 확인
colSums(is.na(df))

# medv : 방이 많을수록 집값 높음 → 강한 양의 상관
# lstat : 하위계층 비율 높은 지역은 방 개수 적은 경향
# indus : 공업지구일수록 주거 환경(방 크기) 다름
# nox : 환경 수준이 주거 형태와 연관
# age : 오래된 건물일수록 방 구조 다름


# 2. 결측치 대치
# 1) 전체 평균으로 대치
# 2) chas 변수별 평균으로 대치
# 3) knn imputer

더보기

[ 내 답안 ]

 

df <- read.csv("Boston_na.csv")
head(df)

> head(df)
     crim zn indus chas   nox    rm  age    dis rad tax ptratio  black lstat medv
1 0.00632 18  2.31    0 0.538 6.575 65.2   4.09   1 296    15.3 396.90  4.98 24.0
2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14 21.6
3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03 34.7
4 0.03237  0  2.18    0 0.458 6.998 45.8 6.0622   3 222    18.7 394.63  2.94 33.4
5 0.06905  0  2.18    0 0.458 7.147 54.2 6.0622   3 222    18.7 396.90  5.33 36.2
6 0.02985  0  2.18    0 0.458 6.430 58.7          3 222    18.7 394.12  5.21 28.7


# 컬럼별 결측치 수 확인
colSums(is.na(df))

> colSums(is.na(df))
   crim      zn   indus    chas     nox      rm     age     dis     rad     tax ptratio   black   lstat    medv 
      0       0       0       0       0      13       0       0       0       0       0       0       0       0 

 

# 2. 결측치 대치
# 1) 전체 평균으로 대치

# 데이터 프레임 복사
df_imp1 <- df

# 전체 평균으로 대치 진행
total_rm <- mean(df$rm, na.rm=T)
df_imp1$rm[is.na(df_imp1$rm)] <- total_rm

sum(is.na(df_imp1$rm))


# 2) chas 변수별 평균으로 대치

library(plyr)
ddply(df_imp2, .(chas), summarise, mean_rm = mean(rm, na.rm=T))

df_imp2 <- ddply(df_imp2, .(chas), mutate, rm = ifelse(is.na(rm), mean(rm, na.rm=T), rm))
sum(is.na(df_imp2$rm))


# 3) knn imputer

# 데이터 프레임 복사
df_imp3 <- df

# 대치 진행
library(VIM)

df_imp3 <- kNN(df_imp3, variable = 'rm', dist_var = c('zn', 'age', 'tax', 'ptratio', 'lstat', 'medv'), k=5, imp_var=F)
sum(is.na(df_imp3$rm))


# 데이터 프레임 복사
df_imp4 <- df

# 대치 진행 (예제문제에 제시된 변수)
library(VIM)

df_imp4 <- kNN(df_imp4, variable = 'rm', dist_var = c('medv', 'lstat', 'indus', 'nox', 'age'), k=5, imp_var=F)
sum(is.na(df_imp4$rm))


# 성능 비교
library(randomForest)

# randomForest 학습을 위한 종속변수의 factor 타입 변경(필수)
df_imp1$rm <- factor(df_imp1$rm)
df_imp2$rm <- factor(df_imp2$rm)
df_imp3$rm <- factor(df_imp3$rm)
df_imp4$rm <- factor(df_imp4$rm)

# 학습
set.seed(0)
rf1 <- randomForest(rm ~ ., data = df_imp1, ntree = 100)

set.seed(0)
rf2 <- randomForest(rm ~ ., data = df_imp2, ntree = 100)

set.seed(0)
rf3 <- randomForest(rm ~ ., data = df_imp3, ntree = 100)

set.seed(0)
rf4 <- randomForest(rm ~ ., data = df_imp4, ntree = 100)

# accuracy
sum(df_imp1$rm == rf1$predicted) / nrow(df) * 100
sum(df_imp2$rm == rf2$predicted) / nrow(df) * 100
sum(df_imp3$rm == rf3$predicted) / nrow(df) * 100
sum(df_imp4$rm == rf4$predicted) / nrow(df) * 100

> sum(df_imp1$rm == rf1$predicted) / nrow(df) * 100
[1] 0.1976285
> sum(df_imp2$rm == rf2$predicted) / nrow(df) * 100
[1] 0.1976285
> sum(df_imp3$rm == rf3$predicted) / nrow(df) * 100
[1] 1.581028
> sum(df_imp4$rm == rf4$predicted) / nrow(df) * 100
[1] 0.5928854

 

# OOB accuracy 비교
data.frame(
  방법 = c("전체평균 대치", "그룹평균 대치", "모든변수 KNN", "관련변수 KNN"),
  Accuracy = round(c(1 - rf1$err.rate[100, "OOB"],
                     1 - rf2$err.rate[100, "OOB"],
                     1 - rf3$err.rate[100, "OOB"],
                     1 - rf4$err.rate[100, "OOB"]), 4)
)

> # OOB accuracy 비교
> data.frame(
+   방법 = c("전체평균 대치", "그룹평균 대치", "모든변수 KNN", "관련변수 KNN"),
+   Accuracy = round(c(1 - rf1$err.rate[100, "OOB"],
+                      1 - rf2$err.rate[100, "OOB"],
+                      1 - rf3$err.rate[100, "OOB"],
+                      1 - rf4$err.rate[100, "OOB"]), 4)
+ )
           방법 Accuracy
1 전체평균 대치   0.0020
2 그룹평균 대치   0.0020
3  모든변수 KNN   0.0158
4  관련변수 KNN   0.0059

 

# ** 반복 평가 점수 확인
n_iter <- 30
result <- data.frame(iter = 1:n_iter, imp1 = NA, imp2 = NA, imp3 = NA, imp4 = NA)

for (i in 1:n_iter) {
  set.seed(i)
  rf1 <- randomForest(rm ~ ., data = df_imp1, ntree = 100)
  rf2 <- randomForest(rm ~ ., data = df_imp2, ntree = 100)
  rf3 <- randomForest(rm ~ ., data = df_imp3, ntree = 100)
  rf4 <- randomForest(rm ~ ., data = df_imp4, ntree = 100)
  
  result$imp1[i] <- 1 - rf1$err.rate[100, 'OOB']
  result$imp2[i] <- 1 - rf2$err.rate[100, 'OOB']
  result$imp3[i] <- 1 - rf3$err.rate[100, 'OOB']
  result$imp4[i] <- 1 - rf4$err.rate[100, 'OOB']
}



result

colMeans(result[,-1])

> colMeans(result[,-1])
        imp1         imp2         imp3         imp4 
0.0009881423 0.0005270092 0.0160079051 0.0070487484 

 

[ 문제풀이 ]

 

# 1. 데이터 로딩
df <- read.csv('Boston_na.csv')

# 변수 설명
# crim       범죄율
# zn         25,000 sq.ft 이상 주거지 비율
# indus      비소매 상업지구 비율
# chas       찰스강 인접 여부 (1=인접)
# nox        일산화질소 농도
# rm         평균 방 개수
# age        1940년 이전 건축 비율
# dis        고용센터까지 거리
# rad        고속도로 접근성
# tax        재산세율
# ptratio    학생/교사 비율
# black      흑인 인구 비율
# lstat      하위계층 비율
# medv주택   중앙값 (단위: $1000)

# 컬럼별 결측치 수 확인
colSums(is.na(df))

# medv : 방이 많을수록 집값 높음 → 강한 양의 상관
# lstat : 하위계층 비율 높은 지역은 방 개수 적은 경향
# indus : 공업지구일수록 주거 환경(방 크기) 다름
# nox : 환경 수준이 주거 형태와 연관
# age : 오래된 건물일수록 방 구조 다름


# 2. 결측치 대치
# 1) 전체 평균으로 대치
df_imp1 <- df
df_imp1$rm[is.na(df_imp1$rm)] <- mean(df_imp1$rm, na.rm = T)
sum(is.na(df_imp1$rm))

# 2) chas 변수별 평균으로 대치
library(plyr)
df_imp2 <- df
df_imp2 <- ddply(df_imp2, .(chas), mutate, 
                 rm = ifelse(is.na(rm), mean(rm, na.rm = T), rm))
sum(is.na(df_imp2$rm)) 

# 3) knn imputer
library(VIM)

# 3-1) 모든 컬럼 사용
X <- df[,-ncol(df)]
df_imp3 <- kNN(X, variable = 'rm', dist_var = colnames(X), k = 5, imp_var = F)
df_imp3$medv <- df$medv
sum(is.na(df_imp3$income))


# 3-2) 필요 컬럼 사용
df_imp4 <- kNN(X, variable = 'rm', 
               dist_var = c('lstat', 'indus', 'nox', 'age'), k = 5, imp_var = F)
df_imp4$medv <- df$medv
sum(is.na(df_imp4$income))



# 3. 성능비교
library(randomForest)
sum(is.na(df_imp2$rm))
# 학습
set.seed(0)
rf1 <- randomForest(medv ~ ., data = df_imp1, ntree = 100)
rf2 <- randomForest(medv ~ ., data = df_imp2, ntree = 100)
rf3 <- randomForest(medv ~ ., data = df_imp3, ntree = 100)
rf4 <- randomForest(medv ~ ., data = df_imp4, ntree = 100)

# mse
library(Metrics)
mse(df$medv, rf1$predicted)    # 12.30664
mse(df$medv, rf2$predicted)    # 131.8262
mse(df$medv, rf3$predicted)    # 12.65105
mse(df$medv, rf4$predicted)    # 10.67851