파이프가 해결하는 문제
파이프는 앞의 값을 받아 뒤에 오는 함수 호출로 넣어 줍니다. x |> f()는 f(x)를 뜻하죠. 사소한 표기 변경처럼 들리지만, 서너 단계를 이어 붙이면 이야기가 달라집니다. 파이프가 없으면 여러 단계 변환이 중첩되고, 중첩 호출은 거꾸로 읽힙니다. 가장 먼저 일어나는 일이 가장 안쪽에 놓이니까요.
round(sqrt(x), 1)을 읽으려면 가운데(x)에서 시작해 바깥으로(sqrt 다음 round) 나가면서, 뒤에 붙은 , 1)가 어느 호출의 것인지 계속 추적해야 합니다. 세 단계면 성가시고, 데이터 프레임 동사와 각기 여러 인자가 붙은 다섯 단계쯤 되면 정말로 따라가기 어렵습니다. 사람들이 택하는 대안, 즉 중간 단계를 tmp1, tmp2, tmp3에 저장하는 방식은 아무도 필요로 하지 않는 이름으로 작업 공간을 어지럽힙니다.
파이프는 단계가 실제로 일어나는 순서대로 사슬을 다시 씁니다. x를 가져와서, 그다음 제곱근을 구하고, 그다음 반올림합니다.
네이티브 파이프 |>
R 4.1부터 파이프가 언어 자체에 내장되어 패키지가 필요 없습니다. |>는 앞의 값을 다음 호출의 첫 인자로 넣습니다.
c(1, 4, 9, 16) |> sqrt() |> round(1)은 정확히 round(sqrt(c(1, 4, 9, 16)), 1)입니다. R이 파싱하면서 말 그대로 하나를 다른 하나로 다시 쓰므로 실행 시 추가 비용이 전혀 없습니다. 하지만 이제 코드가 위에서 아래로 레시피처럼 읽힙니다. 데이터, 그다음 각 변환이 순서대로죠. 데이터 도구뿐 아니라 어떤 함수에도 통합니다. mtcars |> head(3)은 처음부터 끝까지 순수한 기본 R입니다.
첫 매개변수가 "데이터"인 함수와 파이프가 얼마나 잘 맞는지 눈여겨보세요. head, sort, unique, summary, 그리고 모든 dplyr 동사가 그렇게 설계되어 있고, 그래서 R에서 파이프라인이 그토록 자연스럽게 느껴집니다.
|>의 규칙
네이티브 파이프는 의도적으로 엄격합니다. 세 가지 규칙이 거의 전부를 덮습니다.
1. 다음 단계는 괄호가 있는 호출이어야 합니다. x |> sqrt는 문법 오류이고 x |> sqrt()라고 써야 합니다.
c(1, 4, 9) |> sqrt # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt() # correct
2. 값은 첫 인자 자리에 놓입니다. 여러분이 쓴 추가 인자는 그 뒤에 그대로 남습니다. x |> round(1)은 round(x, 1)입니다.
3. 다른 자리에 놓으려면 이름 있는 인자와 함께 _ 자리표시자를 쓰세요(R 4.2 이상). 고전적인 사례로 lm은 수식을 먼저, 데이터를 나중에 받으므로 데이터 프레임을 파이프로 넣으려면 data = _가 필요합니다.
_는 한 번만, 그리고 이름 있는 인자로만 등장할 수 있습니다. 그것이 너무 제한적이라면 익명 함수로 파이프하세요. 익명 함수는 값을 원하는 어디에든 놓을 수 있습니다.
람다를 괄호로 감싼 뒤 ()로 호출합니다. 그러면 규칙 1이 만족됩니다. 한 파이프라인에서 이런 일을 자주 하고 있다면, 그 단계는 아마 이름 있는 함수가 되고 싶어 하는 것입니다.
magrittr 파이프 %>%
언어에 파이프가 생기기 전에는 magrittr 패키지가 하나를 제공했고, %>%는 tidyverse 코드의 서명이 되었습니다. dplyr을 불러오면 자동으로 따라옵니다. 지난 10년간 쓰인 거의 모든 dplyr 튜토리얼과 스크립트에서 보게 될 것입니다.
library(dplyr)
mtcars %>%
filter(mpg > 30) %>%
select(mpg, wt)
%>%는 패키지에서 오기 때문에 이 예제들은 순수한 R 세션에서 실행할 수 없습니다. library(dplyr)(또는 library(magrittr))을 잊으면 정확히 Error: could not find function "%>%"가 나옵니다.
흔한 경우의 동작은 |>와 동일합니다. 앞의 값을 다음 호출의 첫 인자로 넣죠. 다만 magrittr가 더 관대합니다.
c(1, 4, 9) %>% sqrt # bare function name - no parentheses needed
mtcars %>% lm(mpg ~ wt, data = .) # . placeholder, usable anywhere
c(-2, 3) %>% { max(abs(.)) } # . can even appear several times, in nested calls
|>와 %>%: 중요한 차이들
첫 인자를 쓰는 파이프라인, 즉 실제 코드의 압도적 다수에서는 둘을 바꿔 써도 됩니다. 차이는 가장자리에 있습니다.
- 가용성:
|>는 기본 R 4.1 이상이고,%>%는 magrittr(보통 dplyr을 통해)가 필요합니다. - 괄호:
|>는sqrt()를 요구하고,%>%는 그냥sqrt도 받아들입니다. - 자리표시자:
|>는_를 이름 있는 인자로 한 번만 쓸 수 있고(4.2 이상),%>%는.을 중첩 호출 안을 포함해 어디에든 몇 번이든 쓸 수 있습니다. - 속도:
|>는 파싱 시점에 중첩 호출로 다시 쓰이므로 비용이 0입니다.%>%는 평범한 함수 호출이라 (실제로는 무시할 만한) 약간의 오버헤드가 있습니다.
이 중 무엇도 %>%를 틀린 것으로 만들지 않습니다. 충분히 검증되었고 조금 더 유연합니다. 다만 |>를 넘어서는 기능은 전부 람다로 해낼 수 있고, 기본 파이프는 의존성이 필요 없습니다.
어느 쪽을 써야 할까요?
새 코드에는 |>를 쓰세요. 언어의 일부이고, R 4.1 이상이 도는 어디서나 동작하며, 패키지가 필요 없고, 그 엄격함이 오히려 장점으로 읽힙니다. 파이프라인이 단순하게 유지되거나 이름 있는 함수로 리팩터링되니까요.
하지만 %>%를 막힘없이 읽을 수 있어야 합니다. tidyverse 코드베이스, Stack Overflow 답변, 2014년 이후 쓰인 대부분의 R 책이 그것으로 가득하기 때문입니다. 기존 dplyr 스타일을 가진 팀은 일관성을 위해 %>%를 유지하기도 하며, 그것도 합리적인 판단입니다. 최악의 선택은 한 파일에 두 연산자를 섞는 것입니다. 어느 쪽을 쓰든 하나의 관례는 그대로 통합니다. 긴 파이프라인에서는 각 단계를 한 줄씩 두고 파이프를 줄 끝에 붙여, 레시피가 단계 목록처럼 읽히게 하세요.
apply 계열과 같은 감각이 여기에도 적용됩니다. 파이프는 선형적이고 단계가 이어지는 변환을 위한 것입니다. 계산이 갈라지거나 중간 결과가 두 번 필요하다면, 전부를 하나의 사슬로 밀어 넣지 말고 이름을 잘 지은 변수에 대입하세요.
핵심 정리
- 파이프는 앞의 값을 다음 호출로 넣어 줍니다.
x |> f() |> g()는g(f(x))를 일어나는 순서대로 쓴 것입니다. |>는 기본 R(4.1 이상)입니다. 괄호가 필요하고, 첫 인자를 겨냥하며,_자리표시자는 이름 있는 인자와 함께 씁니다(4.2 이상).%>%는 magrittr/tidyverse입니다. 함수 이름만 써도 되고.자리표시자를 어디에든 쓸 수 있지만 패키지가 필요합니다.- 새 코드에는
|>를 택하고, 다른 모든 곳의%>%는 눈 하나 깜짝하지 않고 읽으세요. - 파이프는 선형적인 레시피에 어울립니다. 분기하는 논리에는 이름 있는 변수가 어울립니다.
다음 순서는 패키지입니다. %>%와 dplyr, 그리고 R의 나머지 2만 개 확장이 실제로 어디에서 오는지 살펴봅니다.
자주 묻는 질문
R에서 %>%는 무슨 뜻인가요?
%>%는 magrittr 파이프입니다. 앞의 값을 받아 뒤에 오는 호출의 첫 인자로 넣어 주므로 x %>% f() %>% g()는 g(f(x))를 뜻합니다. magrittr 패키지에서 오며 dplyr과 tidyverse를 불러오면 자동으로 함께 로드됩니다. 기본 R의 일부가 아닙니다.
R에서 |>를 쓰려면 패키지가 필요한가요?
아니요. |>는 네이티브 파이프로, 4.1 버전부터 기본 R에 내장되어 있습니다. 아무것도 설치하지 않은 순수 R 세션에서도 동작합니다. 패키지가 필요한 쪽은 %>%입니다(magrittr, 또는 dplyr처럼 이를 다시 내보내는 패키지).
R에서 |>와 %>%의 차이는 무엇인가요?
둘 다 앞의 값을 다음 호출로 넣어 줍니다. |>는 기본 R이고 괄호를 명시해야 하며(x |> sqrt()), _ 자리표시자는 이름 있는 인자에만 쓸 수 있습니다. %>%는 magrittr가 필요하고 함수 이름만 써도 되며(x %>% sqrt), . 자리표시자는 어디에든 몇 번이든 놓을 수 있습니다. 흔한 첫 인자 사용에서는 동작이 동일합니다.