티스토리 뷰
magrittr 패키지
tidyverse 패키지를 사용해 데이터를 정제하다보면 자연스레 %>% 연산자를 자주 사용하게 됩니다. 그리고 %>% 연산자는 따로 import 하지 않고 사용하기 때문에 그냥 dplyr 에 포함되어 있는 연산자인가보다 라고 생각하지만 사실 magrittr 이라는 패키지에 속해있는 연산자 입니다.
파이프 연산자의 역사를 보다 자세히 알고 싶으신 분은 이 url 을 통해 더 공부하셔도 좋을 것 같습니다. %^^%
https://www.datacamp.com/community/tutorials/pipe-r-tutorial
tidyverse 홈페이지에서 magrittr 관련 페이지에 접속하면 이 패키지에 대한 간단한 설명을 확인할 수 있습니다. 더 자세한 사항들은 Vignette 를 통해 알 수 있습니다.
https://cran.r-project.org/web/packages/magrittr/magrittr.pdf
한 마디로 정의하면 magrittr 패키지는 코드의 가독성을 높이는 연산자들을 모아놓은 패키지 라고 할 수 있습니다. 그 중 자주 사용하는 3가지 연산자를 살펴볼 예정입니다.
%>% 연산자 사용법
%>% 연산자는 tidyverse 패키지를 attach 하거나 magrittr 패키지를 직접 attach 하는 방법을 통해 사용할 수 있습니다.
코드를 작성하다보면 위의 경우와 같이 계산한 결과값이 다른 함수의 argument 로 사용되는 경우가 빈번합니다. 그림을 살펴보면 x 값이 f 라는 함수의 인수로 사용되어 f( x ) 라는 결과값을 만들어내는데 그 f( x ) 가 g 함수의 인수로 다시 사용되고 그 결과값인 g( f( x ) ) 가 h 함수의 인수로 다시 사용되어 결과값 y 를 만들어 내는 것을 확인할 수 있습니다.
이런 경우에 괄호의 중첩이 발생하는데 2~3회 까지는 큰 문제 없을 수 있으나 각 함수의 인수가 많아지고 괄호의 중첩 단계가 높아지면 코드를 작성하는 입장에서도 코드를 읽는 입장에서도 매우 혼란스러운 상황이 발생하곤 합니다.
이런 상황을 개선하기 위해 나온 연산자가 %>% 연산자 입니다.
위의 식 y = h( g( f( x ) ) ) 를 %>% 연산자를 사용하여 다시 표현하면 다음과 같이 표현이 가능합니다.
y = x %>% f( . ) %>% g( . ) %>% h( . )
예시를 통해 더 자세히 살펴볼까요
runif 라는 함수를 사용하는 예시 입니다. runif 라는 함수는 난수를 발생시키는 함수로 인자로는 n (난수 발생 개수), min (난수의 최소값), max (난수의 최대값) 총 3개가 있습니다.
random_number_count 라는 변수를 지정하고 random_numbers 를 만들어볼까요. 위의 3가지 방법은 모두 같은 결과값을 리턴합니다.
1번 방법은 %>% 를 사용하지 않고 n 에 직접 random_number_count 를 대입하여 실행하는 방법입니다.
2번 방법은 %>% 를 사용하여 n 자리에 . 을 사용하는 것을 확인할 수 있습니다.
3번 방법은 %>% 를 사용하면서 n 값 자체를 명시하지 않는 방법입니다. 일반적으로 %>% 를 사용할 때는 이 방법을 이용합니다. . 을 작성하지 않는 것은 %>% 이전의 값이 다음 함수의 인자들 중 가장 앞에 있는 것에 해당한다는 의미입니다.
다음 예제를 통해서 더 . 에 대해 살펴볼까요
위의 예시와 동일하지만 이번엔 min_value 를 통한 연산들 입니다.
3번 예시를 보면 위의 예시와는 달리 . 을 중간에 사용한 것을 확인할 수 있습니다. 만약 위의 경우처럼
min_value %>% runif( 10, 10 )
이라고 작성한다면 runif 는 n 이 1 이고 min 이 10, max 가 10 이라고 판단할 것 입니다.
Why Pipe?
일단 %>% 연산자에 기능에 대해서는 알았지만 과연 이게 그렇게 필요한 것인가? 라는 의문이 생길 수 있습니다.
pipe 연산자의 효용성을 알아보기 위해서 준비했습니다. 우리가 항상 사용하는 iris 라는 데이터를 이용할 예정입니다.
iris 중 Sepal.Length 가 5 이상인 데이터의 종류별 개수를 구하시오.
파이프가 없는 경우 이렇게 중간중간 변수에 지정하면서 진행하신 분이 있을 수 있습니다. 이 방법은 3개의 추가 변수를 생성하게 되고 메모리 또한 추가로 소모하게 되는 방법입니다.
이렇게 작성했을 때 효용도 있지만 매 step 을 변수화 하는 것은 좋은 방법이라고 생각되진 않습니다.
두번째 방법은 함수 안에 함수 안에 함수 안에 넣는 방법입니다. 이 예제야 그나마 간단하지만 만약 parameter 가 매우 많고 복잡한 상황이라면 가독성이 매우 떨어지겠죠.
하지만 pipe 를 사용한다면 이렇게 작성이 가능합니다. 위에서 magrittr 의 효용중 연산의 흐름을 좌에서 우로 변경해준다는 것이 있었는데 바로 이를 뜻하는 것입니다.
%>% 사용시 글을 읽듯이 좌에서 우로 읽어나갈 수 있습니다.
둘을 비교해보면 차이가 더 확실해지죠. 연산의 흐름대로 읽어나갈 수 있어 %>% 연산자 사용시 가독성이 훨씬 높아졌다는 것을 느낄 수 있을 겁니다.
mutate 라는 단계를 추가했습니다. 이 과정에서 실질적으로는 아무 의미 없지만 예시를 위해 추가하는 함수 입니다.
또 하나의 장점으로 꼽히는 중간 단계에서 연산을 추가하는 부분입니다. 만약 함수 내에 함수를 넣는 방법으로 코드를 작성했을 때 새로운 연산을 추가한다면 일단 어떤 괄호 안에 넣어야할지 부터 찾아야겠죠.
실제로 긴 코드를 작성하다보면 괄호의 개수나 괄호의 위치 때문에 스크립트가 잘못 작동하는 경우가 수도 없이 많습니다. 하지만 %>% 를 사용한다면 어떨까요?
읽다가 필요한 부분에 한 줄만 추가해주면 됩니다. 코드가 크게 오류날 부분도 없고 추가를 위해 전체 코드를 다시 읽을 필요성도 없습니다.
하지만 모든 코드를 %>% 연산자를 통해 작성하는 것 또한 올바른 방법은 아닙니다. 언제 사용하지 않는 것이 좋은지에 대해서는 https://r4ds.had.co.nz/pipes.html 을 참고했습니다.
한 연산에서 파이프가 10회 이상 나오는 경우는 끊어서 가주는 것이 좋다고 합니다. 하지만 이것은 사실 %>% 를 사용하지 않는 경우에도 동일하게 적용됩니다. 중간중간 임시변수를 만들어 가면서 작성하는 것이 코드 이해나 디버깅 측면에서 훨씬 도움되는 경우가 많습니다.
Pipe 연산자의 특징은 좌에서 우로 연산을 진행한다는 점입니다. 이는 다시 말해 선형적이지 않은 작업에 대해서는 적용하기 어려운 부분이 있을 수 있다는 것을 의미합니다. 예를 들어 1번 테이블과 2번 테이블을 각각 전처리한 뒤 합쳐서 새로운 연산을 진행하는 작업같은 경우는 %>% 를 통해 한번에 기술하는 것이 좋지 않다는 것입니다. 이런 경우에는 1번 테이블을 전처리, 2번을 전처리 한 뒤 합쳐주는 3개의 과정으로 나누어서 진행하는 것이 좋다는 말입니다.
Tee 파이프 %T>%
Tee 파이프는 연산의 결과물을 출력하고 싶은 동시에 리턴 받고자 할 때 사용하는 파이프 입니다.
예시를 보면 setosa 에 필터링된 iris 를 저장하고 싶으면서 View 하고 싶은 상황인데 이 코드로는 리턴은 안되고 View 만 실행되는 것을 확인하실 수 있습니다.
이 때 %T>% 연산자를 사용하면 동시에 2가지 기능을 모두 실현할 수 있습니다.
setosa <- iris %>% filter(Species == 'setosa')setosa %>% View()
이 두 줄을 하나로 줄여주는 역할을 한다고 생각할 수 있겠네요.
병합 파이프 %<>%
저는 처음 R 을 사용할 때 1 번 방법처럼 작성하는 것이 너무 귀찮았습니다. 변수를 연산한 뒤 다시 그 변수에게 할당하는 과정이 많은데 매번 저렇게 작성하는 것이 좋을 때도 있었지만 귀찮았습니다.
병합 파이프의 존재를 안 뒤로는 2 번 방법처럼 작성하는 것을 기본으로 했었습니다만 코드 관리 및 공유할 때 불편한점이 생기곤 해서 자주 사용하지는 않고 있습니다.
추가로 %$% 라는 연산자가 있습니다만 이 연산자를 사용해서 줄이는 것보다 풀어서 작성하는 것이 가독성이 오히려 좋아지는 것 같아 사용하지 않고 있습니다. vignette 에 쉽게 설명되어 있으니 기회 되면 확인해보셔도 좋을 것 같습니다.
부족한 블로그에 방문해 주셔서 감사합니다.
잘못된 부분이나 질문이 있으시면
댓글로 말씀해주세요.
금방 확인하고 피드백 드리겠습니다.
좋은 하루 되세요. ^^
'R > R Language' 카테고리의 다른 글
FormatC in r : 숫자 콤마로 구분하기 (0) | 2018.11.27 |
---|---|
tibble 중 숫자에 콤마 넣어서 표시하는 방법 (0) | 2018.11.19 |
[R] 꼭 얄아야 할 R 기본 개념 part.2 (행렬곱, t, rbind, cbind, seq, rep) (4) | 2018.06.20 |
[R] 꼭 알아야 할 R 기본 개념 part.1 (help, install.package, getwd, setwd) (6) | 2018.06.20 |
[R] na 값 0 으로 대체하기 (3) | 2018.06.19 |
- Total
- Today
- Yesterday
- 머신러닝
- 영월캠핑
- 가평여행
- sql
- 가평캠핑
- 커플여행
- Oracle
- 가족여행
- 강원도여행
- 여름휴가추천
- 반려견캠핑
- 서울근교캠핑
- 여름캠핑
- python
- 카카오
- 여름휴가
- 캠핑장추천
- 자연힐링
- 가족캠핑
- 영월여행
- 계곡캠핑
- Koreancuisine
- 백준
- 강원도캠핑
- bukhansannationalpark
- 캠핑초보
- 파이썬
- 알고리즘
- 글램핑
- SeoulTravel
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |