6.1 O operador pipe
A ideia do operador %>% (pipe) é bem simples: usar o valor resultante da expressão do lado esquerdo como primeiro argumento da função do lado direito.
# As duas linhas abaixo são equivalentes.
f(x, y)
x %>% f(y)Nos casos mais simples, o pipe parece não trazer grandes vantagens. Agora, veja como fica um caso com mais etapas.
# Vamos calcular a raiz quadrada da soma dos valores de 1 a 4. Primeiro, sem o pipe.
x <- c(1, 2, 3, 4)
sqrt(sum(x))## [1] 3.162278
# Agora com o pipe.
x %>% sum() %>% sqrt()## [1] 3.162278
O caminho que o código x %>% sum %>% sqrt seguiu foi enviar o objeto x como argumento da função sum() e, em seguida, enviar a saida da expressão sum(x) como argumento da função sqrt(). Observe que escrevemos o código na mesma ordem em que as operações são realizadas. A utilização de parênteses após o nome das funções não é necessário, mas recomendável.
Se você ainda não está convencido com o poder do pipe, fica que vai ter bolo!
No exemplo abaixo, vamos ilustrar um caso em que temos um grande número de funções aninhadas. Veja como a utilização do pipe transforma um código confuso e difícil de ser lido em algo simples e intuitivo.
# Receita de bolo sem pipe. Tente entender o que é preciso fazer.
esfrie(
asse(
coloque(
bata(
acrescente(
recipiente(
rep("farinha", 2),
"água",
"fermento",
"leite",
"óleo"
),
"farinha",
ate = "macio"
),
duracao = "3min"
),
lugar = "forma",
tipo = "grande",
untada = TRUE
),
duracao = "50min"
),
lugar = "geladeira",
duracao = "20min"
)
# Veja como o código acima pode ser reescrito utilizando-se o pipe. Agora realmente se parece com uma receita de bolo.
recipiente(rep("farinha", 2), "água", "fermento", "leite", "óleo") %>%
acrescente("farinha", ate = "macio") %>%
bata(duracao = "3min") %>%
coloque(lugar = "forma", tipo = "grande", untada = TRUE) %>%
asse(duracao = "50min") %>%
esfrie(lugar = "geladeira", duracao = "20min")Às vezes, queremos que o resultado do lado esquerdo vá para outro argumento do lado direito, que não o primeiro. Para isso, utilizamos um . como marcador.
# Queremos que o dataset seja recebido pelo segundo argumento (data=) da função "lm".
airquality %>%
na.omit() %>%
lm(Ozone ~ Wind + Temp + Solar.R, data = .) %>%
summary()##
## Call:
## lm(formula = Ozone ~ Wind + Temp + Solar.R, data = .)
##
## Residuals:
## Min 1Q Median 3Q Max
## -40.485 -14.219 -3.551 10.097 95.619
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -64.34208 23.05472 -2.791 0.00623 **
## Wind -3.33359 0.65441 -5.094 1.52e-06 ***
## Temp 1.65209 0.25353 6.516 2.42e-09 ***
## Solar.R 0.05982 0.02319 2.580 0.01124 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 21.18 on 107 degrees of freedom
## Multiple R-squared: 0.6059, Adjusted R-squared: 0.5948
## F-statistic: 54.83 on 3 and 107 DF, p-value: < 2.2e-16
O pipe é a força da gravidade dentro do tidyverse. Veremos nos próximos capítulos como as funções de diferentes pacotes interagem perfeitamente graças a esse operador.
Exercícios
1. Reescreva a expressão abaixo utilizando o %>%.
round(mean(sum(1:10)/3), digits = 1)Dica: utilize a função magrittr::divide_by(). Veja o help da função para mais informações.
2. Reescreva o código abaixo utilizando o %>%.
x <- rnorm(100)
x.pos <- x[x>0]
media <- mean(x.pos)
saida <- round(media, 1)3. Sem rodar, diga qual a saída do código abaixo. Consulte o help das funções caso precise.
2 %>%
add(2) %>%
c(6, NA) %>%
mean(na.rm = T) %>%
equals(5)