R Conditional Evaluation When Using the Pipe Operator %≫%

R Conditional evaluation when using the pipe operator %%

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.

use if() to use select() within a dplyr pipe chain

You need to make sure that your statement between { returns a data.frame regardless of the condition. So you need an else ..

cond <- FALSE

mtcars %>%
group_by(cyl) %>%
{ if (cond) filter(., am == 1) else . } %>%
summarise(m = mean(wt))

Works fine with TRUE or FALSE.

(Also note that a simple example like this really makes the question a lot more easy to grasp.)

Use of ifelse after pipe operator

Your solution works fine if you add curly braces:

c(18,20,21,15) %>% {ifelse(.>18, .+1900, .+2000)}
# [1] 2018 1920 1921 2015

You could also do this to avoid the {}:

c(18,20,21,15) %>% `+`(1900+100*(.<=18))

Or with magrittr:

library(magrittr)
c(18,20,21,15) %>% add(1900+100*(.<=18))

keeping your ifelse:

c(18,20,21,15) %>% add(ifelse(.>18,1900,2000))

Reason for magrittr pipe (%%) with ifelse behavior?

The magrittr documentation says that when the dot is used in a nested function calls that it behaves as you saw.

Using the dot for secondary purposes

Often, some attribute or property of lhs is desired in the rhs call in addition to the value of lhs itself, e.g. the number of rows or columns. It is perfectly valid to use the dot placeholder several times in the rhs call, but by design the behavior is slightly different when using it inside nested function calls. In particular, if the placeholder is only used in a nested function call, lhs will also be placed as the first argument! The reason for this is that in most use-cases this produces the most readable code.

For example, iris %>% subset(1:nrow(.) %% 2 == 0) is equivalent to iris %>% subset(., 1:nrow(.) %% 2 == 0) but slightly more compact. It is possible to overrule this behavior by enclosing the rhs in braces. For example, 1:10 %>% {c(min(.), max(.))} is equivalent to c(min(1:10), max(1:10)).

So the solution that is recommened is actually to use the curly braces as you have already found.

The logical evaluation seems to be a seperate function call within the ifelse and therefore the reason that it's behaving as such.

Conditionally apply pipeline step depending on external value

How about this approach:

mtcars %>% 
tibble::rownames_to_column(var = "model") %>%
filter(if(applyfilter== 1) grepl(x = model, pattern = "Merc") else TRUE) %>%
group_by(am) %>%
summarise(meanMPG = mean(mpg))

This means grepl is only evaluated if the applyfilter is 1, otherwise the filter simply recycles a TRUE.


Or another option is to use {}:

mtcars %>% 
tibble::rownames_to_column(var = "model") %>%
{if(applyfilter == 1) filter(., grepl(x = model, pattern = "Merc")) else .} %>%
group_by(am) %>%
summarise(meanMPG = mean(mpg))

There's obviously another possible approach in which you would simply break the pipe, conditionally do the filter and then continue the pipe (I know OP didn't ask for this, just want to give another example for other readers)

mtcars %<>% 
tibble::rownames_to_column(var = "model")

if(applyfilter == 1) mtcars %<>% filter(grepl(x = model, pattern = "Merc"))

mtcars %>%
group_by(am) %>%
summarise(meanMPG = mean(mpg))

Combining conditional evaluation within dplyr pipe operators (%%)

I've managed it (see result all the way at the bottom) by first defining two columns in my data prep script, stating whether to plot a square marker or a default marker ('IND_VAR') and whether to display a star inside the corresponding marker ('VAR_SHOWING_STAR'):

 Dataset <- Dataset %>% 
dplyr::group_by(NAME, VAR) %>%
dplyr::mutate(IND_VAR = ifelse(VAR == '4', 1, 0)) %>%
dplyr::ungroup() %>%
dplyr::mutate(NICE_ICON = ifelse(VAR_SHOWING_STAR == "NOT", "", "Star"))

Second, I've defined colors in my app script:

 Dataset <- Dataset %>%
mutate(COLOR_WAIT = case_when(
(is.na(WAIT) | is.nan(WAIT)) ~"gray",
(WAIT >= 0 & WAIT <= 1) ~ "darkgreen",
(WAIT == 2) ~ "green",
(WAIT >= 3 & WAIT <= 4) ~ "lightgreen",
(WAIT >= 5 & WAIT <= 6) ~ "orange",
(WAIT >= 7 & WAIT <= 8) ~ "lightred",
(WAIT >= 9 & WAIT <= 10) ~ "red",
TRUE ~ "darkred"))

Third, I've defined icons (also in my app script), including an ifelse() regarding 'IND_VAR':

  icons <- makeAwesomeIcon(icon = Dataset$NICE_ICON, lib  = 'fa',
squareMarker = ifelse(Dataset$IND_VAR == 1, TRUE, FALSE),
iconColor = "#FFFFFF", spin = TRUE,
markerColor = Dataset$COLOR_WAIT)

And lastly, I've implemented addAwesomeMarkers() in renderLeaflet({}):

    %>% 
addAwesomeMarkers(lng = ~longitude, lat = ~latitude, icon = icons,
label = ~as.character(Dataset$SOME_LABEL),
popup = paste0("<strong>Pop_up: </strong>", Dataset$SOME_POPUP) %>%

Result:

Sample Image

How to pipe to if statements in R

Here's an option with dplyr's case_when()

df %>% 
mutate(Plataform = case_when(
!is.na(tw_likes) | !is.na(tw_comments) ~ "Twitter",
!is.na(fb_likes) | !is.na(fb_comments) ~ "Facebook",
!is.na(ig_likes) | !is.na(ig_comments) ~ "Instagram"))

Access result later in pipe

This is due to R's lazy evaluation. It occurs even if pipes are not used. See code below. In that code the argument to n_excluded is filter(n_before(iris), Species != 'setosa') and at the point that rows is used in the print statement the argument has not been referenced from within n_excluded so the entire argument will not have been evaluated and so rows does not yet exist.

if (exists("rows")) rm(rows)  # ensure rows does not exist
n_excluded(filter(n_before(iris), Species != 'setosa'))
## Error in h(simpleError(msg, call)) :
## error in evaluating the argument 'x' in selecting a method for function
## 'print': object 'rows' not found

To fix this

1) we can force x before the print statement.

n_excluded = function(x) { 
force(x)
print(rows - nrow(x))
return(x)
}

2) Alternately, we can use the magrittr sequential pipe which guarantees that legs are run in order. magrittr makes it available but does not provide an operator for it but we can assign it to an operator like this.

`%s>%` <- magrittr::pipe_eager_lexical
iris %>%
n_before() %>%
filter(Species != 'setosa') %s>% # note use of %s>% on this line
n_excluded()

The magrittr developer has stated that he will add it as an operator if there is sufficient demand for it so you might want to add such request to magrittr issue #247 on github.

R combinations with dot (.), ~, and pipe (%%) operator

That line uses the . in three different ways.

         [1]             [2]      [3]
aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2))

Generally speaking you pass in the value from the pipe into your function at a specific location with . but there are some exceptions. One exception is when the . is in a formula. The ~ is used to create formulas in R. The pipe wont change the meaning of the formula, so it behaves like it would without any escaping. For example

aggregate(. ~ cyl, data=mydata)

And that's just because aggregate requires a formula with both a left and right hand side. So the . at [1] just means "all the other columns in the dataset." This use is not at all related to magrittr.

The . at [2] is the value that's being passed in as the pipe. If you have a plain . as a parameter to the function, that's there the value will be placed. So the result of the subset() will go to the data= parameter.

The magrittr library also allows you to define anonymous functions with the . variable. If you have a chain that starts with a ., it's treated like a function. so

. %>% mean %>% round(2)

is the same as

function(x) round(mean(x), 2)

so you're just creating a custom function with the . at [3]



Related Topics



Leave a reply



Submit