Combining Pipes and The Magrittr Dot (.) Placeholder

Combining pipes and the magrittr dot (.) placeholder

The "problem" is that magrittr has a short-hand notation for anonymous functions:

. %>% is.data.frame

is roughly the same as

function(.) is.data.frame(.)

In other words, when the dot is the (left-most) left-hand side, the pipe has special behaviour.

You can escape the behaviour in a few ways, e.g.

(.) %>% is.data.frame

or any other way where the LHS is not identical to .
In this particular example, this may seem as undesirable behaviuour, but commonly in examples like this there's really no need to pipe the first expression, so is.data.frame(.) is as expressive as . %>% is.data.frame, and
examples like

data %>% 
some_action %>%
lapply(. %>% some_other_action %>% final_action)

can be argued to be clearner than

data %>% 
some_action %>%
lapply(function(.) final_action(some_other_action(.)))

dot notation in magrittr pipe not be

. in this case refers to data which is present in the previous step which is (data %>% group_by(carb)). Although the data is grouped it is still complete data. If you are on dplyr > 1.0.0 you could use cur_data() to refer to the data in the group.

library(dplyr)
library(broom)
library(tidyr)

data %>%
group_by(carb) %>%
summarize(new = list(tidy(lm(formula = drat ~ mpg, data = cur_data())))) %>%
unnest(cols = new)

This gives the same output as your first example.

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]

Native pipe placeholder

The native R pipe does not use dot. It always inserts into the first argument. To get the effect of dot define a function or if it is at the beginning combine it yourself repeating the input (or break it up into two pipelines and do the same -- not shown since doesn't apply here).

library(dplyr)

mtcars |>
(\(x) filter(x, complete.cases(x)))() |>
summary()

or

f <- function(x) filter(x, complete.cases(x))
mtcars |> f() |> summary()

or

filter(mtcars, complete.cases(mtcars)) |> summary()

Sometimes with can be used to create a workaround. This creates a list with one element named x and then uses that in the next leg of the pipe.

mtcars |>
list() |>
setNames("x") |>
with(filter(x, complete.cases(x))) |>
summary()

Note that you can do this with only base R -- the Bizarro pipe which is not really a pipe but looks like one.

mtcars ->.;
filter(., complete.cases(.)) ->.;
summary(.)

Update

Since this question appeared R has added a _ placeholder so the with example could be shortened to:

# needs R 4.2 or later
mtcars |>
list(x = _) |>
with(filter(x, complete.cases(x))) |>
summary()

`magrittr` pipe into apply

Here are two ways, with the magrittr and native pipes.

suppressPackageStartupMessages(library(magrittr))
dat <- 1:10
locs <- list(c(1, 2),
c(3, 4),
c(5, 6))

foo <- function(x, y, z) {
out <- mean(c(x[y], x[z]))
return(out)
}

# wrap the function around parenthesis
dat %>%
(\(d) lapply(locs, \(z) foo(., z[1], z[2])))()
#> [[1]]
#> [1] 1.5
#>
#> [[2]]
#> [1] 3.5
#>
#> [[3]]
#> [1] 5.5

# New native pipe with anonymous function
dat |>
{\(d) lapply(locs, \(z) foo(x = d, z[1], z[2]))}()
#> [[1]]
#> [1] 1.5
#>
#> [[2]]
#> [1] 3.5
#>
#> [[3]]
#> [1] 5.5

Created on 2022-05-18 by the reprex package (v2.0.1)

How to combine magrittr pipes and %in% inside a dplyr::filter predicate function?

Don't make this complicated for its own sake.

ids <- (df %>% count(id) %>% arrange(n) %>% tail(2))$id
filter(df, id %in% ids)


Related Topics



Leave a reply



Submit