R Language
Operadores de tuberías (%>% y otros)
Buscar..
Introducción
Los operadores de tuberías, disponibles en magrittr
, dplyr
y otros R, procesan un objeto de datos utilizando una secuencia de operaciones al pasar el resultado de un paso como entrada para el siguiente paso utilizando operadores de infijo en lugar del método R más típico de anidado función de llamadas.
Tenga en cuenta que el objetivo de los operadores de tuberías es aumentar la legibilidad humana del código escrito. Vea la sección de Comentarios para consideraciones de rendimiento.
Sintaxis
Sintaxis de
rhs(lhs)
lhs%>% rhs # pararhs(lhs)
lhs%>% rhs (a = 1) # sintaxis de tubería para
rhs(lhs, a = 1)
lhs%>% rhs (a = 1, b =.) # sintaxis de tubería para
rhs(a = 1, b = lhs)
lhs% <>% rhs # sintaxis de tuberías para
lhs <- rhs(lhs)
lhs% $% rhs (a) # sintaxis de tubería para
with(lhs, rhs(lhs$a))
lhs% T>% rhs # sintaxis de tubería para
{ rhs(lhs); lhs }
Parámetros
lhs | rs |
---|---|
Un valor o el marcador de posición magrittr. | Una llamada a la función utilizando la semántica magrittr. |
Observaciones
Paquetes que utilizan %>%
El operador de tubería está definido en el paquete magrittr
, pero ganó gran visibilidad y popularidad con el paquete dplyr
(que importa la definición de magrittr
). Ahora es parte de tidyverse
, que es una colección de paquetes que "funcionan en armonía porque comparten representaciones de datos comunes y diseño de API" .
El paquete magrittr
también proporciona varias variaciones del operador de tubería para aquellos que desean una mayor flexibilidad en la tubería, como la tubería de asignación compuesta %<>%
, la tubería de exposición %$%
y el operador en %T>%
. También proporciona un conjunto de funciones de alias para reemplazar funciones comunes que tienen una sintaxis especial ( +
, [
, [[
, etc.) para que puedan usarse fácilmente dentro de una cadena de tuberías.
Encontrar documentación
Al igual que con cualquier operador de infijo (como +
, *
, ^
, &
, %in%
), puede encontrar la documentación oficial si la pone entre comillas ?'%>%'
O help('%>%')
( asumiendo que ha cargado un paquete que adjunta pkg:magrittr
).
Teclas de acceso rápido
Hay una tecla de acceso rápido especial en RStudio para el operador de tuberías: Ctrl+Shift+M
( Windows y Linux ), Cmd+Shift+M
( Mac ).
Consideraciones de rendimiento
Si bien el operador de tuberías es útil, tenga en cuenta que existe un impacto negativo en el rendimiento debido principalmente a la sobrecarga de su uso. Considere las siguientes dos cosas cuidadosamente al usar el operador de tubería:
- Rendimiento de la máquina (bucles)
- Evaluación (
object %>% rm()
no elimina elobject
)
Uso básico y encadenamiento.
El operador de tubería, %>%
, se usa para insertar un argumento en una función. No es una función básica del idioma y solo se puede usar después de adjuntar un paquete que lo proporcione, como magrittr
. El operador de la tubería toma el lado izquierdo (LHS) del tubo y lo utiliza como primer argumento de la función en el lado derecho (RHS) del tubo. Por ejemplo:
library(magrittr)
1:10 %>% mean
# [1] 5.5
# is equivalent to
mean(1:10)
# [1] 5.5
La tubería se puede utilizar para reemplazar una secuencia de llamadas a funciones. Los múltiples tubos nos permiten leer y escribir la secuencia de izquierda a derecha, en lugar de hacerlo desde adentro hacia afuera. Por ejemplo, supongamos que tenemos years
definidos como un factor, pero queremos convertirlo en un valor numérico. Para evitar una posible pérdida de información, primero convertimos a carácter y luego a numérico:
years <- factor(2008:2012)
# nesting
as.numeric(as.character(years))
# piping
years %>% as.character %>% as.numeric
Si no queremos que el LHS (lado izquierdo) se use como primer argumento en el RHS (lado derecho), hay soluciones alternativas, como nombrar los argumentos o usarlos .
para indicar donde va la entrada entubada.
# example with grepl
# its syntax:
# grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
# note that the `substring` result is the *2nd* argument of grepl
grepl("Wo", substring("Hello World", 7, 11))
# piping while naming other arguments
"Hello World" %>% substring(7, 11) %>% grepl(pattern = "Wo")
# piping with .
"Hello World" %>% substring(7, 11) %>% grepl("Wo", .)
# piping with . and curly braces
"Hello World" %>% substring(7, 11) %>% { c(paste('Hi', .)) }
#[1] "Hi World"
#using LHS multiple times in argument with curly braces and .
"Hello World" %>% substring(7, 11) %>% { c(paste(. ,'Hi', .)) }
#[1] "World Hi World"
Secuencias funcionales
Dada una secuencia de pasos que usamos repetidamente, a menudo es útil almacenarlo en una función. Las tuberías permiten guardar dichas funciones en un formato legible al iniciar una secuencia con un punto como en:
. %>% RHS
Como ejemplo, supongamos que tenemos fechas factoriales y queremos extraer el año:
library(magrittr) # needed to include the pipe operators
library(lubridate)
read_year <- . %>% as.character %>% as.Date %>% year
# Creating a dataset
df <- data.frame(now = "2015-11-11", before = "2012-01-01")
# now before
# 1 2015-11-11 2012-01-01
# Example 1: applying `read_year` to a single character-vector
df$now %>% read_year
# [1] 2015
# Example 2: applying `read_year` to all columns of `df`
df %>% lapply(read_year) %>% as.data.frame # implicit `lapply(df, read_year)
# now before
# 1 2015 2012
# Example 3: same as above using `mutate_all`
library(dplyr)
df %>% mutate_all(funs(read_year))
# if an older version of dplyr use `mutate_each`
# now before
# 1 2015 2012
Podemos revisar la composición de la función escribiendo su nombre o usando functions
:
read_year
# Functional sequence with the following components:
#
# 1. as.character(.)
# 2. as.Date(.)
# 3. year(.)
#
# Use 'functions' to extract the individual functions.
También podemos acceder a cada función por su posición en la secuencia:
read_year[[2]]
# function (.)
# as.Date(.)
En general, este enfoque puede ser útil cuando la claridad es más importante que la velocidad.
Asignación con% <>%
El paquete magrittr
contiene un operador de asignación de infijo compuesto, %<>%
, que actualiza un valor insertándolo primero en una o más expresiones de rhs
y luego asignando el resultado. Esto elimina la necesidad de escribir el nombre de un objeto dos veces (una vez en cada lado del operador de asignación <-
). %<>%
debe ser el primer operador de infijo en una cadena:
library(magrittr)
library(dplyr)
df <- mtcars
En lugar de escribir
df <- df %>% select(1:3) %>% filter(mpg > 20, cyl == 6)
o
df %>% select(1:3) %>% filter(mpg > 20, cyl == 6) -> df
El operador de asignación compuesta canalizará y reasignará df
:
df %<>% select(1:3) %>% filter(mpg > 20, cyl == 6)
Exponer contenidos con% $%
El operador del tubo de exposición, %$%
, expone los nombres de las columnas como símbolos R dentro del objeto del lado izquierdo a la expresión del lado derecho. Este operador es útil cuando canaliza funciones que no tienen un argumento de data
(a diferencia de, digamos, lm
) y que no toman un data.frame y nombres de columna como argumentos (la mayoría de las funciones principales de dplyr).
El operador de tubería de exposición %$%
permite que un usuario evite romper una tubería cuando necesite referirse a nombres de columnas. Por ejemplo, supongamos que desea filtrar un data.frame y luego ejecutar una prueba de correlación en dos columnas con cor.test
:
library(magrittr)
library(dplyr)
mtcars %>%
filter(wt > 2) %$%
cor.test(hp, mpg)
#>
#> Pearson's product-moment correlation
#>
#> data: hp and mpg
#> t = -5.9546, df = 26, p-value = 2.768e-06
#> alternative hypothesis: true correlation is not equal to 0
#> 95 percent confidence interval:
#> -0.8825498 -0.5393217
#> sample estimates:
#> cor
#> -0.7595673
Aquí el %>%
pipe estándar pasa el data.frame a través de filter()
, mientras que el cor.test()
%$%
expone los nombres de columna a cor.test()
.
El tubo de exposición funciona como una versión de la base R with()
funciones with()
capaz de canalizar, y los mismos objetos del lado izquierdo se aceptan como entradas.
Usando el tubo con dplyr y ggplot2
El operador %>%
también se puede usar para canalizar la salida de dplyr en ggplot. Esto crea un flujo de análisis de datos exploratorios (EDA) unificado que es fácilmente personalizable. Este método es más rápido que hacer las agregaciones internamente en ggplot y tiene el beneficio adicional de evitar variables intermedias innecesarias.
library(dplyr)
library(ggplot)
diamonds %>%
filter(depth > 60) %>%
group_by(cut) %>%
summarize(mean_price = mean(price)) %>%
ggplot(aes(x = cut, y = mean_price)) +
geom_bar(stat = "identity")
Creando efectos secundarios con% T>%
Algunas funciones en R producen un efecto secundario (es decir, guardar, imprimir, trazar, etc.) y no siempre devuelven un valor significativo o deseado.
%T>%
(operador de tee) le permite reenviar un valor a una función que produce efectos secundarios mientras mantiene intacto el valor original de lhs
. En otras palabras: el operador de tee funciona como %>%
, excepto que los valores de retorno son lhs
sí, y no el resultado de la función / expresión de rhs
.
Ejemplo: crear, canalizar, escribir y devolver un objeto. Si se utilizara %>%
en lugar de %T>%
en este ejemplo, entonces la variable all_letters
contendrá NULL
lugar del valor del objeto ordenado.
all_letters <- c(letters, LETTERS) %>%
sort %T>%
write.csv(file = "all_letters.csv")
read.csv("all_letters.csv") %>% head()
# x
# 1 a
# 2 A
# 3 b
# 4 B
# 5 c
# 6 C
Advertencia: la canalización de un objeto sin nombre a save()
producirá un objeto denominado .
cuando se carga en el espacio de trabajo con load()
. Sin embargo, es posible una solución alternativa utilizando una función auxiliar (que también se puede escribir en línea como una función anónima).
all_letters <- c(letters, LETTERS) %>%
sort %T>%
save(file = "all_letters.RData")
load("all_letters.RData", e <- new.env())
get("all_letters", envir = e)
# Error in get("all_letters", envir = e) : object 'all_letters' not found
get(".", envir = e)
# [1] "a" "A" "b" "B" "c" "C" "d" "D" "e" "E" "f" "F" "g" "G" "h" "H" "i" "I" "j" "J"
# [21] "k" "K" "l" "L" "m" "M" "n" "N" "o" "O" "p" "P" "q" "Q" "r" "R" "s" "S" "t" "T"
# [41] "u" "U" "v" "V" "w" "W" "x" "X" "y" "Y" "z" "Z"
# Work-around
save2 <- function(. = ., name, file = stop("'file' must be specified")) {
assign(name, .)
call_save <- call("save", ... = name, file = file)
eval(call_save)
}
all_letters <- c(letters, LETTERS) %>%
sort %T>%
save2("all_letters", "all_letters.RData")