El problema que resuelven los pipes
Un pipe toma el valor que tiene delante y lo pasa a la llamada de función que tiene detrás - x |> f() significa f(x). Eso suena a una reescritura trivial hasta que encadenas tres o cuatro pasos. Sin un pipe, las transformaciones de varios pasos se anidan, y las llamadas anidadas se leen al revés: lo primero que ocurre está en la posición más interior.
Para leer round(sqrt(x), 1) empiezas por el centro (x), avanzas hacia fuera (sqrt y luego round) y vas siguiendo qué , 1) final pertenece a qué llamada. Con tres niveles esto es molesto; con cinco niveles, con verbos de data frame y varios argumentos cada uno, es genuinamente difícil de seguir. La alternativa a la que recurre la gente - guardar cada paso intermedio en tmp1, tmp2, tmp3 - ensucia el espacio de trabajo con nombres que nadie necesita.
Un pipe reescribe la cadena en el orden en que los pasos ocurren de verdad: toma x, luego hazle la raíz cuadrada, luego redondéalo.
El pipe nativo |>
Desde R 4.1, el pipe está incorporado en el propio lenguaje - no hace falta ningún paquete. |> inserta el valor anterior como primer argumento de la siguiente llamada:
c(1, 4, 9, 16) |> sqrt() |> round(1) es exactamente round(sqrt(c(1, 4, 9, 16)), 1) - R reescribe literalmente uno en el otro al analizar el código, así que no hay ninguna sobrecarga en tiempo de ejecución. Pero ahora el código se lee como una receta, de arriba abajo: los datos y luego cada transformación en orden. Esto funciona con cualquier función, no solo con herramientas de datos - mtcars |> head(3) es R base puro de principio a fin.
Fíjate en lo bien que encajan los pipes con las funciones cuyo primer parámetro son "los datos": head, sort, unique, summary y todos los verbos de dplyr están diseñados así, y por eso las tuberías resultan tan naturales en R.
Las reglas de |>
El pipe nativo es deliberadamente estricto. Tres reglas cubren casi todo:
1. El siguiente paso debe ser una llamada, con paréntesis. x |> sqrt es un error de sintaxis; tienes que escribir x |> sqrt():
c(1, 4, 9) |> sqrt # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt() # correct
2. El valor aterriza en la posición del primer argumento. Los argumentos extra que escribas se quedan detrás de él - x |> round(1) es round(x, 1).
3. Para que aterrice en otro sitio, usa el marcador _ con un argumento con nombre (R 4.2+). Caso clásico: lm toma primero una fórmula y después los datos, así que canalizar un data frame hacia ella requiere data = _:
El _ puede aparecer una vez y solo como argumento con nombre. Cuando eso resulte demasiado restrictivo, canaliza hacia una función anónima - puede poner el valor donde le apetezca:
La lambda se envuelve entre paréntesis y luego se llama con () - eso mantiene satisfecha la regla 1. Si te ves haciendo esto a menudo en una misma tubería, ese paso probablemente quiera ser una función con nombre.
El pipe de magrittr %>%
Antes de que el lenguaje tuviera un pipe, el paquete magrittr proporcionaba uno, y %>% se convirtió en la firma del código del tidyverse - cargar dplyr te lo da automáticamente. Lo verás en casi todos los tutoriales de dplyr y en los scripts escritos en la última década:
library(dplyr)
mtcars %>%
filter(mpg > 30) %>%
select(mpg, wt)
Como %>% viene de un paquete, estos ejemplos no se pueden ejecutar en una sesión de R pelada - Error: could not find function "%>%" es exactamente lo que obtienes si olvidas library(dplyr) (o library(magrittr)).
El comportamiento en el caso común es idéntico al de |>: pasar el valor anterior al primer argumento de la siguiente llamada. Pero magrittr es más permisivo:
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
|> frente a %>%: las diferencias que importan
Para una tubería de primer argumento - que es la inmensa mayoría del código real -, los dos son intercambiables. Las diferencias viven en los extremos:
- Disponibilidad:
|>es R base 4.1+;%>%necesita magrittr (normalmente vía dplyr). - Paréntesis:
|>exigesqrt();%>%acepta unsqrtsuelto. - Marcador de posición:
|>usa_, una vez y solo como argumento con nombre (4.2+);%>%usa., en cualquier sitio, cuantas veces quieras, incluso dentro de llamadas anidadas. - Velocidad:
|>se reescribe en tiempo de análisis a la llamada anidada - coste cero.%>%es una llamada a función normal con una pequeña sobrecarga (en la práctica despreciable).
Nada de esto convierte a %>% en algo incorrecto - está más que probado y es algo más flexible. Pero todo lo que hace más allá de |> se puede hacer con una lambda, y el pipe de base no necesita ninguna dependencia.
¿Cuál deberías usar?
Para código nuevo: usa |>. Forma parte del lenguaje, funciona en todas partes donde corra R 4.1+, no necesita ningún paquete, y su rigidez se lee como una ventaja - las tuberías se mantienen simples o se refactorizan en funciones con nombre.
Pero debes ser capaz de leer %>% con fluidez, porque las bases de código del tidyverse, las respuestas de Stack Overflow y la mayoría de los libros de R escritos desde 2014 están llenos de él. Los equipos con un estilo dplyr ya establecido suelen mantener %>% por coherencia, y es una decisión razonable - la peor elección es mezclar ambos operadores en un mismo archivo. Escribas el que escribas, una convención se mantiene: en las tuberías largas, pon cada paso en su propia línea con el pipe al final de la línea, para que la receta se lea como una lista de pasos.
Aquí aplica el mismo criterio que con la familia apply: los pipes son para transformaciones lineales, paso tras paso. Cuando un cálculo se ramifica o necesita resultados intermedios dos veces, asigna una variable con un buen nombre en lugar de forzarlo todo por una única cadena.
Lo que te llevas
- Un pipe pasa el valor anterior a la siguiente llamada:
x |> f() |> g()esg(f(x)), escrito en el orden en que ocurre. |>es de R base (4.1+): exige paréntesis, apunta al primer argumento y usa el marcador_con un argumento con nombre (4.2+).%>%es de magrittr/tidyverse: admite nombres sueltos y el marcador.en cualquier sitio - pero necesita un paquete.- Prefiere
|>en código nuevo; lee%>%en todas partes sin pestañear. - Los pipes encajan con recetas lineales; la lógica que se ramifica merece variables con nombre.
Lo siguiente: los paquetes - de dónde salen realmente %>%, dplyr y las otras 20.000 extensiones de R.
Preguntas frecuentes
¿Qué significa %>% en R?
%>% es el pipe de magrittr: toma el valor que tiene delante y lo pasa como primer argumento de la llamada que tiene detrás, así que x %>% f() %>% g() significa g(f(x)). Viene del paquete magrittr y se carga automáticamente con dplyr y el tidyverse - no forma parte de R base.
¿Necesito un paquete para usar |> en R?
No. |> es el pipe nativo, incorporado en R base desde la versión 4.1 - funciona en una sesión de R normal sin instalar nada. %>% es el que necesita un paquete (magrittr, o cualquier cosa que lo reexporte, como dplyr).
¿Cuál es la diferencia entre |> y %>% en R?
Ambos pasan el valor anterior a la siguiente llamada. |> es de R base, exige paréntesis explícitos (x |> sqrt()) y usa el marcador _ solo con un argumento con nombre. %>% necesita magrittr, acepta nombres de función sueltos (x %>% sqrt) y su marcador . puede ir en cualquier sitio y cuantas veces quieras. El comportamiento es idéntico en el caso común del primer argumento.