tidyverse / magrittr

Improve the readability of R code with the pipe
https://magrittr.tidyverse.org
Other
957 stars 157 forks source link

%>% pipe operator, . placeholder and ~ function. Three good friends? #202

Closed szamoransky closed 4 years ago

szamoransky commented 4 years ago

I have some problem with these three good friends. I would like to use a %>% operator with . placeholder in an ~ function. But it seems that it does not work as I expect.

Here is a triangle:

library(tidyverse)
library(magrittr)

(triangle <- tibble(a = 1:5,
                    b = c(6:9,NA),
                    c = c(10:12,rep(NA,2)),
                    d = c(13:14,rep(NA,3)),
                    e = c(0L,rep(NA,4))))
#> # A tibble: 5 x 5
#>       a     b     c     d     e
#>   <int> <int> <int> <int> <int>
#> 1     1     6    10    13     0
#> 2     2     7    11    14    NA
#> 3     3     8    12    NA    NA
#> 4     4     9    NA    NA    NA
#> 5     5    NA    NA    NA    NA

I would like to fill the NA values from the first column. I don't understand why this does not work:

(triangle %>% map_dfc(~coalesce(.x, .[[1]])))
#> # A tibble: 5 x 5
#>       a     b     c     d     e
#>   <int> <int> <int> <int> <int>
#> 1     1     6    10    13     0
#> 2     2     7    11    14     0
#> 3     3     8    12    13     0
#> 4     4     9    10    13     0
#> 5     5     6    10    13     0

It handles the . placeholder as the .x argument of the ~ function, instead of the object 'triangle'.

This code works, but I don't understand why the previous does not do the same.

(triangle %>% map_dfc(function(x) coalesce(x, .[[1]])))
#> # A tibble: 5 x 5
#>       a     b     c     d     e
#>   <int> <int> <int> <int> <int>
#> 1     1     6    10    13     0
#> 2     2     7    11    14     2
#> 3     3     8    12     3     3
#> 4     4     9     4     4     4
#> 5     5     5     5     5     5

Any idea? Thanks!

I use R version 3.4.4 (2018-03-15) -- "Someone to Lean On"

moodymudskipper commented 4 years ago

The . is protected from the pipe because ~ is a quoting function.

The pipe is not substituting symbols, it's evaluating the expression on the rhs in a context where . has the value of the lhs.

In purrr lambdas .x and . are equivalent.

What you want to do is pass the first column as a second argument to your function, and use it as .y there :

triangle %>% map_dfc(~coalesce(.x, .y), .[[1]])
#> # A tibble: 5 x 5
#>       a     b     c     d     e
#>   <int> <int> <int> <int> <int>
#> 1     1     6    10    13     0
#> 2     2     7    11    14     2
#> 3     3     8    12     3     3
#> 4     4     9     4     4     4
#> 5     5     5     5     5     5