聰明不如鈍筆
총명불여둔필
assignment KindeR

최대한 친절하게 쓴 R로 데이터 뽑아내기(feat. dplyr)


R는 마이크로소프트(MS) 엑셀만큼이나 데이터도 아주 잘 처리합니다. 특히 해들리 위컴 박사가 개발한 dplyr 패키지와 함께라면 더더욱 그렇습니다.


이번 포스트에서는 프로야구를 처음 시작한 1982년부터 지난해(2017년)까지 팀 타격 데이터를 가지고 dplyr를 어떻게 활용하는지 알아보도록 하겠습니다. 


인터넷 포털 사이트 등에서 dplyr를 찾아 이 포스트를 찾아오신 분이라면 이미 R가 무엇인지 잘 알고 계실 겁니다.


혹시 모르시는 분들께 R 홈페이지에 올라온 대로 설명드리면 R는 통계 계산과 그래픽에 활용하는 무료 소프트웨어 환경(R is a free software environment for statistical computing and graphics)"입니다.


무료니까 누구든 이 페이지에서 원하시는 R 버전을 선택해 설치하실 수 있습니다. 2018년 8월 현재 최신 버전은 3.5.1이며 MS 윈도에 이를 설치하시려면 이 링크를 누르시면 됩니다.


이 포스트 제목에는 분명 '최대한 친절하게'라고 썼지만 프로그램 설치법까지 모르실 거라고는 생각하지 않으니 넘어가도록 하겠습니다.


설치를 마치시고 R를 실행하시면 아래 화면이 나타납니다. 이 창을 흔히 'R 콘솔(Console)'이라고 부릅니다. 창 제목에도 그렇게 나와 있습니다.



여기서 '>' 표시 뒤에 명령어를 입력하면 프로그래밍(코딩)을 진행할 수 있습니다. 코딩은 '이러저러한 게 하고 싶다'고 컴퓨터에게 알려주는 일을 뜻합니다


 제가 "print('Hello, World!')"라고 쓴 건 Hello, World!를 (화면에) 출력하고 싶다고 컴퓨터에게 알려준 겁니다. 이 "Hello, World!" 프로그램은 코딩을 처음 배울 때 제일 많이 쓰는 기본 예제입니다. 


이 22글자도 치기 귀찮으시다면 '1+1'을 입력해 보세요. 그러면 '2'를 표시할 겁니다.


기껏 R를 깔았는데 어떻게 사용해야 할지 모르실 때는 계산기로 쓰셔도 된다는 뜻입니다. 물론 컴퓨터에 이미 계산기 프로그램이 들어 있는 게 함정이기는 하지만 말입니다. (네, 재미없는 농담 죄송합니다.)


이제 두 번째 명령어(함수)를 배울 차례가 됐습니다.


두 번째 함수는 R에 패키지를 설치하라는 뜻인 'install.packages()'입니다. 패키지는 R에 기본으로 들어 있지 않은 각종 기능을 추가할 수 있는 묶음이라고 생각하시면 쉽습니다.


그렇다면 이번에 우리가 설치해야 하는 패키지 이름은 뭘까요? 맞습니다. 'dplyr'입니다. dplyr를 설치하시려면 아래처럼 입력하시면 그만입니다.

install.packages('dplyr')


R에서는 패키지를 설치했다고 곧바로 사용할 수 있게 되는 건 아닙니다. 패키지를 불러오는 절차를 한 번 더 거쳐야 합니다. 이때는 'library()'라는 함수를 쓰시면 됩니다.

library('dplyr')


아무 반응도 없다고요? 네, 그러면 dplyr를 불러오는 데 성공한 겁니다. 축하드립니다. 이제 누군가 dplyr 이야기를 꺼내면 '나도 안다'고 하실 수 있게 됐습니다. 


새로운 패키지를 불러들이고 나면 사용할 수 있는 함수가 늘어납니다. dplyr에서 제일 중요한 함수는 아래 있는 여섯 가지입니다. 이번 포스트도 이 여섯 가지 함수에 초점을 맞추게 됩니다.


▌dplyr 주요 함수
 함수  기능  사용법
 arrange()  행 정렬  arrange(데이터, 변수 이름)
 filter()  행 추출  filter(데이터, 조건)
 group_by()  행 결합  group_by(조건)
 mutate()  열 추가  mutate(데이터, 새 변수 = 계산식)
 select()  열 선택  select(데이터, 변수 이름 또는 인덱스)
 summarise()  행 요약  summarise(데이터, 새 변수 = 계산식)



데이터 불러와서 확인하기

이 여섯 가지 함수를 활용하려면 일단 처리해야 할 데이터가 있어야겠죠?


앞서 말씀드린 것처럼 1982~2017 프로야구 팀 타격 기록을 활용하도록 하겠습니다. 아래 링크에서 내려받으시면 됩니다.


kbo.csv


이 데이터는 쉼표로 구분된 값(CSV·Comma Separated Value)이라는 형태로 되어 있습니다. 기본적으로 텍스트 파일에서 각 열을 쉼표로 구분한 형태라고 생각하시면 됩니다.


이 파일을 확인하는 가장 쉬운 방법은 MS 엑셀을 활용하시는 것. 컴퓨터에 이미 MS 엑셀이 깔려 있다면 그냥 더블클릭만으로 내용을 확인하실 수 있습니다.


아니면 아래처럼 구글 스프레드시트를 활용하셔도 됩니다. 이번 데이터는 행이 284개, 열이 21개인 구조입니다. 



R 역시 CSV 파일을 읽을 수 있도록 함수를 제공하고 있습니다. 이름 그대로 'read.csv()'가 그 주인공. 이 파일을 읽으시려면 read.csv('kbo.csv')라고만 치셔도 무방합니다.


단, 이때 이 파일이 R 작업 디렉토리(폴더)에 들어 있어야 합니다. MS 윈도에서 기본 작업 디렉토리는 기본적으로 '문서'입니다. 그러니까 한번도 작업 폴더를 바꾸신 적이 없다면 내 문서에 이 파일을 내려받으시면 됩니다.


만약 작업 디렉토리를 바꾸고 싶으실 때는 메뉴에서 File - Change dir...를 선택하시면 됩니다. 이게 제일 간단하고 쉬운 방법입니다. 콘솔에 직접 입력하고 싶으실 때는 'setwd()'라는 함수 뒤에 원하는 폴더를 쓰셔도 같은 효과를 경험하실 수 있습니다. 


이렇게 원하는 위치에 파일 저장을 마치셨으면 진짜 파일을 불러옵니다.

read.csv('kbo.csv')


파일 내용이 잘 보였으리라 믿습니다. 그런데 이렇게 그냥 내용을 읽기만 하는 걸로는 다음 번 작업에 활용하기가 곤란합니다. 그래서 컴퓨터 메모리 안에 빈 방을 만들어 이 내용을 담아두는 방법을 씁니다. 프로그래밍에서는 이런 빈 방을 '변수'라고 부릅니다.


R에서 변수에 어떤 값을 넣을 때는 '<-' 또는 '=' 기호를 씁니다. 이번에는 kbo라는 빈 방에 이 결과를 넣어보도록 하겠습니다. 이렇게 치시면 됩니다.

kbo <- read.csv('kbo.csv')


이번에는 아무 내용도 출력하지 않습니다. 변수 내용을 확인하시려면 그냥 변수 이름만 치시면 됩니다. > 다음에 kbo라고 쓰시면 print(kbo)라고 치셨을 때와 같은 화면이 나타날 겁니다.


문제가 있다면 자료가 너무 많아서 어떤 모양인지 알아보기가 쉽지 않다는 것. 이럴 때는 head()를 써보시면 좋습니다. head()는 어떤 데이터에서 처음 여섯 줄만 보여주는 구실을 합니다.

head(kbo)
##   연도   팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 1982  MBC   80 3061 2686  419  757    124     12   65   1100  381  134
## 2 1982 삼성   80 3043 2647  429  705    126     18   57   1038  374  147
## 3 1982   OB   80 3098 2745  399  778    137     23   57   1132  362  106
## 4 1982 해태   80 2990 2665  374  696    110     14   84   1086  332  155
## 5 1982 롯데   80 3062 2628  353  674    112      8   59    979  325   83
## 6 1982 삼미   80 2954 2653  302  637    117     20   40    914  272   74
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       60  268           47       20  316   56       32         27
## 2       42  307           30        2  349   50       36         18
## 3       61  247           41       22  254   35       46         18
## 4       52  235           41       12  296   59       28         21
## 5       53  326           40        8  315   61       41         27
## 6       43  221           29        3  369   44       33         17


텍스트 형태로 화면에 표시하다 보니 폭이 잘 맞지 않지만 일단 원하는 자료는 다 들어왔습니다.


여기서 눈에 띄는 또 한 가지는 '2루타', '3루타'로 되어 있던 열 이름 앞에 'X'가 붙었다는 점입니다. R에서는 열 이름을 숫자로 시작할 수 없기 때문에 자동으로 이렇게 바뀐 겁니다. '몸에 맞는 공'이 '몸에.맞는.공'으로 바뀐 건 공백 때문입니다.


혹시 이런 생각하시는 분은 안 계신가요? '왜 여섯 줄이냐? 나는 다섯 줄만 보고 싶다.' 걱정 없습니다. head(변수, 숫자) 형태로 쓰시면 처음부터 원하는 행까지 데이터 확인이 가능합니다.


거꾸로 'tail()'이라는 함수도 있습니다. head()와 반대로 끝에서부터 자료를 보여주는 구실을 합니다. tail()도 head()와 마찬가지로 tail(변수, 숫자) 형태로 쓰실 수 있습니다.


이제 dplyr 공부를 시작할 준비를 모두 마쳤습니다. 원래 시험 때도 공부할 준비를 마쳤으면 잠시 쉬어주는 게 예의. 기지개라도 한번 켜시면서 다음으로 넘어가 볼까요?



데이터 정렬 그리고 파이프

데이터를 처리할 때 꼭 필요한 기능을 꼽으라면 역시 정렬입니다. dplyr로 데이터를 정렬할 때 쓰는 함수는? 네, 위에서 보신 것처럼 arrange()입니다.


지금 우리가 쓰고 있는 데이터는 기본적으로 '연도'를 오름차순으로 정렬한 형태입니다. 이걸 팀 이름 순서로 바꾸려면 어떻게 하면 될까요?


arrange()는 arrange(데이터, 변수) 순서로 활용합니다. 이때 데이터는 'kbo'이고, 변수는 '팀'이 됩니다. 그렇다면 arrange(kbo, 팀)이라고 입력하면 우리가 원하는 결과를 얻을 수 있을 겁니다.


결과가 다 나오면 너무 기니까 head() 함수를 써서 확인해 보겠습니다. 이렇게 함수를 겹쳐 쓸 때는 어떤 함수 안에 다른 함수를 포함하면 그만입니다.

head(arrange(kbo, 팀))
##   연도  팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 2002 KIA  133 5200 4575  643 1230    220     14  120   1838  611  155
## 2 2003 KIA  133 5127 4448  657 1211    219     16  129   1849  626  146
## 3 2004 KIA  133 5190 4426  643 1182    224      9  143   1853  610  127
## 4 2005 KIA  133 4848 4234  541 1102    183     13   99   1608  507  101
## 5 2006 KIA  126 4754 4181  476 1067    191     22   62   1488  431   93
## 6 2007 KIA  126 4811 4204  499 1080    198     16   73   1529  466   70
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       39  454           91       32  914  107       45         35
## 2       67  505           42       18  721   72       94         37
## 3       54  549           79       22  693  118       82         54
## 4       43  430           74       24  718  101       79         31
## 5       43  390           55       20  650   90      102         26
## 6       44  428           55        7  630  117       78         45


일단 잘 나왔습니다. KIA가 제일 먼저 나온 건 알파벳부터 오름차순으로 정렬했기 때문입니다. 그러면 내림차순으로 정렬할 때는 어떻게 하면 될까요? 이때는 변수 이름 앞에 desc만 쓰면 됩니다. desc는 내림차순을 뜻하는 'descending'을 줄인 말입니다.


그렇다면 arrange(kbo, desc(팀))이라고 쓰면 될 테도 그 결과는 아래와 같습니다

arrange(kbo, desc(팀))
##   연도       팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점
## 1 2008 히어로즈  126 4834 4317  499 1149    190     19   70   1587  471
## 2 2009 히어로즈  133 5211 4486  683 1219    228     20  153   1946  650
## 3 1996     현대  126 4729 4127  493 1001    171     21  106   1532  462
## 4 1997     현대  126 4680 4032  497  994    179     19   82   1457  459
## 5 1998     현대  126 4779 4161  633 1123    216     21  142   1807  587
## 6 1999     현대  132 5260 4456  699 1210    187     18  143   1862  652
##   도루 도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1   97       57  396           37       20  713  114       51         33
## 2  192       67  547           63       22  905  116       64         48
## 3  112       90  396           72       35  747   66      104         30
## 4  112       78  424           72       33  720   92      122         28
## 5  134       85  413           74       16  820   69       91         38
## 6  109       65  537           98       22  780   88      127         42


물론 head()만 이렇게 쓸 수 있는 건 아닙니다. tail(arrange(kbo, desc(팀)), 5) 같은 표현도 가능합니다. 이러면 팀 이름을 내림차순으로 정렬한 데이터 가운데 끝에서 다섯 번째까지 데이터를 출력하게 됩니다.

tail(arrange(kbo, desc(팀)), 5)
##     연도  팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 279 2013 KIA  128 5051 4318  587 1125    195     13   88   1610  545  141
## 280 2014 KIA  128 5023 4443  662 1279    246     27  121   1942  631  121
## 281 2015 KIA  144 5454 4777  648 1197    223     22  136   1872  602  115
## 282 2016 KIA  144 5696 4999  803 1429    264     28  170   2259  754  101
## 283 2017 KIA  144 5841 5142  906 1554    292     29  170   2414  868   76
##     도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 279       47  537           75        8  864  110       78         42
## 280       63  416           62       11  870   95       67         35
## 281       50  454          101       16 1126  105       79         43
## 282       50  499           87       12  997  109       66         45
## 283       34  499           89       19  891  118       55         56


지금 보기는 식이 간단하지만 명령어가 길어지면 이렇게 함수(함수(함수(함수())))로 표시하기가 쉽지 않게 됩니다.


그래서 dplyr는 파이프(pipe)라는 기능을 가지고 있습니다. 파이프가 한 곳에서 다른 곳으로 물 같은 액체를 보낼 수 있게 도와주는 것처럼 이 녀석도 함수에서 다른 함수로 자료를 손쉽게 보내주는 구실을 합니다. R에서 파이프는 '%>%'로 표시합니다.


바로 위에서 쓴 명령어를 파이프를 넣어 바꾸면 arrange(kbo, desc(팀)) %>% tail(5)로 바꿔쓸 수 있습니다.

arrange(kbo, desc(팀)) %>% tail(5)
##     연도  팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 279 2013 KIA  128 5051 4318  587 1125    195     13   88   1610  545  141
## 280 2014 KIA  128 5023 4443  662 1279    246     27  121   1942  631  121
## 281 2015 KIA  144 5454 4777  648 1197    223     22  136   1872  602  115
## 282 2016 KIA  144 5696 4999  803 1429    264     28  170   2259  754  101
## 283 2017 KIA  144 5841 5142  906 1554    292     29  170   2414  868   76
##     도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 279       47  537           75        8  864  110       78         42
## 280       63  416           62       11  870   95       67         35
## 281       50  454          101       16 1126  105       79         43
## 282       50  499           87       12  997  109       66         45
## 283       34  499           89       19  891  118       55         56


수식이 간단해지는 것 말고 장점이 하나 더 있습니다. tail(arrange())로 쓴 것보다 이 쪽이 어떤 작업을 하는지 한눈에 더 잘 들어오지 않나요?



필요한 데이터만 뽑아내기

dplyr에는 데이터를 추출하는 함수가 두 개 들어있습니다. 행을 추출할 때는 filter(), 열을 추출할 때는 select()입니다.


그렇다면 2017년 데이터만 뽑아내고 싶을 때는 어떻게 하면 될까요?


filter()는 filter(데이터, 조건) 형태로 씁니다. 데이터는 계속 kbo입니다. 어느 해 데이터인지는 '연도'라는 열에 들어 있습니다.


그러면 filter(kbo, 연도=2017)이 될 겁니다.


그런데 이렇게 치면 'Error: `연도` (`연도 = 2017`) must not be named, do you need `==`?'라고 에러 메시지가 뜹니다.


저 설명이 알려주고 있는 것처럼 조건에서 똑같다고 표시할 때는 '='가 아니라 '=='라고 써야 합니다. '='는 어떤 변수에 데이터를 넣으라는 뜻이니까요.


그래서 정답은 filter(kbo, 연도==2017)이 됩니다. 파이프를 써서 앞부분만 확인하려면? 네, 그렇습니다. filter(kbo, 연도==2017) %>% head()가 정답입니다.

filter(kbo, 연도==2017) %>% head()
##   연도   팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 2017 두산  144 5833 5102  849 1499    270     20  178   2343  812   69
## 2 2017  KIA  144 5841 5142  906 1554    292     29  170   2414  868   76
## 3 2017   NC  144 5790 5079  786 1489    277     19  149   2251  739   93
## 4 2017 넥센  144 5712 5098  789 1479    267     30  141   2229  748   70
## 5 2017   SK  144 5564 4925  761 1337    222     15  234   2291  733   53
## 6 2017 롯데  144 5671 4994  743 1425    250     17  151   2162  697   92
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       33  541           95       19  960  120       48         47
## 2       34  499           89       19  891  118       55         56
## 3       32  471          130       19  979  119       62         48
## 4       34  468           83       15 1066  129       21         42
## 5       42  427          113       20 1100   97       57         41
## 6       44  457          112       33 1018  146       76         32


그러면 2017년 자료 중에서 안타, 2루타, 3루타, 홈런만 확인하고 싶을 때는 어떻게 할까요?


이번에는 열을 선택해야 하니까 select()를 씁니다. 위에서 살펴본 것처럼 R 변수 이름 규칙 때문에 바뀐 게 있으니까 select(kbo, 안타, X2루타, X3루타, 홈런)으로 쓰면 됩니다.

select(kbo, 안타, X2루타, X3루타, 홈런)

이렇게 써도 잘 작동하지만 사실 이건 R에서 데이터를 표시하는 원칙에는 맞지 않습니다. R는 이렇게 데이터를 한꺼번에 묶어야 할 때 '묶는다'는 뜻인 'concatenate'를 줄인 c를 쓰도록 하고 있습니다. 그냥 c(안타, X2루타, X3루타, 홈런)으로 쓰면 그만입니다. 그러면 select(kbo, c(안타, X2루타, X3루타, 홈런))이 됩니다.


아직은 연도를 적용하기 전이라서 이렇게 치면 1982년부터 2017년까지 자료가 모두 뜹니다. 2017년만 뽑아 내려면 filter()를 써야겠죠? 이때는 데이터가 kbo가 아니라 위에서 본 것처럼 filter(kbo, 연도==2017)이 됩니다.


만약 파이프가 없는 상태로 head()까지 써서 이를 나타내려면 'head(select(filter(kbo, 연도==2017), c(안타, X2루타, X3루타, 홈런)))' 이렇게 쓰면 됩니다. 괄호를 어디에 어떻게 써야 할지도 헷갈릴 지경입니다.


우리는 파이프를 아니까 이렇게 쓸 필요가 없습니다.

filter(kbo, 연도==2017) %>% select(안타, X2루타, X3루타, 홈런) %>% head()
##   안타 X2루타 X3루타 홈런
## 1 1499    270     20  178
## 2 1554    292     29  170
## 3 1489    277     19  149
## 4 1479    267     30  141
## 5 1337    222     15  234
## 6 1425    250     17  151


어떤가요? 이 정도만 전체 명령어가 길어져도 파이프를 쓰는 편이 훨씬 편하고 직관적이지 않습니까?


아, 경우에 따라서는 특정 열만 빼고 선택하고 싶으실 때가 있을 겁니다. 이때는 변수 이름 앞에 '-'를 붙여주시면 됩니다.


그러니까 2017년 데이터 가운데 안타, 2루타, 3루타, 홈런을 빼고 싶으시면 이렇게 쓰시면 되는 겁니다.

filter(kbo, 연도==2017) %>% select(-안타, -X2루타, -X3루타, -홈런) %>% head()
##   연도   팀 경기 타석 타수 득점 총루타 타점 도루 도루실패 볼넷
## 1 2017 두산  144 5833 5102  849   2343  812   69       33  541
## 2 2017  KIA  144 5841 5142  906   2414  868   76       34  499
## 3 2017   NC  144 5790 5079  786   2251  739   93       32  471
## 4 2017 넥센  144 5712 5098  789   2229  748   70       34  468
## 5 2017   SK  144 5564 4925  761   2291  733   53       42  427
## 6 2017 롯데  144 5671 4994  743   2162  697   92       44  457
##   몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1           95       19  960  120       48         47
## 2           89       19  891  118       55         56
## 3          130       19  979  119       62         48
## 4           83       15 1066  129       21         42
## 5          113       20 1100   97       57         41
## 6          112       33 1018  146       76         32


계산하고 붙여 넣자

야구에서 타자 기록 중 가장 기본이 되는 건 타율이고 '안타÷타수'로 계산합니다. kbo 데이터에는 아직 이 기록이 없습니다. 그러면 새로 열을 만들어 넣어야 할 겁니다.


이럴 때 쓰는 함수는? 네, 그렇습니다. mutate()입니다.


이 함수는 'mutate(데이터, 새 변수 = 계산식)' 형태로 사용합니다.


우리는 kbo라는 데이터에 타율이라는 새 열을 만드는데, 그 안에 '안타/타수'를 넣으려고 합니다. (÷와 /가 같은 뜻인 거 설마 모르시지 않겠죠?) 이를 종합하면 이렇게 쓰면 됩니다.

kbo %>% mutate(타율=안타/타수) %>% head()
##   연도   팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 1982  MBC   80 3061 2686  419  757    124     12   65   1100  381  134
## 2 1982 삼성   80 3043 2647  429  705    126     18   57   1038  374  147
## 3 1982   OB   80 3098 2745  399  778    137     23   57   1132  362  106
## 4 1982 해태   80 2990 2665  374  696    110     14   84   1086  332  155
## 5 1982 롯데   80 3062 2628  353  674    112      8   59    979  325   83
## 6 1982 삼미   80 2954 2653  302  637    117     20   40    914  272   74
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       60  268           47       20  316   56       32         27
## 2       42  307           30        2  349   50       36         18
## 3       61  247           41       22  254   35       46         18
## 4       52  235           41       12  296   59       28         21
## 5       53  326           40        8  315   61       41         27
## 6       43  221           29        3  369   44       33         17
##        타율
## 1 0.2818317
## 2 0.2663393
## 3 0.2834244
## 4 0.2611632
## 5 0.2564688
## 6 0.2401055


파이프를 쓰는 방식이 조금 달라진 것 눈치채셨습니까? 지금까지는 첫 함수에는 데이터 이름을 따로 넣었습니다. 그러니까 이렇게 썼던 것.

mutate(kbo, 타율=안타/타수) %>% head()


그런데 사실은 데이터부터 곧바로 파이프를 써도 관계가 없습니다. 그리고 이 쪽이 더 직관적입니다. 그렇죠?


이렇게 타율을 구하고 나니 연도별 리그 평균 타율을 계산하고 싶다는 생각이 듭니다. 이럴 때는 어떻게 하면 될까요?



그루핑(grouping) 그리고 tibble

네, 이럴 때 쓰라고 group_by() 함수가 기다리고 있습니다. 우리는 연도를 기준으로 묶고 싶은 거니까 group_by(연도)라고 쓰면 그만입니다. 이렇게 말입니다.

kbo %>% group_by(연도)
## # A tibble: 283 x 21
## # Groups:   연도 [36]
##     연도 팀     경기  타석  타수  득점  안타 X2루타 X3루타  홈런 총루타
##    <int> <fct> <int> <int> <int> <int> <int>  <int>  <int> <int>  <int>
##  1  1982 MBC      80  3061  2686   419   757    124     12    65   1100
##  2  1982 삼성     80  3043  2647   429   705    126     18    57   1038
##  3  1982 OB       80  3098  2745   399   778    137     23    57   1132
##  4  1982 해태     80  2990  2665   374   696    110     14    84   1086
##  5  1982 롯데     80  3062  2628   353   674    112      8    59    979
##  6  1982 삼미     80  2954  2653   302   637    117     20    40    914
##  7  1983 삼성    100  3847  3383   448   889    143     14    90   1330
##  8  1983 해태    100  3734  3340   423   892    130     15    78   1286
##  9  1983 MBC     100  3715  3273   405   837    145     21    45   1159
## 10  1983 OB      100  3766  3330   418   863    142     26    50   1207
## # ... with 273 more rows, and 10 more variables: 타점 <int>, 도루 <int>,
## #   도루실패 <int>, 볼넷 <int>, 몸에.맞는.공 <int>, 고의사구 <int>,
## #   삼진 <int>, 병살 <int>, 희생번트 <int>, 희생플라이 <int>


이상한 모양이 등장했습니다. 이렇게 생긴 데이터 형태를 (위에 나온 것처럼) tibble 또는 tbl_df라고 부릅니다. 이때 df는 데이터 프레임(Data Frame)이라는 뜻입니다.


요컨대 tibble은 데이터 프레임을 현대적으로 업그레이드한 형태라고 생각하시면 됩니다.


데이터 프레임은 그럼 뭘까요? MS 엑셀이 데이터를 다루는 방식이 바로 데이터 프레임입니다. 지금까지 우리가 사용한 CSV도 마찬가지.


그러니까 행과 열이 있는 표 형태로 데이터를 저장하는 방식을 데이터 프레임이라고 생각하시면 쉽습니다. 


자, 다시 본론으로 돌아와서 우리는 연도별 리그 평균 타율을 구하려고 합니다. 리그 기록은 기본적으로 각 구단 기록을 모두 더한 결과물입니다.


R에서 이렇게 모두 더하라는 함수는 sum()입니다. 타율은 여전히 '안타÷타수'로 계산할 수 있으니까 '(리그) 타율 = sum(안타)/sum(타수)'가 됩니다. 


이 계산식을 어디에 넣어야 할까요? 그렇습니다. summarise()입니다. 리그 평균을 구하는 것도 전체 데이터를 요약하는 작업입니다. 이렇게 생각하면 summarise() 함수를 쓰는 게 당연한 일입니다.이미 연도별 그루핑은 해놓은 상태니까 이렇게 쓰면 됩니다.

kbo %>% group_by(연도) %>% summarise(타율=sum(안타)/sum(타수))
## # A tibble: 36 x 2
##     연도  타율
##    <int> <dbl>
##  1  1982 0.265
##  2  1983 0.256
##  3  1984 0.254
##  4  1985 0.260
##  5  1986 0.251
##  6  1987 0.265
##  7  1988 0.268
##  8  1989 0.257
##  9  1990 0.257
## 10  1991 0.256
## # ... with 26 more rows


예상대로 잘 나왔습니다. 꼭 mutate()를 쓰시고 싶거나 총 안타와 총 타수를 표시하고 싶으실 때는 이렇게 쓰셔도 됩니다.

kbo %>% group_by(연도) %>% summarise(총안타=sum(안타), 총타수=sum(타수)) %>% mutate(타율=총안타/총타수)
## # A tibble: 36 x 4
##     연도 총안타 총타수  타율
##    <int>  <int>  <int> <dbl>
##  1  1982   4247  16024 0.265
##  2  1983   5102  19951 0.256
##  3  1984   4995  19703 0.254
##  4  1985   5700  21890 0.260
##  5  1986   6234  24879 0.251
##  6  1987   6666  25125 0.265
##  7  1988   6747  25163 0.268
##  8  1989   7099  27654 0.257
##  9  1990   7077  27566 0.257
## 10  1991   8514  33233 0.256
## # ... with 26 more rows


우리는 타율만 필요한 상태니까 처음에 썼던 버전을 계속 쓰겠습니다. 



필요한 데이터만 뽑아내기 #2

이번에는 계산 결과 중에서 필요한 데이터만 뽑아내는 걸 연습해 보겠습니다. 연도별 타율 가운데 2001년 리그 평균 타율을 구하고 싶을 때는 어떻게 해야 할까요? 별 거 없습니다. 파이프를 이어가기면 하면 됩니다.

kbo %>% group_by(연도) %>% summarise(타율=sum(안타)/sum(타수)) %>% filter(연도==2001)
## # A tibble: 1 x 2
##    연도  타율
##   <int> <dbl>
## 1  2001 0.274


난도를 조금 높여보겠습니다. 리그 평균 타율이 제일 높았던 연도를 알아보려면 어떻게 해야 할까요? 힌트는 R에서 최댓값을 구하는 함수는 max()라는 점입니다. 어떻게요?

kbo %>% group_by(연도) %>% summarise(타율=sum(안타)/sum(타수)) %>% filter(타율==max(타율))
## # A tibble: 1 x 2
##    연도  타율
##   <int> <dbl>
## 1  2016 0.290


아, 그런데 여기서 아쉬운 점이 있습니다. 명령어 전체가 너무 길다 보니까 화면에서 잘립니다. 화면에서 보이지 않는 거야 저처럼 이렇게 설명을 드리는 처지가 아니라면 별 관계가 없는 게 사실. 그래도 잘 보이지 않으면 직관적으로 명령을 이해하기가 쉽지 않습니다.


그럴 때는 그냥 행을 나누면 됩니다. 이렇게 말입니다.

kbo %>%
group_by(연도) %>%
summarise(타율=sum(안타)/sum(타수)) %>%
filter(타율==max(타율))

위에 있는 코드를 그냥 그대로 콘솔에 붙여 넣으셔도 한 줄로 된 코드와 100% 똑같이 작동합니다. 그냥 행갈이만 한 거니까요.



빈 방 채우기

지금까지는 곧바로 결과를 확인하려고 명령어를 전부 한 줄에 넣었지만 데이터를 처리한 걸 별도로 빈 방에 담아 새 데이터를 만들 수도 있습니다.


예를 들어 연도별 타율(batting average)을 avg라는 함수에 넣고 싶다면 이렇게 쓰면 됩니다.

avg <- kbo %>% group_by(연도) %>% summarise(타율=sum(안타)/sum(타수))
avg
## # A tibble: 36 x 2
##     연도  타율
##    <int> <dbl>
##  1  1982 0.265
##  2  1983 0.256
##  3  1984 0.254
##  4  1985 0.260
##  5  1986 0.251
##  6  1987 0.265
##  7  1988 0.268
##  8  1989 0.257
##  9  1990 0.257
## 10  1991 0.256
## # ... with 26 more rows


혹시 잊으셨을까 봐 말씀드리면 데이터(변수) 이름만 콘솔에 치는 건 그 변수 내용을 보여달라는 뜻입니다.


물론 행갈이를 해서 저 내용을 이런 식으로 써도 같은 결과를 얻을 수 있습니다.

avg <- kbo %>%
group_by(연도) %>%
summarise(타율=sum(안타)/sum(타수))


사실은 변수 입력 기호 방향을 반대로 그러니까 파이프 방향으로 써도 자료가 들어갑니다.

 kbo %>%
group_by(연도) %>%
summarise(타율=sum(안타)/sum(타수)) -> avg
avg
## # A tibble: 36 x 2
##     연도  타율
##    <int> <dbl>
##  1  1982 0.265
##  2  1983 0.256
##  3  1984 0.254
##  4  1985 0.260
##  5  1986 0.251
##  6  1987 0.265
##  7  1988 0.268
##  8  1989 0.257
##  9  1990 0.257
## 10  1991 0.256
## # ... with 26 more rows


이렇게 연도별 자료를 계산하고 나니 선 그래프로 추이를 확인하고 싶어지시죠? ggplot2라는 패키지를 써서 그려보도록 하겠습니다. ggplot2 역시 dplyr처럼 tidyverse 패키지 가운데 일부로 시각화를 도와주는 구실을 합니다. ggplot2를 어떻게 쓰는지 알아보고 싶으신 분은 '최대한 친절하게 쓴 R로 그래프 그리기(feat. ggplot2)' 포스트가 도움이 될 수 있습니다.


R에서 패키지를 쓰려면 제일 먼저 해야 할 일은? 네, install.packages() 함수로 패키지를 설치하는 겁니다. 그리고 library() 함수로 패키지를 로드해야 합니다.

install.packages('ggplot2')
library('ggplot2')


이어서 x축은 연도, y축은 타율을 나타내는 선 그래프를 그리라고 명령해 보겠습니다.

ggplot(data=avg, aes(x=연도, y=타율)) + geom_line()


예쁘게 잘 나왔습니다. 물론 파이프만 계속 써서 똑같은 그림을 그릴 수도 있습니다. 이렇게 말입니다.

kbo %>% 
group_by(연도) %>%&
summarise(타율=sum(안타)/sum(타수))  %>%
ggplot(., aes(x=연도, y=타율)) + geom_line()


원래 data=avg가 차지하고 있던 자리를 '.'이 차지해야 한다는 것만 생각하면 어려울 게 없는 코드입니다.



연습 문제 풀이

이제 dplyr를 어떻게 쓰는지 어느 정도 감을 잡을셨을 줄로 믿습니다. 지금부터는 연습 문제 형태로 조금 더 디테일한 세계로 들어가 보겠습니다.


문1. 타격 기록 중 가장 기본이 되는 타율/출루율/장타력과 OPS(출루율+장타력) 변수(열)를 만드시오.


답1. 바로 mutate() 함수를 떠올리셨죠? 그렇다면 이제 필요한 건 공식뿐입니다. 타율은 이미 '안타 ÷ 타수'라는 걸 알고 있습니다. 출루율과 장타력은 아래 공식으로 계산합니다.



그런데 kbo 데이터에는 이미 '단타 + 2×2루타 + 3×3루타 + 4×홈런'을 계산한 변수가 들어 있습니다. 바로 총루타입니다. 그래서 장타력은 '총루타 ÷ 타수'로 바꿀 수 있습니다. OPS는 문자 그대로 '출루율 + 장타력'이 전부입니다. 그래서 이렇게 쓰면 됩니다. (※결과에는 head 적용)

kbo <- mutate(kbo, 타율=안타/타수,
출루율 = (안타+볼넷+몸에.맞는.공)/(타수+볼넷+몸에.맞는.공+희생플라이),
장타력=총루타/타수,
ops=출루율+장타력)
##   연도   팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 1982  MBC   80 3061 2686  419  757    124     12   65   1100  381  134
## 2 1982 삼성   80 3043 2647  429  705    126     18   57   1038  374  147
## 3 1982   OB   80 3098 2745  399  778    137     23   57   1132  362  106
## 4 1982 해태   80 2990 2665  374  696    110     14   84   1086  332  155
## 5 1982 롯데   80 3062 2628  353  674    112      8   59    979  325   83
## 6 1982 삼미   80 2954 2653  302  637    117     20   40    914  272   74
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       60  268           47       20  316   56       32         27
## 2       42  307           30        2  349   50       36         18
## 3       61  247           41       22  254   35       46         18
## 4       52  235           41       12  296   59       28         21
## 5       53  326           40        8  315   61       41         27
## 6       43  221           29        3  369   44       33         17
##        타율    출루율    장타력       ops
## 1 0.2818317 0.3540291 0.4095309 0.7635600
## 2 0.2663393 0.3471019 0.3921420 0.7392440
## 3 0.2834244 0.3493936 0.4123862 0.7617798
## 4 0.2611632 0.3281567 0.4075047 0.7356613
## 5 0.2564688 0.3442569 0.3725266 0.7167835
## 6 0.2401055 0.3037671 0.3445156 0.6482828



문2. OPS가 0.7 이상이면서 팀 홈런이 70개 미만이 몇 개인지 구하시오.


답2. '~면서'라는 건 '그리고(and)'라는 뜻입니다. 이렇게 논리적인 접근이 필요할 때는 '논리 연산자'를 씁니다. R에서 그리고는 '&'로 표시합니다. '또는'(or)은 '|'입니다. 같은 건 '=='라는 갈 아실 테고 다른 건 '!='입니다. 또 크다(>), 크거나 같다(>=),  작다(<), 작거나 같다(<=) 같은 연산자도 씁니다.


따라서 ops가 0.7 이상인 건 'ops >= 0.7', '~면서'는 '&', 홈런 70개 미만은 '홈런 < 70'라고 쓸 수 있습니다. 이걸 어디에 넣어야 할까요? 네, filter()입니다. filter(ops >= 0.7 & 홈런 < 70)처럼 쓰면 됩니다.


이를 파이프로 연결하면 이렇게 쓸 수 있습니다.

kbo %>% filter(ops >= 0.7 & 홈런 < 70)
##    연도     팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점
## 1  1982    MBC   80 3061 2686  419  757    124     12   65   1100  381
## 2  1982   삼성   80 3043 2647  429  705    126     18   57   1038  374
## 3  1982     OB   80 3098 2745  399  778    137     23   57   1132  362
## 4  1982   롯데   80 3062 2628  353  674    112      8   59    979  325
## 5  1985     OB  110 4189 3667  473  954    146     27   67   1355  432
## 6  1987   롯데  108 4155 3625  432  970    161     27   40   1305  400
## 7  1987 빙그레  108 4054 3547  410  972    144     22   48   1304  370
## 8  1988   롯데  108 4095 3578  482  967    142     18   68   1349  446
## 9  1990     LG  120 4661 4024  578 1089    156     40   61   1508  528
## 10 1992   롯데  126 4904 4205  663 1213    211     62   68   1752  619
## 11 1996 쌍방울  126 4805 4111  545 1085    192     33   58   1517  489
## 12 2005   두산  133 4922 4256  596 1147    183     17   63   1553  558
## 13 2008   두산  126 4975 4344  647 1198    166     37   68   1642  598
## 14 2013     LG  128 4934 4291  616 1210    194     38   59   1657  574
## 15 2013   롯데  128 4978 4289  556 1118    208     18   61   1545  522
##    도루 도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1   134       60  268           47       20  316   56       32         27
## 2   147       42  307           30        2  349   50       36         18
## 3   106       61  247           41       22  254   35       46         18
## 4    83       53  326           40        8  315   61       41         27
## 5   107       76  354           50       19  442   54       87         31
## 6    99       50  390           34       20  330  102       86         20
## 7    79       85  326           53       11  423   84      104         23
## 8   127       64  347           40       16  333   81      104         24
## 9   140      101  465           65       24  450   82       56         50
## 10  130       55  483           59       14  542   90      103         53
## 11   85       59  427           70       34  690   89      143         47
## 12  103       50  465           85       14  701   93       81         35
## 13  189       63  472           78       23  719  115       36         44
## 14  139       71  451           59       11  806   80       91         42
## 15  133       58  502           72       22  862   92       74         41
##         타율    출루율    장타력       ops
## 1  0.2818317 0.3540291 0.4095309 0.7635600
## 2  0.2663393 0.3471019 0.3921420 0.7392440
## 3  0.2834244 0.3493936 0.4123862 0.7617798
## 4  0.2564688 0.3442569 0.3725266 0.7167835
## 5  0.2601582 0.3310580 0.3695119 0.7005699
## 6  0.2675862 0.3425903 0.3600000 0.7025903
## 7  0.2740344 0.3421119 0.3676346 0.7097465
## 8  0.2702627 0.3394334 0.3770263 0.7164597
## 9  0.2706262 0.3516507 0.3747515 0.7264022
## 10 0.2884661 0.3656250 0.4166468 0.7822718
## 11 0.2639261 0.3398496 0.3690100 0.7088596
## 12 0.2695019 0.3505474 0.3648966 0.7154440
## 13 0.2757827 0.3539895 0.3779926 0.7319821
## 14 0.2819856 0.3551518 0.3861571 0.7413088
## 15 0.2606668 0.3450245 0.3602238 0.7052483


이 코드를 그대로 실행하면 답이 15개라는 걸 어렵지 않게 확인할 수 있습니다. 자료가 너무 많아서 세기 어려울 때 직접 숫자를 출력하게 하려면 어떻게 해야 할까요? 개수를 세어주는 함수는 n()입니다. 우리는 이미 자료를 요약했습니다. 그렇다면 summarise() 함수를 쓰지 못할 이유가 없겠죠?

kbo %>%
filter(ops >= 0.7 & 홈런 < 70) %>%
summarise(n())
##   n()
## 1  15


그렇다면 ops가 0.7 미만이거나 홈런이 70개 이하인 팀이 몇 팀인지 구하려면 어떻게 쓰면 될까요? 네 이번에는 '또는'을 활용하면 되니까 '|'를 쓰면 됩니다.

kbo %>%
filter(ops < 0.7 | 홈런 >= 70) %>%
summarise(n())
##   n()
## 1 268



문3. 문2에서 뽑은 15개 팀 중 1982~1990년 사이 팀을 골라 각각 경기당 평균 득점과 도루 성공률을 구하시오.


답3. 일단 팀을 뽑을 때까지는 똑같으니까 filter() 부분까지는 똑같을 겁니다. 그다음 할 일은 1982~1990년 사이에 속하는 팀을 골라야겠죠? 이때는 여러 가지 방법이 있을 수 있지만 (당장 뒤에서도 다른 방식으로 계산합니다.) 우리는 위에서 잠깐 나온 c()를 써보도록 하겠습니다.


R에서는 ':'를 통해 연속해서 숫자를 표시할 수 있습니다. 예를 들어 콘솔에 1:5라고 쓰면 '[1] 1 2 3 4 5'라는 결과가 나옵니다. 마찬가지로 1982:1990이라고 쓰면 1982부터 1990까지 출력합니다. 이걸 한 데 묶어 두려면 c(1982:1990)으로 쓰면 그만입니다.


1982~1990년 사이 팀을 고르려면 연도가 이 범위 안에 들어있어야겠죠? 이럴 때는 '%in%'이라는 연산자를 쓰면 됩니다. '연도 %in% c(1982:1990)'이라고 쓰면 되는 것. (위에서 살펴본 것처럼 이때도 c를 쓰는 게 정석이지만 빼도 정상 작동합니다.)


이를 토대로 위 코드에서 filter를 이렇게 수정할 수 있습니다. 이번에도 그리고(and) 사례니까 '&' 기호를 써야겠죠?

kbo %>%
filter(ops >= 0.7 & 홈런 < 70 & 연도 %in% 1982:1990)
##   연도     팀 경기 타석 타수 득점 안타 X2루타 X3루타 홈런 총루타 타점 도루
## 1 1982    MBC   80 3061 2686  419  757    124     12   65   1100  381  134
## 2 1982   삼성   80 3043 2647  429  705    126     18   57   1038  374  147
## 3 1982     OB   80 3098 2745  399  778    137     23   57   1132  362  106
## 4 1982   롯데   80 3062 2628  353  674    112      8   59    979  325   83
## 5 1985     OB  110 4189 3667  473  954    146     27   67   1355  432  107
## 6 1987   롯데  108 4155 3625  432  970    161     27   40   1305  400   99
## 7 1987 빙그레  108 4054 3547  410  972    144     22   48   1304  370   79
## 8 1988   롯데  108 4095 3578  482  967    142     18   68   1349  446  127
## 9 1990     LG  120 4661 4024  578 1089    156     40   61   1508  528  140
##   도루실패 볼넷 몸에.맞는.공 고의사구 삼진 병살 희생번트 희생플라이
## 1       60  268           47       20  316   56       32         27
## 2       42  307           30        2  349   50       36         18
## 3       61  247           41       22  254   35       46         18
## 4       53  326           40        8  315   61       41         27
## 5       76  354           50       19  442   54       87         31
## 6       50  390           34       20  330  102       86         20
## 7       85  326           53       11  423   84      104         23
## 8       64  347           40       16  333   81      104         24
## 9      101  465           65       24  450   82       56         50
##        타율    출루율    장타력       ops
## 1 0.2818317 0.3540291 0.4095309 0.7635600
## 2 0.2663393 0.3471019 0.3921420 0.7392440
## 3 0.2834244 0.3493936 0.4123862 0.7617798
## 4 0.2564688 0.3442569 0.3725266 0.7167835
## 5 0.2601582 0.3310580 0.3695119 0.7005699
## 6 0.2675862 0.3425903 0.3600000 0.7025903
## 7 0.2740344 0.3421119 0.3676346 0.7097465
## 8 0.2702627 0.3394334 0.3770263 0.7164597
## 9 0.2706262 0.3516507 0.3747515 0.7264022


이제 남은 과제는 경기당 평균 득점과 도루 성공률을 구하는 것뿐입니다. 경기당 평균 득점은 '득점 ÷ 경기 (숫자)'로 계산하면 되고, 도루 성공률은 '도루 ÷ (도루 + 도루실패)로 계산합니다.


그러면 필요한 변수(열)는 경기, 득점, 도루, 도루실패가 됩니다 = select(경기, 득점, 도루, 도루실패). 이걸 다시 공식에 따라 계산해서 붙여 넣으면 되니까 mutate(평균득점 = 득점/경기, 도루성공률=도루/(도루+도루실패))라고 정리할 수 있습니다. 연도랑 팀도 같이 나오게 써볼까요?

kbo %>%
filter(ops >= 0.7 & 홈런 < 70 & 연도 %in% 1982:1990) %>%
select(연도, 팀, 경기, 득점, 도루, 도루실패) %>%
mutate(평균득점 = 득점/경기, 도루성공률=도루/(도루+도루실패))
##   연도     팀 경기 득점 도루 도루실패 평균득점 도루성공률
## 1 1982    MBC   80  419  134       60 5.237500  0.6907216
## 2 1982   삼성   80  429  147       42 5.362500  0.7777778
## 3 1982     OB   80  399  106       61 4.987500  0.6347305
## 4 1982   롯데   80  353   83       53 4.412500  0.6102941
## 5 1985     OB  110  473  107       76 4.300000  0.5846995
## 6 1987   롯데  108  432   99       50 4.000000  0.6644295
## 7 1987 빙그레  108  410   79       85 3.796296  0.4817073
## 8 1988   롯데  108  482  127       64 4.462963  0.6649215
## 9 1990     LG  120  578  140      101 4.816667  0.5809129



문4. 1982~1990년, 1991~2000년, 2001~2010년, 2011~2017년 평균 희생번트 숫자를 구하시오. 


답4. 이번에는 다른 방법으로 그룹을 구분해 보도록 하겠습니다. 여러 그룹으로 자료를 분류할 때는 그룹 이름으로 새로운 변수(열)을 만들어 사용하면 편합니다. 그러니까 어떤 행이 △1982~1990년 △1991~2000년 △2001~2010년 △2011~2017년에 각각 속한다는 걸 알려주는 이름 변수를 추가하는 겁니다. 예를 들어 이 네 개 그룹에 각각 A~D라는 이름을 붙일 수 있을 겁니다.


이 이름을 붙여주려면 조건문이라는 걸 쓰면 됩니다. '연도' 변수가 1982~1990에 속해 있다면 '그룹'이라는 열에 A라는 값을 추가하는 방식입니다. R에서 조건문을 쓸 때는 if()라는 함수를 쓰는 게 제일 일반적입니다. 이런 구조입니다.

if(연도<=1990){
그룹 <- "A"
} else{
if(연도<=2000){
그룹 <- "B"
} else{
if(연도<=2010){
그룹 <- "C"
}
else{
그룹 <- "D"
}}}


if와 else를 계속 반복하니까 좀 지겹죠? 그래서 R에는 ifelse()라는 함수도 들어 있습니다. ifelse()는 MS 엑셀에서 if 함수를 활용할 때와 같은 방식입니다. 그래서 이 코드를 이렇게 쓸 수 있습니다.

ifelse(연도<=1990, 그룹 <-"A",
ifelse(연도<=2000, 그룹 <- "B",
ifelse(연도<=2010, 그룹 <- "C", 그룹 <- "D")))


우리는 이 그룹을 새로운 열에 추가하기로 했으니까 mutate()를 써야겠죠? 그다음 파이프로 연결하면 이렇게 됩니다.

kbo %>% mutate(그룹 = ifelse(연도<=1990, "A",
ifelse(연도<=2000, "B",
ifelse(연도<=2010, "C", "D"))))


이제 그룹으로 묶고 평균을 내면 됩니다. 그러면 group_by(), summarise()를 쓰면 되겠죠?

kbo %>% mutate(그룹 = ifelse(연도<=1990, "A",
ifelse(연도<=2000, "B",
ifelse(연도<=2010, "C", "D")))) %>% 
group_by(그룹) %>%
summarise(평균희생번트=mean(희생번트))
## # A tibble: 4 x 2
##   그룹  평균희생번트
##   <chr>        <dbl>
## 1 A             71.1
## 2 B             83.7
## 3 C             83.3
## 4 D             77.8



문5. 통산 병살타가 가장 많은 세 개 팀을 고르시오.


답5. 마지막이라 쉬운 문제로 준비했습니다. 답은 아래와 같습니다.

kbo %>% group_by(팀) %>%
summarise(병살=sum(병살)) %>%
arrange(desc(병살)) %>%
head(3)
## # A tibble: 3 x 2
##   팀     병살
##   <fct> <int>
## 1 삼성   3392
## 2 롯데   3273
## 3 LG    2687


그냥 팀별로 그루핑을 한 다음 병살타 합계를 요약하고 내림차순으로 그 결과를 정렬해서 그 가운데 제일 위에 나온 세 개만 뽑으면 그만입니다.



고의사구 타석을 남겨두면서

지금까지 쓰고 보니 결국 타석과 고의사구 변수는 한 번도 사용하지 않았습니다. 이 변수를 사용하면 '타석당 고의사구가 가장 높은 팀은 어디인가?', '타석당 고의사구가 가장 높았던 시즌은?' 같은 문제를 만들어 풀 수 있을 겁니다.


그리고 위에서 사용한 함수 여섯 가지가 dplyr 패키지 기능 전부인 것도 아닙니다. 기본 원리를 공부하려고 제일 많이 쓰는 여섯 가지만 소개해 드린 겁니다. 나머지 함수는 아래 있는 dplyr 커닝 페이퍼를 통해 사용법을 살펴보시면 됩니다. (내려 받으시면 큰 크기로 보실 수 있습니다. 이미지 두 장입니다.) 



혹시 이 포스트 내용 중 이해가 가지 않거나 잘못된 게 있으면 댓글 등을 통해 언제든 알려주세요. 같이 공부하면서 고쳐나가도록 하겠습니다.


댓글, 10

  • 댓글 수정/삭제 홍 아무개
    2018.09.14 22:03

    그 동안 dplyr 공부하면서 이해 안 되는 부분이 많았는데, 이 글 보고 몇 번 연습해보니 완전히 깨치게 됐어요. 감사합니다, 기자님!

  • 댓글 수정/삭제 ㅇㅇ
    2019.03.29 00:38

    이 정도면 기초 수준인가요? 잘 배우겠습니다!

  • 댓글 수정/삭제 dd
    2019.04.01 15:38

    단일시즌 팀 스탯은 구하기가 참 힘드네요..

  • 댓글 수정/삭제 욕칸다
    2019.04.16 18:59

    파이프 연산자 의 순서를 주의 해야하는거죠? 그리고 파이프연산자를 쓰고 head(, 5)를 기입하셨는데 나오는 결과물은 6줄이 나오는데 그건 왜그런거죠?

    •  수정/삭제 kini
      2019.04.17 16:05 신고

      중요한 지적을 해주셨습니다.
      앞에 있는 자료를 그대로 쓴다는 뜻으로 '.'을 찍었어야 했는데 제가 빼먹었습니다.
      head(., 5)처럼 쓰시는 게 맞습니다.

  • 댓글 수정/삭제 어쩔수 없이 시작하는 R
    2020.07.29 15:29

    연습 문제에 보면 '몸에 맞는 공' 이라고 띄어쓰기 된 부분이 에러가나는데 2020년에 들어서 바꿔서그런지안도네요. 2루타의 경우도X2루타가 아닌 '2루타' 라고 하면되는데 띄어쓰기된 colum은 안되네요. 어떻게하는게 좋을까요?

    •  수정/삭제 kini
      2020.08.03 00:14 신고

      일단 지금 '몸에.맞는.공', 'X2루타'라고 써도 잘 작동하는 걸 확인했습니다.

      혹시 사용하신 코드를 보여주실 수 있나요?

      그나저자 연습 문제에 몸에 맞는 공이나 2루타가 등장하지 않는 것 같은데요?

account_circle
vpn_key
web

security

mode_edit
KindeR | 카테고리 다른 글 더 보기