091

[R] 데이터 전처리 본문

Programming Language/R

[R] 데이터 전처리

공구일 2026. 4. 26. 01:48
728x90

1. 결측값과 특이값

- 결측값이란 데이터를 수집하고 저장하는 과정에서 저장할 값을 얻지 못하는 경우에 발생하는 값으로, R에서는 NA(Not Available)로 표현합니다. 결측값을 제거, 제외하거나 적당한 값으로 치환하는 방식으로 데이터를 전처리합니다.

z <- c(1,2,3,NA,5,NA,8)
sum(z) #[1] NA
is.na(z) #[1] FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE
z[is.na(z)] <- 0
sum(z) #[1] 19

z2 <- c(5,8,1,NA,3,NA,7)
sum(is.na(z2)) #[1] 2(NA의 개수)

-> 결측값이 있는 상태로, 백터의 연산을 시도하면 NA가 출력됩니다. NA를 0으로 치환하여 준 뒤에 다시 합을 해줘야 원하는 값을 받을 수 있습니다. 

-> 결측값을 찾을 때는 is.na() 함수를 통해 개수를 해당 백터의 요소가 NA인지에 대한 참/거짓을 출력하고 이를 sum() 함수를 통해 개수를 받을 수 있습니다.

x <- c(1,2,3)
y <- c(4,5,6)
y[3] <- NA
xy1 <- data.frame(x,y)
xy1
#   x  y
# 1 1  4
# 2 2  5
# 3 3 NA
col_na <- function(y) {
  return(sum(is.na(y)))
}

na_count <- apply(xy1,1,FUN=col_na)
na_count #[1] 0 0 1

-> 직접 만든 함수인 NA의 개수를 반환하는 함수인 col_na를 사용하여 na_count의 개수를 반환하는 로직으로 apply의 방향이 1이기 때문에 같은 행 중에서 NA의 개수를 반환합니다.(3번째 행에 NA가 있으니까 3번째 요소에서만 1이 반환됩니다.)

name <- c("Alice","Bob",NA,"Diana","Eve")
score <- c(85,NA,90,NA,78)
grade <- c("A","B","A",NA,"C")

student_df <- data.frame(name,score,grade)
student_df
#   name score grade
# 1 Alice    85     A
# 2   Bob    NA     B
# 3  <NA>    90     A
# 4 Diana    11  <NA>
# 5   Eve    78     C

check_na <- function(z){
  return(which(is.na(z)))
}
na_pos <- apply(student_df,2,FUN=check_na)
na_pos
# $name
# [1] 3
# 
# $score
# [1] 2 4
# 
# $grade
# [1] 4

# name score grade 
# 3     2     4

-> 각 열마다 NA의 개수가 다르다면 apply는 결과물들의 길이가 달라서 맞춰 출력할 수 없기 때문에 list로 반환합니다. 만약 score의 NA를 숫자로 치환하여 재출력을 해보면 다시 벡터처럼 출력됩니다.

head(xy1)
#   x  y
# 1 1  4
# 2 2  5
# 3 3 NA
xy1[!complete.cases(xy1),]
#   x  y
# 3 3 NA
y_c <- xy1[complete.cases(xy1),]
y_c
#   x y
# 1 1 4
# 2 2 5

-> complete.cases()는 NA를 행 단위로 검사하는 함수입니다. is.na()와의 가장 큰 차이는 아래처럼 행 단위/셀 단위입니다.

complete.cases(xy1) #[1]  TRUE  TRUE FALSE
is.na(xy1)
#          x     y
# [1,] FALSE FALSE
# [2,] FALSE FALSE
# [3,] FALSE  TRUE

 

- 특이값은 정상적이라고 생각되는 데이터 분포 범위 밖에 위치한 값들로, 이상치라고도 합니다. 

region_data <- data.frame(
  Region=c("A","B","C","D","E"),
  Area=c(120,350,200,180,1500),
  Population=c(80,120,75,95,300)
)

region_data
#   Region Area Population
# 1      A  120         80
# 2      B  350        120
# 3      C  200         75
# 4      D  180         95
# 5      E 1500        300

boxplot(region_data$Area)
boxplot.stats(region_data$Area)$out #[1] 1500

out.val <- boxplot.stats(region_data$Area)$out
region_data$Area[region_data$Area %in% out.val] <- NA
head(region_data)
#   Region Area Population
# 1      A  120         80
# 2      B  350        120
# 3      C  200         75
# 4      D  180         95
# 5      E   NA        300

newdata <- region_data$Area[!is.na(region_data$Area)]
head(newdata) #[1] 120 350 200 180

-> 이상치를 구하여 NA 값으로 변환 후 newdata에다가 NA가 아닌 값만 담아주는 코드로 이상치를 제거해주는 로직입니다.

-> boxplot.stats에는 아래처럼 $stats, $n, $conf, $out 4가지 결과값이 있습니다. 위에서 접근한대로 $out에 접근하면 이상치값이며, 나머지 세개는 순서대로 각각 핵심지점(아래쪽 수염의 끝, Q1, Q2, Q3, 위쪽 수염의 끝), 데이터의 총 개수, 신뢰구간을 나타냅니다.

-> %in%는 포함 여부 확인용 연산자로, region_data$Area가 out.val에 포함된 값을 찾아내는 함수입니다. ==는 단 하나의 값을 비교할 때만 사용하기 때문에 여러 개의 후보 중 해당되는지를 볼 때는 %in%을 사용해야합니다.

boxplot.stats(region_data$Area)
# $stats
# [1] 120 180 200 350 350
# 
# $n
# [1] 5
# 
# $conf
# [1]  79.87843 320.12157
# 
# $out
# [1] 1500

 

2. 데이터 분리 및 샘플링

- 데이터를 분리할 때는 split()이라는 함수를 사용합니다. split()은 데이터를 그룹별로 쪼개어 리스트 형태로 반환합니다.

student_data <- data.frame(
  Name = c("Alex","Brian","Cindy","David","Emma","Frank"),
  Gender = c("M","M","F","M","F","M"),
  Score = c(85,92,78,64,91,75)
)

student_data
#     Name Gender Score
# 1  Alex      M    85
# 2 Brian      M    92
# 3 Cindy      F    78
# 4 David      M    64
# 5  Emma      F    91
# 6 Frank      M    75

sp <- split(student_data,student_data$Gender)#Gender를 기준으로 쪼개기
sp
# $F
#    Name Gender Score
# 3 Cindy      F    78
# 5  Emma      F    91
# 
# $M
#    Name Gender Score
# 1  Alex      M    85
# 2 Brian      M    92
# 4 David      M    64
# 6 Frank      M    75

head(sp$F)
#    Name Gender Score
# 3 Cindy      F    78
# 5  Emma      F    91

head(sp$M)
#    Name Gender Score
# 1  Alex      M    85
# 2 Brian      M    92
# 4 David      M    64
# 6 Frank      M    75

-> 리스트로 반환하기 때문에 아래처럼 클래스가 반환합니다.

x <- data.frame(tel=c('02','031','032','031'),local1=c(3,3,3,4))
x
sp <- split(x,x$tel)
sp$`031`

-> 리스트의 이름은 변수처럼 이용됩니다. 이때 변수는 숫자로 시작할 수가 없기 때문에 백틱으로 감싸서 사용합니다.

class(sp) #[1] "list"
class(sp$M) #[1] "data.frame"

 

- 샘플링한다는 것은 주어진 값들이 있을 때, 그 중에서 임의의 개수의 값들을 추출하는 작업을 말합니다. sample()함수를 통해 원하는 샘플 추출할 수 있습니다. replace가 TRUE일 때는 중복값을 허용하는 것입니다. 그러기 때문에 size가 자유로울 수 있지만, replace=FALSE인 경우에는 size가 주어진 값보다 같거나 작아야합니다.

x <- c(1:100)
y <- sample(x,size=10,replace=F)
y #[1] 28  1 19 20  5 58 77  8 29 66

sample(1:20,size=5,replace=F) #[1]  8  4  9 11  6
sample(1:20,size=5,replace=T) #[1]  4 16  6  8 10
sample(1:20,size=5,replace=T) #[1] 10 16 16 12 13

 

3. 데이터 집계와 병합

- 2차원 데이터는 데이터 그룹에 대해서 합계나 평균을 계산해야하는 일이 많고 이와 같은 작업을 집계라고 합니다. aggregate() 함수를 통해 구현합니다.

aggregate(계산할값 ~ 기준값, data = 데이터셋, FUN = 함수)
simple_data <- data.frame(
  Group = c("A","A","B","B"),
  Val1 = c(5,7,10,14)
)

simple_data
#   Group Val1
# 1     A    5
# 2     A    7
# 3     B   10
# 4     B   14

agg <- aggregate(Val1 ~ Group, data=simple_data, FUN=sd)
agg
#   Group     Val1
# 1     A 1.414214
# 2     B 2.828427

-> sd()함수를 넣어 표준편차를 구해줍니다. 

 

- 병합이란 분리된 데이터 파일을 공동 열을 기준으로 하나로 합치는 작업을 말합니다. merge() 함수를 통해 구현합니다.

x <- data.frame(name=c("a","b","c"),math=c(90,80,40))
y <- data.frame(name=c("a","b","d"),korean=c(75,60,90))
x
#   name math
# 1    a   90
# 2    b   80
# 3    c   40
y
#   name korean
# 1    a     75
# 2    b     60
# 3    d     90

z <- merge(x,y,by=c("name"))
z
#   name math korean
# 1    a   90     75
# 2    b   80     60

-> merge() 함수는 기본적으로 inner join이기 때문에, 양쪽 표에 모두 존재하는 이름만 합쳐서 c,d에 대한 값은 소실됩니다.

728x90