pipes
Install and attach package:
# install.packages("devtools")
devtools::install_github("moodymudskipper/pipes")
library(pipes)The pipes package expands the magrittr package by providing :
- More pipe operators to debug, print extra info, suppress warnings or messages etc
- A convenient way to create custom pipes
- A couple of pipe friendly functions for printing (
pprint) and testing (pif).
The package works just as magrittr except that:
aliasfunctions were not imported- pipes have a class
pipeand have a dedicated printing method
magrittr doesn't need to be attached, but attaching it before pipes will make the alias functions available.
New operators
%D>%: Debug the pipe chain at the relevant step%V>%: UseView()on the output%L>%: Log the relevant call and execution time to the console%P>%: Useprint()on the output%summary>%: Print thesummary()off the output%glimpse>%: Usetibble::glimpse()on the output%skim>%: Useskimr::skim()on the output%ae>%: Useall.equalon the input and output%compare>%: Usearsenal::compare()and open the report of the differences in the default browser window%gg>%: Apply therhsto the data of aggobject and return the modifiedggobject%nowarn>%: Silence warnings%nomsg>%: Silence messages%strict>%: Fail on warning%try>%: Try, and in case of failure prints error and returns input%quietly>%: Usepurrr::quietly()to capture outputs of all kind and print them
Let's showcase a few of them.
debug the chain:
iris %>% head(2) %D>% `[`(4:5)view steps of chain in the viewer:
iris %V>% head(2) %V>% `[`(4:5)Log steps in the console:
iris %L>% {Sys.sleep(1);head(.,2)} %L>% {Sys.sleep(2);.[4:5]}
#> {
#> Sys.sleep(1)
#> head(., 2)
#> } ~ ...��� 1 sec
#> {
#> Sys.sleep(2)
#> .[4:5]
#> } ~ ...��� 2 sec
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosaUse print, summary or glimpse on output:
iris %P>% head(2) %P>% `[`(4:5)
#> head(., 2)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> .[4:5]
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosa
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosa
iris %summary>% head(2) %summary>% `[`(4:5)
#> head(., 2)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> Min. :4.90 Min. :3.000 Min. :1.4 Min. :0.2 setosa :2
#> 1st Qu.:4.95 1st Qu.:3.125 1st Qu.:1.4 1st Qu.:0.2 versicolor:0
#> Median :5.00 Median :3.250 Median :1.4 Median :0.2 virginica :0
#> Mean :5.00 Mean :3.250 Mean :1.4 Mean :0.2
#> 3rd Qu.:5.05 3rd Qu.:3.375 3rd Qu.:1.4 3rd Qu.:0.2
#> Max. :5.10 Max. :3.500 Max. :1.4 Max. :0.2
#> .[4:5]
#> Petal.Width Species
#> Min. :0.2 setosa :2
#> 1st Qu.:0.2 versicolor:0
#> Median :0.2 virginica :0
#> Mean :0.2
#> 3rd Qu.:0.2
#> Max. :0.2
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosa
iris %glimpse>% head(2) %glimpse>% `[`(4:5)
#> Loading required namespace: tibble
#> head(., 2)
#> Observations: 2
#> Variables: 5
#> $ Sepal.Length <dbl> 5.1, 4.9
#> $ Sepal.Width <dbl> 3.5, 3.0
#> $ Petal.Length <dbl> 1.4, 1.4
#> $ Petal.Width <dbl> 0.2, 0.2
#> $ Species <fct> setosa, setosa
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> .[4:5]
#> Observations: 2
#> Variables: 2
#> $ Petal.Width <dbl> 0.2, 0.2
#> $ Species <fct> setosa, setosa
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosa
#> Petal.Width Species
#> 1 0.2 setosa
#> 2 0.2 setosaUse all.equal on input and output, note that the method for tibbles gives a different output.
iris %>% head(2) %ae>%
transform(Species = as.character(Species), cst = 42)
#> transform(., Species = as.character(Species), cst = 42)
#> [1] "Length mismatch: comparison on first 5 components"
#> [2] "Component \"Species\": 'current' is not a factor"
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species cst
#> 1 5.1 3.5 1.4 0.2 setosa 42
#> 2 4.9 3.0 1.4 0.2 setosa 42
iris %>% tibble::as_tibble() %>% head(2) %ae>%
transform(Species = as.character(Species), cst = 42)
#> transform(., Species = as.character(Species), cst = 42)
#> [1] "Attributes: < Component \"class\": 1 string mismatch >"
#> [2] "Length mismatch: comparison on first 5 components"
#> [3] "Component \"Species\": 'current' is not a factor"
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species cst
#> 1 5.1 3.5 1.4 0.2 setosa 42
#> 2 4.9 3.0 1.4 0.2 setosa 42Use arsenal::compare on input and output, and opens a markdown report written into a temp file.
iris %>% head(2) %compare>%
transform(Species = as.character(Species), cst = 42)Use tidyverse syntax to mofidy a gg object's underlying data:
library(ggplot2,quietly = TRUE, warn.conflicts = FALSE)
ggplot(iris, aes(Species, Sepal.Width, fill=Species)) +
geom_col() %gg>% dplyr::group_by(Species) %gg>% dplyr::summarize_at("Sepal.Width", mean) +
ggtitle("Using dplyr verbs")Silence a warning or a message:
-1 %>% sqrt
#> Warning in sqrt(.): production de NaN
#> [1] NaN
-1 %nowarn>% sqrt
#> [1] NaN
iris[50:51,3:5] %>% dplyr::left_join(iris[50:51,c(1:2,5)])
#> Joining, by = "Species"
#> Petal.Length Petal.Width Species Sepal.Length Sepal.Width
#> 1 1.4 0.2 setosa 5 3.3
#> 2 4.7 1.4 versicolor 7 3.2
iris[50:51,3:5] %nomsg>% dplyr::left_join(iris[50:51,c(1:2,5)])
#> Petal.Length Petal.Width Species Sepal.Length Sepal.Width
#> 1 1.4 0.2 setosa 5 3.3
#> 2 4.7 1.4 versicolor 7 3.2Strictly fail on a warning
try(-1 %strict>% sqrt())Try, and in case of failure prints error and returns input
"a" %try>% log()
#> [1] "a"Use quietly to capture outputs of all kind and print them.
iris[50:51,3:5] %quietly>%
dplyr::left_join(iris[50:51,c(1:2,5)]) %quietly>%
dplyr::mutate(Petal.Length = - Petal.Length, Petal.Length = sqrt(Petal.Length))
#> dplyr::left_join(., iris[50:51, c(1:2, 5)])
#> $messages
#> [1] "Joining, by = \"Species\"\n"
#>
#>
#> dplyr::mutate(., Petal.Length = -Petal.Length, Petal.Length = sqrt(Petal.Length))
#> $warnings
#> [1] "production de NaN"
#> Petal.Length Petal.Width Species Sepal.Length Sepal.Width
#> 1 NaN 0.2 setosa 5 3.3
#> 2 NaN 1.4 versicolor 7 3.2new_pipe
It's easier to understand how to build a new pipe by looking at examples.
`%T>%`
#> Special pipe operator
#> wrap:
#> {
#> local(BODY)
#> .
#> }If we want to rebuild this operator from scratch, all we have to do is :
`%newT>%` <- new_pipe({
local(BODY)
.
}). is the value of the input and BODY contains the code that would have been executed by %>%, for example iris %>% head(3) BODY would be head(.,3).
so what %newT>% is doing is executing the call in a protected environment through local(BODY), then returning the unaltered input ..
iris %>% head(2) %newT>% print %>% head(1)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosaTake a look at the other functions to understand how to make your own :
`%nowarn>%`
#> Special pipe operator
#> wrap:
#> suppressWarnings(BODY)
`%P>%`
#> Special pipe operator
#> wrap:
#> {
#> message(deparse(quote(BODY)))
#> . <- print(BODY)
#> cat("\n")
#> .
#> }
`%summary>%`
#> Special pipe operator
#> wrap:
#> {
#> message(deparse(quote(BODY)))
#> . <- BODY
#> print(summary(.))
#> cat("\n")
#> .
#> }
`%strict>%`
#> Special pipe operator
#> wrap:
#> {
#> current_warn <- options()$warn
#> options(warn = 2)
#> on.exit(options(warn = current_warn))
#> BODY
#> }easy conditional steps with pif
Using functions, formulas or expressions
iris %>% pif(is.data.frame, dim, nrow)
#> [1] 150 5
iris %>% pif(~is.numeric(Species), ~"numeric :)",~paste(class(Species)[1],":("))
#> [1] "factor :("
iris %>% pif(nrow(iris) > 2, head(iris,2))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosaprint info on intermediate steps with pprint
iris %>%
pprint(~"hello") %>%
head(2) %>%
transform(Species = NULL) %>%
pprint(rowSums,na.rm = TRUE) %>%
pprint(~setNames(.[1:2],toupper(names(.[1:2])))) %>%
pprint(dim)
#> [1] "hello"
#> 1 2
#> 10.2 9.5
#> SEPAL.LENGTH SEPAL.WIDTH
#> 1 5.1 3.5
#> 2 4.9 3.0
#> [1] 2 4