R Conditional evaluation when using the pipe operator %>%
When using the pipe operator %>%
with packages such as dplyr
, ggvis
, dycharts
, etc, how do I do a step conditionally? For example;
step_1 %>%
step_2 %>%
if(condition)
step_3
These approaches don't seem to work:
step_1 %>%
step_2
if(condition) %>% step_3
step_1 %>%
step_2 %>%
if(condition) step_3
There is a long way:
if(condition)
{
step_1 %>%
step_2
}else{
step_1 %>%
step_2 %>%
step_3
}
Is there a better way without all the redundancy?
Here is a quick example that takes advantage of the .
and ifelse
:
X<-1
Y<-T
X %>% add(1) %>% { ifelse(Y ,add(.,1), . ) }
In the ifelse
, if Y
is TRUE
if will add 1, otherwise it will just return the last value of X
. The .
is a stand-in which tells the function where the output from the previous step of the chain goes, so I can use it on both branches.
Edit
As @BenBolker pointed out, you might not want ifelse
, so here is an if
version.
X %>%
add(1) %>%
{if(Y) add(.,1) else .}
Thanks to @Frank for pointing out that I should use {
braces around my if
and ifelse
statements to continue the chain.
I think that's a case for purrr::when()
. Let's sum up a few numbers if their sum is below 25, otherwise return 0.
library("magrittr")
1:3 %>%
purrr::when(sum(.) < 25 ~ sum(.),
~0
)
#> [1] 6
when
returns the value resulting from the action of the first valid condition. Put the condition to the left of ~
, and the action to the right of it. Above, we only used one condition (and then an else case), but you can have many conditions.
You can easily integrate that into a longer pipe.
Here is a variation on the answer provided by @JohnPaul. This variation uses the `if`
function instead of a compound if ... else ...
statement.
library(magrittr)
X <- 1
Y <- TRUE
X %>% `if`(Y, . + 1, .) %>% multiply_by(2)
# [1] 4
Note that in this case the curly braces are not needed around the `if`
function, nor around an ifelse
function—only around the if ... else ...
statement. However, if the dot placeholder appears only in a nested function call, then magrittr will by default pipe the left hand side into the first argument of the right hand side. This behavior is overridden by enclosing the expression in curly braces. Note the difference between these two chains:
X %>% `if`(Y, . + 1, . + 2)
# [1] TRUE
X %>% {`if`(Y, . + 1, . + 2)}
# [1] 4
The dot placeholder is nested within a function call both times it appears in the `if`
function, since . + 1
and . + 2
are interpreted as `+`(., 1)
and `+`(., 2)
, respectively. So, the first expression is returning the result of `if`(1, TRUE, 1 + 1, 1 + 2)
, (oddly enough, `if`
doesn't complain about extra unused arguments), and the second expression is returning the result of `if`(TRUE, 1 + 1, 1 + 2)
, which is the desired behavior in this case.
For more information on how the magrittr pipe operator treats the dot placeholder, see the help file for %>%
, in particular the section on "Using the dot for secondary purposes".
It would seem easiest to me to back off from the pipes a little tiny bit (although I would be interested in seeing other solutions), e.g.:
library("dplyr")
z <- data.frame(a=1:2)
z %>% mutate(b=a^2) -> z2
if (z2$b[1]>1) {
z2 %>% mutate(b=b^2) -> z2
}
z2 %>% mutate(b=b^2) -> z3
This is a slight modification of @JohnPaul's answer (you might not
really want ifelse
, which evaluates both of its arguments
and is vectorized). It would be nice to modify this to return
.
automatically if the condition is false ...
(caution: I think this works but haven't really tested/thought
about it too much ...)
iff <- function(cond,x,y) {
if(cond) return(x) else return(y)
}
z %>% mutate(b=a^2) %>%
iff(cond=z2$b[1]>1,mutate(.,b=b^2),.) %>%
mutate(b=b^2) -> z4