group_by()
후 summarise()
와 mutate()
데이터를 다루다 보면 데이터들을 특정한 기준에 따라 구분하여 그루핑해서 연산해야할 경우가 많다. 예를 들어 성적 데이터를 다룰때 학년별로 혹은 반별로 남여별로 그루핑하여 연산을 해야하는 경우이다. 위에서 불러들인 예에서도 연도별로 그루핑을 할 수 있고 지역별로 그루핑을 할수도 있을 것이다. 보통 group_by()
후에는 그룹별로 요약값을 내는 경우가 일반적이다. 하지만 일부 응영에서는 그룹별로 연산을 수행해야 하는 겅우도 있을 것이다. 이러한 경우를 어떻게 처리해야 하는지 알아보자.
이번 포스트에서 사용할 데이터는 한국교육개발원 교육통계서비스 홈페이지의 고등교육기관 연도별 입학자수를 활용하였다.
df_입학자 <- read_excel('2021_연도별 입학자수.xlsx',
## 'data' 시트의 데이터를 불러오는데,
sheet = 'Sheet0',
## 앞의 10행을 제외하고
skip = 3,
## 첫번째 행은 열 이름을 설정
col_names = FALSE,
## 열의 타입을 설정, 처음 8개는 문자형으로 다음 56개는 수치형으로 설정
col_types = c(rep('text', 2), rep('numeric', 30)))
## New names:
## * `` -> ...1
## * `` -> ...2
## * `` -> ...3
## * `` -> ...4
## * `` -> ...5
## * ...
## df_입학자 데이터에서 1, 2열과 3번부터 32열까지 2열마다 하나씩 선택하여 다시 df_입학자로 저장
df_입학자 <- df_입학자 |> select(1, 2, 5, 7, 9, 11, 13, 19, 29, 31)
## df_입학자의 열이름을 적절한 이름으로 설정
colnames(df_입학자) <- c('연도', '지역', '전문대학', '교육대학', '일반대학', '방송통신대학', '산업대학', '원격및사이버대학', '석사', '박사')
## 연도에 결측치가 입력된 데이터는 제외하여 df_입학자로 저장(엑셀 데이터 하단의 설명 라인 제거)
df_입학자 <- df_입학자 |> filter(!is.na(지역))
이 작업을 filter()
를 사용해서 수행한다면 filter()
로 서브셋을 각각 만들고 연산을 적용하는 과정을 반복적으로 수행해야 할 것이고 이 과정에서 코드도 길어지고 오류를 낼 가능성도 많아진다. 이를 위해 사용되는 함수가 group_by()
이다. group_by()
는 구분자로 사용될 열에 포함된 값에 따라 데이터를 분리하고 연산을 각각의 그룹에 적용시켜주는 함수이다.
group_by(.data, 그루핑 열, ...)
- .data : 데이터프레임, `tibble`과 같은 확장된 데이터프레임
- 그루핑 열 : 그루핑할 구분자가 포함된 열 이름
df_입학자 |> group_by(연도)
## # A tibble: 400 x 10
## # Groups: 연도 [23]
## 연도 지역 전문대학 교육대학 일반대학 방송통신대학 산업대학 원격및사이버대학
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1999 전체 306802 4840 319278 49648 30882 0
## 2 2000 전체 318135 5075 321399 47387 33240 0
## 3 2001 전체 322687 4959 327031 50949 33870 0
## 4 2002 전체 311304 4971 320534 47175 31896 0
## 5 2003 전체 275318 5166 321116 40195 29720 0
## 6 2004 전체 259182 5783 329509 35203 28444 12044
## 7 2005 전체 251283 6188 326284 32389 28197 16086
## 8 2006 전체 254433 6235 335581 31545 22061 14529
## 9 2007 전체 255395 5741 342250 32493 22304 14209
## 10 2008 전체 249291 5459 342916 33725 22374 18401
## # ... with 390 more rows, and 2 more variables: 석사 <dbl>, 박사 <dbl>
df_입학자 |> group_by(연도, 지역)
## # A tibble: 400 x 10
## # Groups: 연도, 지역 [400]
## 연도 지역 전문대학 교육대학 일반대학 방송통신대학 산업대학 원격및사이버대학
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1999 전체 306802 4840 319278 49648 30882 0
## 2 2000 전체 318135 5075 321399 47387 33240 0
## 3 2001 전체 322687 4959 327031 50949 33870 0
## 4 2002 전체 311304 4971 320534 47175 31896 0
## 5 2003 전체 275318 5166 321116 40195 29720 0
## 6 2004 전체 259182 5783 329509 35203 28444 12044
## 7 2005 전체 251283 6188 326284 32389 28197 16086
## 8 2006 전체 254433 6235 335581 31545 22061 14529
## 9 2007 전체 255395 5741 342250 32493 22304 14209
## 10 2008 전체 249291 5459 342916 33725 22374 18401
## # ... with 390 more rows, and 2 more variables: 석사 <dbl>, 박사 <dbl>
위의 코드를 실행시켜보면 실행 결과는 별 차이가 없는 것같다. 하지만 자세히 보면 하나 차이를 발견할 수 있는데 데이터가 표시되는 바로 윗 줄에 ‘# Groups:’ 로 시작하는 한 줄이 표시된다. group_by()
는 데이터프레임을 내부적으로 tibble
로 변환하고, 변환된 tibble
을 그루핑 구분자에 따라 해당 그룹으로 분할하여 저장한다. 그렇기 때문에 group_by()
를 적용한 객체의 타입이 grouped_df
로 변경되고 내부적으로 그루핑된 데이터 구조를 가지는 객체로 변환된다. 다음의 코드를 실행해보자.
## df_입학자의 데이터 구조 출력
df_입학자 |> str()
## tibble [400 x 10] (S3: tbl_df/tbl/data.frame)
## $ 연도 : chr [1:400] "1999" "2000" "2001" "2002" ...
## $ 지역 : chr [1:400] "전체" "전체" "전체" "전체" ...
## $ 전문대학 : num [1:400] 306802 318135 322687 311304 275318 ...
## $ 교육대학 : num [1:400] 4840 5075 4959 4971 5166 ...
## $ 일반대학 : num [1:400] 319278 321399 327031 320534 321116 ...
## $ 방송통신대학 : num [1:400] 49648 47387 50949 47175 40195 ...
## $ 산업대학 : num [1:400] 30882 33240 33870 31896 29720 ...
## $ 원격및사이버대학: num [1:400] 0 0 0 0 0 ...
## $ 석사 : num [1:400] 73826 82374 86992 89557 91178 ...
## $ 박사 : num [1:400] 10447 11705 12570 13227 13310 ...
## df_입학자를 연도별로 그루핑한 결과의 구조 출력
df_입학자 |> group_by(연도) |> str()
## grouped_df [400 x 10] (S3: grouped_df/tbl_df/tbl/data.frame)
## $ 연도 : chr [1:400] "1999" "2000" "2001" "2002" ...
## $ 지역 : chr [1:400] "전체" "전체" "전체" "전체" ...
## $ 전문대학 : num [1:400] 306802 318135 322687 311304 275318 ...
## $ 교육대학 : num [1:400] 4840 5075 4959 4971 5166 ...
## $ 일반대학 : num [1:400] 319278 321399 327031 320534 321116 ...
## $ 방송통신대학 : num [1:400] 49648 47387 50949 47175 40195 ...
## $ 산업대학 : num [1:400] 30882 33240 33870 31896 29720 ...
## $ 원격및사이버대학: num [1:400] 0 0 0 0 0 ...
## $ 석사 : num [1:400] 73826 82374 86992 89557 91178 ...
## $ 박사 : num [1:400] 10447 11705 12570 13227 13310 ...
## - attr(*, "groups")= tibble [23 x 2] (S3: tbl_df/tbl/data.frame)
## ..$ 연도 : chr [1:23] "1999" "2000" "2001" "2002" ...
## ..$ .rows: list<int> [1:23]
## .. ..$ : int [1:17] 1 24 47 70 93 116 139 162 194 217 ...
## .. ..$ : int [1:17] 2 25 48 71 94 117 140 163 195 218 ...
## .. ..$ : int [1:17] 3 26 49 72 95 118 141 164 196 219 ...
## .. ..$ : int [1:17] 4 27 50 73 96 119 142 165 197 220 ...
## .. ..$ : int [1:17] 5 28 51 74 97 120 143 166 198 221 ...
## .. ..$ : int [1:17] 6 29 52 75 98 121 144 167 199 222 ...
## .. ..$ : int [1:17] 7 30 53 76 99 122 145 168 200 223 ...
## .. ..$ : int [1:17] 8 31 54 77 100 123 146 169 201 224 ...
## .. ..$ : int [1:17] 9 32 55 78 101 124 147 170 202 225 ...
## .. ..$ : int [1:17] 10 33 56 79 102 125 148 171 203 226 ...
## .. ..$ : int [1:17] 11 34 57 80 103 126 149 172 204 227 ...
## .. ..$ : int [1:17] 12 35 58 81 104 127 150 173 205 228 ...
## .. ..$ : int [1:17] 13 36 59 82 105 128 151 174 206 229 ...
## .. ..$ : int [1:17] 14 37 60 83 106 129 152 175 207 230 ...
## .. ..$ : int [1:18] 15 38 61 84 107 130 153 176 185 208 ...
## .. ..$ : int [1:18] 16 39 62 85 108 131 154 177 186 209 ...
## .. ..$ : int [1:18] 17 40 63 86 109 132 155 178 187 210 ...
## .. ..$ : int [1:18] 18 41 64 87 110 133 156 179 188 211 ...
## .. ..$ : int [1:18] 19 42 65 88 111 134 157 180 189 212 ...
## .. ..$ : int [1:18] 20 43 66 89 112 135 158 181 190 213 ...
## .. ..$ : int [1:18] 21 44 67 90 113 136 159 182 191 214 ...
## .. ..$ : int [1:18] 22 45 68 91 114 137 160 183 192 215 ...
## .. ..$ : int [1:18] 23 46 69 92 115 138 161 184 193 216 ...
## .. ..@ ptype: int(0)
## ..- attr(*, ".drop")= logi TRUE
위의 코드 중 앞선 코드는 df_입학자의 데이터 구조를 출력하는 코드이다. 데이터의 열과 열의 타입, 몇 개의 데이터 내용이 표시된다. 두번째 코드는 연도별로 그루핑된 데이터 구조를 출력하는 코드이다. 데이터 타입이 grouped_df
로 변경되었다. 열은 앞선 결과와 별 차이없이 나타나지만 그 아래 추가적인 정보가 나타나는데 각각의 그룹에 속한 행번호가 표기된다. 이렇게 그루핑된 데이터를 다시 하나의 데이터로 변환하기 위해서는 ungroup()
을 사용한다. 이 함수를 사용하면 grouped_df
로 바뀌었던 데이터 구조가 다시 데이터프레임이나 tibble
로 변환된다.
## df_입학자를 연도별로 그루핑한 결과를 다시 ungroup()한 결과의 구조 출력
df_입학자 |> group_by(연도) |> ungroup() |> str()
## tibble [400 x 10] (S3: tbl_df/tbl/data.frame)
## $ 연도 : chr [1:400] "1999" "2000" "2001" "2002" ...
## $ 지역 : chr [1:400] "전체" "전체" "전체" "전체" ...
## $ 전문대학 : num [1:400] 306802 318135 322687 311304 275318 ...
## $ 교육대학 : num [1:400] 4840 5075 4959 4971 5166 ...
## $ 일반대학 : num [1:400] 319278 321399 327031 320534 321116 ...
## $ 방송통신대학 : num [1:400] 49648 47387 50949 47175 40195 ...
## $ 산업대학 : num [1:400] 30882 33240 33870 31896 29720 ...
## $ 원격및사이버대학: num [1:400] 0 0 0 0 0 ...
## $ 석사 : num [1:400] 73826 82374 86992 89557 91178 ...
## $ 박사 : num [1:400] 10447 11705 12570 13227 13310 ...
위에서는 group_by()
의 구조를 설명하였다. group_by()
의 사용은 summarise()
를 사용하여 그룹별로 요약값을 산출할 때 주로 사용된다.
## df_입학자를 연도별로 그루핑하고 `summarise()`를 사용하여 연도별 전문대학 합계, 평균, 최고, 최저를 산출
df_입학자 |> group_by(연도) |> summarise(전문대학합계 = sum(전문대학), 전문대학평균 = mean(전문대학), 전문대학최고 = max(전문대학), 전문대학최저 = min(전문대학))
## # A tibble: 23 x 5
## 연도 전문대학합계 전문대학평균 전문대학최고 전문대학최저
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1999 613604 36094. 306802 2576
## 2 2000 636270 37428. 318135 3641
## 3 2001 645374 37963. 322687 4739
## 4 2002 622608 36624 311304 4503
## 5 2003 550636 32390. 275318 4129
## 6 2004 518364 30492 259182 3543
## 7 2005 502566 29563. 251283 3602
## 8 2006 508866 29933. 254433 3441
## 9 2007 510790 30046. 255395 3545
## 10 2008 498582 29328. 249291 3512
## # ... with 13 more rows
## df_입학자를 지역별로 그루핑하고 `summarise()`를 사용하여 연도별 일반대학 합계, 평균, 최고, 최저를 산출
df_입학자 |> group_by(지역) |> summarise(일반대학합계 = sum(일반대학), 일반대학평균 = mean(일반대학), 일반대학최고 = max(일반대학), 일반대학최저 = min(일반대학))
## # A tibble: 18 x 5
## 지역 일반대학합계 일반대학평균 일반대학최고 일반대학최저
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 강원 394317 17144. 19314 14657
## 2 경기 935864 40690. 45211 35937
## 3 경남 321578 13982. 16080 11698
## 4 경북 640879 27864. 31531 23648
## 5 광주 332712 14466. 16193 12761
## 6 대구 237326 10319. 10901 9854
## 7 대전 411064 17872. 21053 14853
## 8 부산 769485 33456. 36533 29505
## 9 서울 1827126 79440. 84771 73674
## 10 세종 26911 2990. 3157 2847
## 11 울산 78185 3399. 4011 3035
## 12 인천 157015 6827. 8235 5849
## 13 전남 210609 9157. 10974 7959
## 14 전북 381670 16594. 19852 13522
## 15 전체 7861291 341795. 372941 319278
## 16 제주 62679 2725. 3205 2398
## 17 충남 692863 30124. 35584 25938
## 18 충북 381008 16566. 19058 14428
앞서 설명한 바와 같이 group_by()
는 주로 summarise()
와 요약 함수를 사용하여 그룹별로 요약 값을 산출하는데 많이 사용된다. 하지만 group_by()
에 mutate()
를 사용할 수도 있다. group_by()
된 객체에 summarise()
를 사용하면 그룹마다 요약 행이 하나씩 생성되어 그룹수만큼의 행이 출력된다. 반면 mutate()
를 사용하면 각 그룹의 데이터를 대상으로 연산된 결과 열이 포함된 결과가 산출된다.
예를 들어 위의 예에서 각 연도마다 일반대학 입학생의 지역별 분포 비율을 구해야 한다고 생각해보자. 먼저 각 연도마다 일반대학 전체 입학생의 합계값이 필요할 것이다. 이 합계값을 지역별 입학생으로 나누어야 지역별 분포가 나올 것이다. 이를 위해서는 먼저 각 연도 그룹별로 일반대학 입학생의 합계를 모든 행에 넣어주어야 mutate()
를 사용하여 비율을 구할 것이다. 이를 group_by()
없이 구현해야 한다면 다음과 같이 구할 수 있을 것이다.
## df_입학자를 연도 1999년으로 필터링하고 전체행을 삭제한 후에 전체 일반대학 합계(sum(일반대학))를 각각의 지역값으로 나누어 비율을 산출
df_입학자 |> filter(연도 == 1999, 지역 != '전체') |> transmute(연도, 지역, 일반대학비율 = 일반대학/sum(일반대학))
## # A tibble: 16 x 3
## 연도 지역 일반대학비율
## <chr> <chr> <dbl>
## 1 1999 서울 0.243
## 2 1999 부산 0.0924
## 3 1999 대구 0.0341
## 4 1999 인천 0.0187
## 5 1999 광주 0.0408
## 6 1999 대전 0.0482
## 7 1999 울산 0.00954
## 8 1999 경기 0.114
## 9 1999 강원 0.0473
## 10 1999 충북 0.0452
## 11 1999 충남 0.0820
## 12 1999 전북 0.0596
## 13 1999 전남 0.0344
## 14 1999 경북 0.0846
## 15 1999 경남 0.0367
## 16 1999 제주 0.00898
위의 코드를 각각의 연도에 적용해야 하기 때문에 이 코드를 1999부터 2021까지 22번 실행해야 한다. 다른 프로그래밍 경험이 있는 사용자라면 for
루프를 사용할 수도 있겠지만 group_by()
와 mutate()
를 다음과 같이 사용하면 간단히 해결된다.
df_입학자 |> ## df_입학자에서
filter(지역 != '전체') |> ## 지역이 전체가 아닌 행들만 걸러내고
group_by(연도) |> ## 연도별로 그루핑하고
transmute(연도, 지역, 일반대학합계 = sum(일반대학), 일반대학비율 = 일반대학/일반대학합계) |> ## 연도, 지역, 일반대학합계, 일반대학비율 열을 생성
filter(연도 == 1999) ## 데이터 확인을 위해 연도가 1999년만 필터링
## # A tibble: 16 x 4
## # Groups: 연도 [1]
## 연도 지역 일반대학합계 일반대학비율
## <chr> <chr> <dbl> <dbl>
## 1 1999 서울 319278 0.243
## 2 1999 부산 319278 0.0924
## 3 1999 대구 319278 0.0341
## 4 1999 인천 319278 0.0187
## 5 1999 광주 319278 0.0408
## 6 1999 대전 319278 0.0482
## 7 1999 울산 319278 0.00954
## 8 1999 경기 319278 0.114
## 9 1999 강원 319278 0.0473
## 10 1999 충북 319278 0.0452
## 11 1999 충남 319278 0.0820
## 12 1999 전북 319278 0.0596
## 13 1999 전남 319278 0.0344
## 14 1999 경북 319278 0.0846
## 15 1999 경남 319278 0.0367
## 16 1999 제주 319278 0.00898
'데이터 전처리' 카테고리의 다른 글
for loop, apply, 벡터 연산의 속도 차이 in R (0) | 2022.02.26 |
---|---|
색 in R (0) | 2022.02.24 |
R-Studio 단축키 (0) | 2021.12.26 |
여러 열의 천단위 구분자 없애기 in R (0) | 2021.12.16 |
데이터 비식별화 처리(가명화, 익명화, 암호화) in R (0) | 2021.11.16 |
댓글