트위티의 열하일기
3. 데이터 가공, 정제하기 본문
# 데이터 가공하기
데이터를 추출하고, 여러 데이터를 합치기 위해 데이터 전처리 작업에 가장 많이 사용되는 R 패키지, dplyr을 사용한다.
1. filter() 함수로 원하는 데이터 추출
# exam 에서 class가 1인 경우만 추출해서 출력
exam %>% filter(class == 1)
# 결과
id class math english science
1 1 1 50 98 50
2 2 1 60 97 60
3 3 1 45 86 78
4 4 1 30 98 58
- %>% 기호를 활용하여 함수들을 나열하는 방식으로 코드 작성
- filter() 에 조건을 입력하면 조건에 해당되는 행만 추출
- 특정 조건에 부합하는 데이터만 추출하기
exam %>% filter(math > 50)
#결과
id class math english science
1 2 1 60 97 60
2 7 2 80 90 45
3 8 2 90 78 25
4 11 3 65 65 65
5 15 4 75 56 78
6 16 4 58 98 65
7 17 5 65 68 98
8 18 5 80 78 90
9 19 5 89 68 87
10 20 5 78 83 58
- 여러 조건을 충족하는 행 추출하기
exam %>% filter(class == 1 & math >= 50)
# 결과
id class math english science
1 1 1 50 98 50
2 2 1 60 97 60
exam %>% filter(math >=90 | english >= 90)
# 결과
id class math english science
1 1 1 50 98 50
2 2 1 60 97 60
3 4 1 30 98 58
4 7 2 80 90 45
5 8 2 90 78 25
6 9 3 20 98 15
7 10 3 50 98 45
8 13 4 46 98 65
9 16 4 58 98 65
- %in% 기호: 변수의값이 지정한 조건 목록에 해당하는지 확인하는 기능
exam %>% filter(class %in% c(1, 3, 5)) # 1, 3, 5반에 해당하면 추출
# 결과
id class math english science
1 1 1 50 98 50
2 2 1 60 97 60
3 3 1 45 86 78
4 4 1 30 98 58
5 9 3 20 98 15
6 10 3 50 98 45
7 11 3 65 65 65
8 12 3 45 85 32
9 17 5 65 68 98
10 18 5 80 78 90
11 19 5 89 68 87
12 20 5 78 83 58
- 추출한 행으로 새로운 데이터 만들기 (using '<-')
class1 <- exam %>% filter(class == 1)
class2 <- exam %>% filter(class == 2)
# class1
id class math english science
1 1 1 50 98 50
2 2 1 60 97 60
3 3 1 45 86 78
4 4 1 30 98 58
#class2
id class math english science
1 5 2 25 80 65
2 6 2 50 89 98
3 7 2 80 90 45
4 8 2 90 78 25
mean(class1$math)
## [1] 46.25
mean(class2$math)
## [1] 61.25
2. select() 함수로 데이터 속 수많은 변수중 일부 변수만 추출해서 사용
exam %>% select(math) %>%
head # 6개만 출력
#결과
math
1 50
2 60
3 45
4 30
5 25
6 50
- 여러 변수 추출하기
exam %>% select(class, math, english)
- 변수 제외하기, 여러 변수 제외하기
exam %>% select(-math) # math를 제외한 나머지 모든 변수 추출
3. '%>%' 기호를 활용하여 dplyr의 함수들 조합
# class가 1인 행만 추출한 다음 english 추출
exam %>% filter(class==1) %>%
select(english)
# 결과
english
1 98
2 97
3 86
4 98
*%>%로 코드가 연결되는 부분에서 줄을 바꾸면 함수별로 구분되기 때문에 가독성 있는 코드 구현 가능
4. arrange() 함수를 활용하여 원하는 순서대로 정렬하기
- 오름차순으로 정렬 (default)
exam %>% arrange(math) # math 점수가 낮은 사람에서 높은 사람 순으로 오름차순 정렬
# 결과
id class math english science
1 9 3 20 98 15
2 5 2 25 80 65
3 4 1 30 98 58
4 3 1 45 86 78
5 12 3 45 85 32
6 13 4 46 98 65
7 14 4 48 87 12
8 1 1 50 98 50
9 6 2 50 89 98
10 10 3 50 98 45
11 16 4 58 98 65
12 2 1 60 97 60
13 11 3 65 65 65
14 17 5 65 68 98
15 15 4 75 56 78
16 20 5 78 83 58
17 7 2 80 90 45
18 18 5 80 78 90
19 19 5 89 68 87
20 8 2 90 78 25
- 내림차순으로 정렬: desc() 에 적용
exam %>% arrange(desc(math)) # math 내림차순 정렬
#결과
id class math english science
1 8 2 90 78 25
2 19 5 89 68 87
3 7 2 80 90 45
4 18 5 80 78 90
5 20 5 78 83 58
6 15 4 75 56 78
7 11 3 65 65 65
8 17 5 65 68 98
9 2 1 60 97 60
10 16 4 58 98 65
11 1 1 50 98 50
12 6 2 50 89 98
13 10 3 50 98 45
14 14 4 48 87 12
15 13 4 46 98 65
16 3 1 45 86 78
17 12 3 45 85 32
18 4 1 30 98 58
19 5 2 25 80 65
20 9 3 20 98 15
- 정렬 기준으로 삼을 변수 여러 개 지정하기
# 먼저 class를 기준으로 오름차순 정렬한 후 math 점수를 기준으로 오름차순 정렬해서 출력
exam %>% arrange(class, math) %>%
head
# 결과
> exam %>% arrange(class, math)
id class math english science
1 4 1 30 98 58
2 3 1 45 86 78
3 1 1 50 98 50
4 2 1 60 97 60
5 5 2 25 80 65
6 6 2 50 89 98
5. mutate()함수를 사용하여 파생변수 만들어 추가
# total이라는 총합 변수를 추가
exam %>% mutate(total = math + english + science) %>%
head
# 결과
id class math english science total
1 1 1 50 98 50 198
2 2 1 60 97 60 217
3 3 1 45 86 78 209
4 4 1 30 98 58 186
5 5 2 25 80 65 170
6 6 2 50 89 98 237
exam %>% mutate(total = math + english + science) %>% # total 변수 추가
arrange(total) %>% # total 변수를 기준으로 정렬
head
# 결과
id class math english science total
1 9 3 20 98 15 133
2 14 4 48 87 12 147
3 12 3 45 85 32 162
4 5 2 25 80 65 170
5 4 1 30 98 58 186
6 8 2 90 78 25 193
- 여러 파생변수 한 번에 추가하기
exam %>% mutate(total = math + english + science,
mean = (math + english + science)/3) %>%
head
# 결과
id class math english science total mean
1 1 1 50 98 50 198 66.00000
2 2 1 60 97 60 217 72.33333
3 3 1 45 86 78 209 69.66667
4 4 1 30 98 58 186 62.00000
5 5 2 25 80 65 170 56.66667
6 6 2 50 89 98 237 79.00000
- mutate()에 ifelse() 조건문 적용하기
exam %>% mutate(test = ifelse(science >= 60, "pass", "fail")) %>%
head
# 결과
id class math english science test
1 1 1 50 98 50 fail
2 2 1 60 97 60 pass
3 3 1 45 86 78 pass
4 4 1 30 98 58 fail
5 5 2 25 80 65 pass
6 6 2 50 89 98 pass
- dplyr 함수의 mutate()를 사용하는 것운 기본 문법을 이용하여 파생변수를 추가하는 것과 비교했을 때, 변수명 앞에 데이터 프레임명을 반복해 입력하지 않아도 된다는 점에서 간편하다.
6. group_by()와 summarise()를 사용하여 요약표를 만들기
- summarise()
exam %>% summarise(mean_math = mean(math))
# 결과
mean_math
1 57.45
→ summarise()는 전체를 요약한 값을 구하기보다는 group_by() 와 조합해 집단별 요약표를 만들 때 사용
- group_by()
: group_by()에 변수를 지정하면 변수 항목별로 데이터를 분리한다. 여기에 summarise()를 조합하면 집단별 요양 통계량을 산출한다.
exam %>%
+ group_by(class) %>%
+ summarise(mean_math = mean(math))
# A tibble: 5 × 2 -> 데이터가 5행 2열의 tibble 형태라는 것을 의미
class mean_math
<int> <dbl>
1 1 46.2
2 2 61.2
3 3 45
4 4 56.8
5 5 78
*group_by()는 출력 결과를 데이터 프레임의 업그레이드 버전인 tibble 형태로 만듦
- 여러 통계량 한 번에 산출하기
exam %>%
group_by(class) %>%
summarise(mean_math = mean(math),
sum_math = sum(math),
median_math = median(math),
n = n()) # 학생수
# A tibble: 5 × 5
class mean_math sum_math median_math n
<int> <dbl> <int> <dbl> <int>
1 1 46.2 185 47.5 4
2 2 61.2 245 65 4
3 3 45 180 47.5 4
4 4 56.8 227 53 4
5 5 78 312 79 4
- group_by()에 여러 변수를 지정 → 각 집단별로 다시 집단 나눌 수 있음
(e.g. 성적 데이터를 반별로 나눈 후 다시 성별로 나눠 각 반의 성별 평균 점수 구하기)
mpg %>%
group_by(manufacturer, drv) %>% # 회사별, 구동방식별 분리리
summarise(mean_cty = mean(cty)) %>% #cty 평균 산출출
head(10)
# A tibble: 10 × 3
# Groups: manufacturer [5]
manufacturer drv mean_cty
<chr> <chr> <dbl>
1 audi 4 16.8
2 audi f 18.9
3 chevrolet 4 12.5
4 chevrolet f 18.8
5 chevrolet r 14.1
6 dodge 4 12
7 dodge f 15.8
8 ford 4 13.3
9 ford r 14.8
10 honda f 24.4
- dplyr 조합하기
: 회사별로 "suv" 자동차의 도시 및 고속도로 통합연비 평균을 구해 내림차순으로 정렬하고, 1~5위까지 출력하기
mpg %>%
group_by(manufacturer) %>%
filter(class=="suv") %>%
mutate(tot = (cty+hwy)/2) %>%
summarise(mean_tot = mean(tot)) %>%
arrange(desc(mean_tot)) %>%
head(5)
# A tibble: 5 × 2
manufacturer mean_tot
<chr> <dbl>
1 subaru 21.9
2 toyota 16.3
3 nissan 15.9
4 mercury 15.6
5 jeep 15.6
7. 데이터 합치기
- left_join() 함수를 활용하여 데이터를 가로로 합치기
test1 <- data.frame(id = c(1, 2, 3, 4, 5),
midterm = c(60, 80, 70, 90, 85))
test2 <- data.frame(id = c(1, 2, 3, 4, 5),
final = c(70, 83, 65, 95, 80))
total <- left_join(test1, test2, by = "id") # id를 기준으로 합쳐 total에 할당
total
# 결과
id midterm final
1 1 60 70
2 2 80 83
3 3 70 65
4 4 90 95
5 5 85 80
- bind_rows() 함수를 활용하여 데이터를 세로로 합치기
group_a <- data.frame(id = c(1, 2, 3, 4, 5),
test = c(60, 80, 70, 90, 85))
group_b <- data.frame(id = c(6, 7, 8, 9, 10),
test = c(60, 80, 70, 90, 85))
group_all <- bind_rows(group_a, group_b)
group_all
# 결과
id test
1 1 60
2 2 80
3 3 70
4 4 90
5 5 85
6 6 60
7 7 80
8 8 70
9 9 90
10 10 85
# 데이터 정제 - 빠진 데이터, 이상한 데이터 제거
1. 결측치 정제하기
결측치란, Missing value, 즉 비어 잇는 값을 의미한다. 결측치가 있으면 함수가 적용되지 않거나 분석 결과가 왜곡되는 문제가 발생한다.
a. 결측치 찾기
- is.na() 함수 활용 → TRUE/FALSE 값을 반환
df <- data.frame(sex = c("M", "F", NA, "M", "F"),
score = c(5, 4, 3, 4, NA))
is.na(df)
# 결과
sex score
[1,] FALSE FALSE
[2,] FALSE FALSE
[3,] TRUE FALSE
[4,] FALSE FALSE
[5,] FALSE TRUE
- is.na()를 table() 함수에 적용: 데이터에 결측치가 총 몇 개 있는지 출력
table(is.na(df))
# 결과
FALSE TRUE
8 2
- table(is.na())에 변수명을 지정: 해당 변수에 결측치가 몇 개 있는지 알 수 있음
table(is.na(df$sex))
# 결과
FALSE TRUE
4 1
table(is.na(df$score))
# 결과
FALSE TRUE
4 1
- 결측치가 포함된 데이터를 함수에 적용하면 정상적으로 연산되지 않고 NA가 출력됨
b. 결측치 제거하기
- is.na()를 filter()에 적용 → 결측치가 있는 행 제거
# 결측치 있는 데이터만 표시
df %>% filter(is.na(score))
#결과
sex score
1 F NA
# score 결측치를 제외한 행 추출
df %>% filter(!is.na(score))
#결과
sex score
1 M 5
2 F 4
3 <NA> 3
4 M 4
- & 기호를 활용하여 여러 변수 동시에 결측치 없는 행 추출
df_nomiss <- df %>% filter(!is.na(score) & !is.na(sex))
df_nomiss
# 결과
sex score
1 M 5
2 F 4
3 M 4
- na.omit(): 결측치가 하나라도 있으면 제거하기
df_nomiss2 <- na.omit(df)
df_nomiss2
# 결과
sex score
1 M 5
2 F 4
4 M 4
- 간편하지만, 분석에 필요한 행까지 손실된다는 단점 존재
- 따라서 filter()를 이용해 분석에 사용할 변수의 결측치만 제거하는 방식 권장
c. na.rm 파라미터 (NA Remove): 결측치를 제외하고 연산하도록 설정
- na.rm을 TRUE로 설정하면 결측치를 제외하고 함수를 적용함
- 그러나 모든 함수가 na.rm을 지원하지는 않음
- 만약 na.rm을 지원하지 않는 함수라면 filter()로 결측치를 제거한 후 함수를 적용해야 함
mean(df$score, na.rm = T) # 결측치 제외하고 평균 산출
sum(df$score, na.rm=T) # 결측치 제외하고 합계 산출
d. 결측치 대체하기
데이터가 작고 결측치가 많은 경우, 결측치를 제거하면 너무 많은 데이터가 손실돼 분석 결과가 왜곡되는 문제가 발생한다. 이때 결측치 대체법 (Imputation)을 수행하면, 결측치가 다른 값으로 대체되어 분석 결과가 왜곡되는 문제를 보완할 수 있다.
- 평균, 최빈값 같은 대표값을 구해 모든 결측치의 하나의 값으로 일괄 대체하는 방법
- 통계 분석 기법으로 각 결측치의 예측값을 추정해 대체하는 방법
- 평균값으로 결측치 대체하기
exam $math <- ifelse(is.na(exam$math), 55, exam$math) # math가 NA면 55로 대체
table(is.na(exam$math)) # 결측치 빈도표를 생성하여 잘 대체되었는지 확인
2. 이상치 정제하기
이상치란, 정상 범주에서 크게 벗어난 값이다. 데이터 수집 과정에서 오류가 발생할 수도 있고, 오류는 아니지만 굉장히 드물게 발생하는 극단적인 값이 있을 수도 있다. 이때, 분석 결과가 왜곡될 수 있기 때문에 이상치를 제거하는 작업이 필요하다.
a. 논리적으로 존재할 수 없는 값
- e.g. sex 변수에 3이라는 value가 들어 있음, 1~5점을 지닐 수 있는 score 변수에 6이라는 value가 들어 있음
outlier <- data.frame(sex = c(1, 2, 1, 3, 2, 1),
score = c(5, 4, 3, 4, 2, 6))
outlier
# 결과
sex score
1 1 5
2 2 4
3 1 3
4 3 4
5 2 2
6 1 6
- 이상치 확인하기 → table()을 통해 빈도표 생성
table(outlier$sex)
#결과
1 2 3
3 2 1
table(outlier$score)
#결과
2 3 4 5 6
1 1 2 1 1
- 결측 처리하기 → ifelse()를 이용해 이상치일 경우 NA를 부여
이상치를 결측치로 변환하는 작업
outlier$sex <- ifelse(outlier$sex == 3, NA, outlier$sex)
outlier
# 결과
sex score
1 1 5
2 2 4
3 1 3
4 NA 4
5 2 2
6 1 6
outlier$score <- ifelse(outlier$score == 6, NA, outlier$score)
outlier
# 결과
sex score
1 1 5
2 2 4
3 1 3
4 NA 4
5 2 2
6 1 NA
- filter()을 이용해 결측치 제외 후 score 평균 구하기
outlier %>%
+ filter(!is.na(sex) & !is.na(score)) %>%
+ group_by(sex) %>%
+ summarise(mean_score = mean(score))
# A tibble: 2 × 2
sex mean_score
<dbl> <dbl>
1 1 4
2 2 3
b. 극단적인 값 (극단치)
- 방법 1) 어디까지를 정상 범위로 볼 것인지 정하고, 이 범위를 벗어나면 극단치로 간주
- 방법 2) 통계적인 기준을 이용하여 (e.g. boxplot) 중심에서 크게 벗어난 값을 극단치로 간주
- 상자 그림으로 극단치 기준 정하기
# mpg 데이터의 hwy 변수로 boxplot 그림 만들기
boxplot(mpg$hwy)
- 결측 처리하기
mpg$hwy <- ifelse(mpg$hwy < 12 | mpg$hwy > 37, NA, mpg$hwy)
table(is.na(mpg$hwy))
# 결과
FALSE TRUE
231 3
- 결측치를 제외하고 간단한 분석 수행
mpg %>%
+ group_by(drv) %>%
+ summarise(mean_hwy = mean(hwy, na.rm = T))
# A tibble: 3 × 2
drv mean_hwy
<chr> <dbl>
1 4 19.2
2 f 27.7
3 r 21
'Programming Languages > R' 카테고리의 다른 글
실전 데이터 분석 프로젝트 (0) | 2025.02.20 |
---|---|
4. 그래프 만들기 (ggplot2) (0) | 2025.02.17 |
2. 데이터 프레임, 데이터 불러오기, 데이터 분석의 기초 (0) | 2025.02.06 |
[심화] R 패키지 DLM 논문 리뷰 (0) | 2025.02.06 |
1. R에서의 변수, 함수, 패키지 (0) | 2025.01.31 |