Use Pipe Without Feeding First Argument

Use pipe without feeding first argument

Is the %>% pipe operator always feeding the left-hand side (LHS) to the first argument of the right-hand side (RHS)? Even if the first argument is specified again in the RHS call?

No. You’ve noticed the exception yourself: if the right-hand side uses ., the first argument of the left-hand side is not fed in. You need to pass it manually.

However, this is not happening in your case because you’re not using . by itself, you’re using it inside an expression. To avoid the left-hand side being fed as the first argument, you additionally need to use braces:

iris %>% {cor(x = .$Sepal.Length, y = .$Sepal.Width)}

Or:

iris %$% cor(x = Sepal.Length, y = Sepal.Width)

— after all, that’s what %$% is there for, as opposed to %>%.

But compare:

iris %>% lm(Sepal.Width ~ Sepal.Length, data = .)

Here, we’re passing the left-hand side expression explicitly as the data argument to lm. By doing so, we prevent it being passed as the first argument to lm.

Why does pipe inside if() function fail when first argument is referred with a '.'

There are two problems:

  1. both the calls to check1 and check2 give errors because their inputs have not been defined

  2. a magrittr pipeline that begins with just a dot on the left hand side defines a function so in the first case the part within the condition portion of the if is defining a function, not a logical condition.

     library(magrittr)

    f <- . %>% { . ^ 2 }
    f(3)
    ## [1] 9

This fails for the same reason

      library(purrr)
library(dplyr)

BOD %>% { if (. %>% pull("demand") %>% is.numeric) 1 else 0 }

but this works since the left hand side is now (.) instead of just .

      BOD %>% { if ( (.) %>% pull("demand") %>% is.numeric) 1 else 0 }

Rxjs, Pipe with one argument

Since RxJS v6, takeUntil (and the others) have become pipeable operators rather than a standalone function.

In the link you shared, please have a look at the imports section which means this example uses a former version of RxJS:

import 'rxjs/add/operator/takeUntil';

From the official docs of RxJS v6, the import path of takeUntil becomes:

import { takeUntil } from 'rxjs/operators';

For further reading: https://rxjs-dev.firebaseapp.com/api/operators/takeUntil

Magritttr + lapply where first argument isn't to LHS

Your issue here is that %>% is inserting mydf as the first argument (so that three arguments are getting passed to lapply. Try wrapping the entire lapply expression in brackets. This prevents the insertion behavior:

mydf %>%
{ lapply( 1:length(.), function(x) {
manipulate_df( mydf[x], using_column_names(names(mydf)[x] )
}) }

I think the prettiest fix would be to make a new function:

manipulate_whole_df = function(mydf)
lapply( 1:length(mydf), function(x)
manipulate_df( mydf[x], using_column_names(names(mydf)[x] ) ) )

mydf %>%
manipulate_whole_df

Or even

library(tidyr)

mydf %>%
gather(variable, value) %>%
group_by(variable) %>%
do(manipulate_df(.$value,
.$variable %>% first %>% using_column_name ) )

Should I use %$% instead of %%?

No, you shouldn't use %$% routinely. It is like using the with() function, i.e. it exposes the component parts of the LHS when evaluating the RHS. But it only works when the value on the left has names like a list or dataframe, so you can't always use it. For example,

library(magrittr)
x <- 1:10
x %>% mean()
#> [1] 5.5
x %$% mean()
#> Error in eval(substitute(expr), data, enclos = parent.frame()): numeric 'envir' arg not of length one

Created on 2022-02-06 by the reprex package (v2.0.1.9000)

You'd get a similar error with x %$% mean(.).

Even when the LHS has names, it doesn't automatically put the . argument in the first position. For example,

mtcars %>% nrow()
#> [1] 32
mtcars %$% nrow()
#> Error in nrow(): argument "x" is missing, with no default

Created on 2022-02-06 by the reprex package (v2.0.1.9000)

In this case mtcars %$% nrow(.) would work, because mtcars has names.

Your example involving .$hp and .$mpg is illustrating one of the oddities of magrittr pipes. Because the . is only used in expressions, not alone as an argument, it is passed as the first argument as well as being passed in those expressions. You can avoid this using braces, e.g.

mtcars %>% {plot(.$hp, .$mpg)}

How can I use the dplyr chain operator %% get the left side itself in R?

The dot . is correct. However, %>% also inserts it as the first argument:

x = c(5,7,8,1)
x %>% sum(.[1 : 3])

Is the same as:

sum(x, x[1 : 3])

You can explicitly prevent this behaviour by wrapping the expression in braces:

x %>% {sum(.[1 : 3])}

However, at this point it may be better to split the pipeline up a bit more (as you’ve done yourself):

x %>% `[`(1 : 3) %>% sum()

Or, using magrittr helper functions (requires library(magrittr)):

x %>% extract(1 : 3) %>% sum()

F#, Pipe-forward first argument

When the order of arguments is inconvenient, you can always transform the function itself to make it take the arguments in a different order:

let flip f x y = f y x

[1; 2; 3]
|> (flip List.append) [4; 5; 6]

Transforming functions is useful in many contexts. It is a functional programmer's bread and butter.

But in the end, I believe, readability is most important: programs are read incomparably more often than they are written. When I find myself in a similar situation, and I don't want to name the intermediate value or use a lambda expression for some reason, I define myself a function that would be intuitive to understand:

let prependTo a b = List.append b a

[1; 2; 3]
|> prependTo [4; 5; 6]

Using the %% pipe, and dot (.) notation

The problem isn't map, but rather how the %>% pipe deals with the .. Consider the following examples (remember that / is a two argument function in R):

Simple piping:

1 %>% `/`(2)

Is equivalent to `/`(1, 2) or 1 / 2 and gives 0.5.

It is also equivalent to 1 %>% `/`(., 2).

Simple . use:

1 %>% `/`(2, .)

Is equivalent to `/`(2, 1) or 2 / 1 and gives 2.

You can see that 1 is no longer used as the first argument, but only as the second.

Other . use:

This doesn't work however, when subsetting the .:

list(a = 1) %>% `/`(.$a, 2)
Error in `/`(., .$a, 2) : operator needs one or two arguments

We can see that . got injected twice, as the first argument and subsetted in the second argument. An expression like .$a is sometimes referred to as a nested function call (the $ function is used inside the / function, in this case).

We use braces to avoid first argument injection:

list(a = 1) %>% { `/`(.$a, 2) }

Gives 0.5 again.

Actual problem:

You are actually calling map(df, df$data, min), not map(df$data, min).

Solution:

Use braces:

df %>% { map(.$data, min) }

Also see the header Using the dot for secondary purposes in ?magrittr::`%>%` which reads:

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)).

magrittr pipe of dot to do.call function

Unless there is a dot as an argument to the main function, here do.call, the pipe will insert the left hand side into argument 1. Using dot within an expression does not count.

Brace brackets can be used to disable automatic insertion, with can be used such that with is the main function instead of do.call, use magrittr's %$% pipe or use a function which explicitly does what you want.

(Note that mean will give a result of NA if there are any NA's in its argument; to suppress the NA's add na.rm=TRUE as an argument to mean -- not shown.)

airquality %>% { do.call("mean", list(x = .$Ozone)) }

airquality %>% with(do.call("mean", list(x = Ozone)))

library(magrittr)
airquality %$% do.call("mean", list(x = Ozone))

# needs R 4.1+ . With earlier versions use function(.) in place of \(.)
airquality %>% (\(.) do.call("mean", list(x = .$Ozone)))

|>

With the |> pipe introduced into base R 4.1 these work but not the analogs to others above:

airquality |> with(do.call("mean", list(x = Ozone)))

airquality |> (\(.) do.call("mean", list(x = .$Ozone)))()


Related Topics



Leave a reply



Submit