Ch 06. 그룹화와 요약 (group_by, summarise)
데이터 분석에서 "그룹별 평균은 얼마인가", "부서별 최댓값은 어느 팀인가" 같은 질문을 가장 많이 하게 됩니다. group_by()와 summarise()를 조합하면 이런 집계를 간결하게 처리합니다.
이 챕터는 반복 실습을 통해 패턴을 완전히 익히는 것이 목표입니다.
기본 패턴: group_by() %>% summarise()
mpg 데이터에서 제조사별 평균 고속도로 연비를 구합니다.
library(tidyverse)
mpg %>%
group_by(manufacturer) %>%
summarise(avg_hwy = mean(hwy))
# A tibble: 15 × 2
manufacturer avg_hwy
<chr> <dbl>
1 audi 26.4
2 chevrolet 21.9
3 dodge 17.9
4 ford 19.4
5 honda 32.6
6 hyundai 26.9
...
group_by()는 데이터를 내부적으로 그룹으로 나눕니다. 겉보기에는 변화가 없지만, 이후 summarise()가 각 그룹에 대해 계산을 수행합니다.
다양한 집계 함수
summarise() 안에서 쓸 수 있는 집계 함수입니다.
mpg %>%
group_by(class) %>%
summarise(
n = n(), # 행 수
avg_hwy = mean(hwy), # 평균
med_hwy = median(hwy), # 중앙값
max_hwy = max(hwy), # 최댓값
min_hwy = min(hwy), # 최솟값
sd_hwy = sd(hwy), # 표준편차
total_cty = sum(cty) # 합계
)
# A tibble: 7 × 8
class n avg_hwy med_hwy max_hwy min_hwy sd_hwy total_cty
<chr> <int> <dbl> <dbl> <int> <int> <dbl> <int>
1 2seater 5 24.8 25 26 23 1.30 73
2 compact 47 28.3 27 44 23 4.51 868
3 midsize 41 27.3 27 32 23 2.14 756
4 minivan 11 22.4 23 24 17 2.07 183
5 pickup 33 16.9 17 22 12 2.56 466
6 subcompact 35 28.1 26 44 20 6.11 621
7 suv 62 18.1 17 27 12 3.06 921
n()은 각 그룹의 행 수를 셉니다. 인수 없이 쓰면 현재 그룹의 크기를 반환합니다.
n()의 형제들
# 고유값의 수
mpg %>%
group_by(class) %>%
summarise(n_mfr = n_distinct(manufacturer))
# A tibble: 7 × 2
class n_mfr
<chr> <int>
1 2seater 1
2 compact 4
3 midsize 6
...
n_distinct()는 중복을 제외한 고유값의 수를 셉니다. 차종별로 몇 개의 제조사가 있는지 알 수 있습니다.
다중 그룹화
두 개 이상의 열로 그룹화할 수 있습니다.
mpg %>%
group_by(manufacturer, class) %>%
summarise(
n = n(),
avg_hwy = mean(hwy),
.groups = "drop"
)
# A tibble: 38 × 4
manufacturer class n avg_hwy
<chr> <chr> <int> <dbl>
1 audi compact 15 28.3
2 audi midsize 3 26.3
3 chevrolet 2seater 5 24.8
4 chevrolet midsize 5 27
5 chevrolet suv 19 18.0
...
.groups = "drop"은 summarise() 후 그룹을 해제합니다. 다중 그룹화 후에는 명시적으로 설정하는 습관을 들이면 예상치 못한 동작을 예방합니다.
arrange()로 결과 정렬
summarise() 결과를 정렬하면 순위를 한눈에 볼 수 있습니다.
mpg %>%
group_by(manufacturer) %>%
summarise(avg_hwy = mean(hwy)) %>%
arrange(desc(avg_hwy))
# A tibble: 15 × 2
manufacturer avg_hwy
<chr> <dbl>
1 honda 32.6
2 volkswagen 29.2
3 hyundai 26.9
4 audi 26.4
5 toyota 24.9
...
혼다가 고속도로 연비 1위입니다.
filter()와 결합: 그룹 내 필터링
group_by() 이후 filter()를 쓰면 그룹 내에서 조건을 적용합니다.
# 각 제조사에서 hwy가 가장 높은 모델
mpg %>%
group_by(manufacturer) %>%
filter(hwy == max(hwy)) %>%
select(manufacturer, model, hwy) %>%
arrange(manufacturer)
# A tibble: 18 × 3
manufacturer model hwy
<chr> <chr> <int>
1 audi a4 31
2 chevrolet corvette 26
3 dodge caravan 2wd 24
...
각 제조사의 최고 연비 모델이 한 줄씩 표시됩니다. max(hwy)가 그룹 안에서 계산되기 때문에 제조사별 최댓값과 비교합니다.
mutate()와 group_by() 결합
group_by() 후 summarise() 대신 mutate()를 쓰면 그룹 통계를 원래 데이터에 추가합니다.
# 제조사 평균 대비 각 차량의 연비 비율
mpg %>%
group_by(manufacturer) %>%
mutate(
mfr_avg_hwy = mean(hwy),
ratio = hwy / mfr_avg_hwy
) %>%
ungroup() %>%
select(manufacturer, model, hwy, mfr_avg_hwy, ratio)
# A tibble: 234 × 5
manufacturer model hwy mfr_avg_hwy ratio
<chr> <chr> <int> <dbl> <dbl>
1 audi a4 29 26.4 1.10
2 audi a4 29 26.4 1.10
3 audi a4 31 26.4 1.17
...
제조사 내 평균 대비 각 모델의 상대적 위치를 볼 수 있습니다. 행 수는 줄어들지 않습니다.
ungroup()의 중요성
group_by() 후 데이터는 그룹이 지정된 상태로 남습니다. 이후 작업에 영향을 줄 수 있습니다.
# 그룹 확인
mpg_grouped <- mpg %>% group_by(manufacturer)
groups(mpg_grouped)
[[1]]
manufacturer
ungroup()으로 그룹을 해제합니다.
mpg_ungrouped <- mpg_grouped %>% ungroup()
groups(mpg_ungrouped)
list()
summarise() 후에는 보통 자동으로 마지막 그룹이 제거되지만, 다중 그룹화의 경우 중간 그룹이 남을 수 있습니다. 작업이 끝난 후 ungroup()을 명시적으로 호출하는 것이 안전합니다.
실전 종합 예제
starwars 데이터로 종족별 신체 통계를 구합니다.
starwars %>%
filter(!is.na(species), !is.na(height), !is.na(mass)) %>%
group_by(species) %>%
summarise(
n = n(),
avg_height = mean(height),
avg_mass = mean(mass),
avg_bmi = mean(mass / (height / 100)^2)
) %>%
filter(n >= 2) %>% # 2명 이상인 종족만
arrange(desc(avg_bmi)) %>%
ungroup()
# A tibble: 9 × 5
species n avg_height avg_mass avg_bmi
<chr> <int> <dbl> <dbl> <dbl>
1 Hutt 1 175 1358 443.
2 Dug 1 94 40 45.3
3 Yoda's … 1 66 17 39.0
...
n이 2 이상인 종족 중 평균 BMI가 높은 순으로 정렬됩니다. group_by() %>% summarise() %>% filter() %>% arrange()가 하나의 흐름처럼 이어집니다.
이 패턴은 앞으로 가장 자주 쓰게 될 분석 패턴입니다. 자연스럽게 손에 익을 때까지 연습하겠습니다.
다음은 여러 데이터를 합치는 방법입니다.