意外的 dplyr::bind_rows() 行为

Unexpected dplyr::bind_rows() behavior

简短版本:

我遇到了 dplyr::bind_rows() 的错误,我不明白。我想根据某些条件(例如 a == 1)拆分我的数据,对一部分进行操作(例如 b = b * 10),然后在单个管道中使用 dplyr::bind_rows() 将其绑定回另一部分链。如果我明确地向两个部分提供第一个输入,它工作正常,但如果我用 . 将它们通过管道输入,它会抱怨 agrument 2.

的数据类型

这是问题的 MRE:

library(tidyverse)

# sim data
d <- tibble(a = 1:4, b = 1:4)

# works when 'd' is supplied directly to bind_rows()
bind_rows(d %>% filter(a == 1),
          d %>% filter(!a == 1) %>% mutate(b = b * 10))
#> # A tibble: 4 x 2
#>       a     b
#>   <int> <dbl>
#> 1     1     1
#> 2     2    20
#> 3     3    30
#> 4     4    40


# fails when 'd' is piped in to bind_rows()
d %>%
  bind_rows(. %>% filter(a == 1),
            . %>% filter(!a == 1) %>% mutate(b = b * 10))
#> Error: Argument 2 must be a data frame or a named atomic vector.

长版:

如果我捕获 bind_rows() 调用作为 list() 输入的内容,我可以看到发生了两件(对我来说)意想不到的事情。

  1. 我没有评估我提供的管链,它似乎只是将它们捕获为 functional sequence
  2. 我可以看到除了两个显式参数之外还无形地提供了输入 (.),因此我在列表中得到了 3 个项目而不是 2 个。
# capture intermediate values for diagnostics
d %>%
  list(. %>% filter(a == 1),
            . %>% filter(!a == 1) %>% mutate(b = b * 10))
#> [[1]]
#> # A tibble: 4 x 2
#>       a     b
#>   <int> <int>
#> 1     1     1
#> 2     2     2
#> 3     3     3
#> 4     4     4
#> 
#> [[2]]
#> Functional sequence with the following components:
#> 
#>  1. filter(., a == 1)
#> 
#> Use 'functions' to extract the individual functions. 
#> 
#> [[3]]
#> Functional sequence with the following components:
#> 
#>  1. filter(., !a == 1)
#>  2. mutate(., b = b * 10)
#> 
#> Use 'functions' to extract the individual functions.

这让我想到了以下不雅的解决方案,我通过管道连接到内部函数解决了第一个问题,这似乎强制正确评估(出于我不明白的原因),然后通过子集 list 在执行 bind_rows() 操作之前。

# hack solution to force eval and clean duplicated input
d %>%
  list(filter(., a == 1),
       filter(., !a == 1) %>% mutate(b = b * 10)) %>%
  .[-1] %>% 
  bind_rows()
#> # A tibble: 4 x 2
#>       a     b
#>   <int> <dbl>
#> 1     1     1
#> 2     2    20
#> 3     3    30
#> 4     4    40

reprex package (v2.0.1)

于 2022-01-24 创建

似乎与 问题有关,但我不太明白是怎么回事。如果能理解为什么会发生这种情况并找到一种无需分配中间变量或进行这种怪异的破解来对中间列表进行子集化的编码方式,那就太好了。


编辑:

知道这与花括号有关 ({}) 使我能够找到一些更有帮助的链接: 1, 2, 3

如果我们想使用.,那么用作用域运算符({})

来阻止它
library(dplyr)
d %>%
   {
  bind_rows({.} %>% filter(a == 1),
            {.} %>% filter(!a == 1) %>% mutate(b = b * 10))
   }

-输出

# A tibble: 4 × 2
      a     b
  <int> <dbl>
1     1     1
2     2    20
3     3    30
4     4    40