tonytonov
tonytonov

Reputation: 25638

How to override magrittr pipe operator?

Say I have a data set and I'd like to apply several filters to it using piping syntax, like so:

library(magrittr)
library(dplyr)
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg)
nrow(mtcars)
#[1] 11

I check the current status of the data set with nrow after each such step, so I thought I could override the piping %<>% operator along the lines of

`%<?>%` <- function(lhs, rhs) {
  x <- magrittr::`%<>%`(lhs, rhs)
  if (is.data.frame(x) & pipeVerbose) print(nrow(x))
}
#Using this will generate errors like
#Error in pipes[[i]] : subscript out of bounds

And now by switching pipeVerbose flag on or off I would control the tracking procedure for the whole flow. Apparently it's not that simple because of the internal evaluation mechanism, as prompted here. The question is, is it possible to achieve the desired behavior with minimal effort, i.e. without patching the magittr internals?

I have to admit the whole idea is slightly unsettling, but my actual scenario is a bit more complicated and I'd like to hide some debug/development details for demonstration purposes via a simple on/off switch.

Upvotes: 3

Views: 407

Answers (3)

moodymudskipper
moodymudskipper

Reputation: 47340

Note that you can have you on/off switch this way, not much more visible than having %<?>% instead of %<>% :

p <- function(x){if(pipeVerbose) print(nrow(x))}

pipeVerbose <- FALSE
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg) %T>% p

rm(mtcars)
pipeVerbose <- TRUE
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg) %T>% p

Upvotes: 1

MrFlick
MrFlick

Reputation: 206536

Since chains take advantage of lazy evaluation, a better translation would have been something like this:

`%<?>%` <- function(lhs, rhs) {
  call <- match.call()
  call[[1]] <- quote(`%<>%`)
  x <- eval.parent(call)
  if (is.data.frame(x) & pipeVerbose) print(nrow(x))
}

We basically re-write the function call and evaluate it.

Upvotes: 2

AEF
AEF

Reputation: 5670

You could make use of a TaskCallback which executes whenever a toplevel task is completed. In the callback check if the expression contains the %<>% operator and if yes print the result:

printAssignmentPipe <- function(exp, res, success, printed){

  if (any(grepl("%<>%", exp, fixed = T))) {
    print(res)
  }
  TRUE
}

addTaskCallback(printAssignmentPipe)

You can easily extend the callback to also check the value of pipeVerbose or you simply call addTaskCallback and removeTaskCallback to activate/deactivate.

Upvotes: 3

Related Questions