管道内 n_distinct 出现意外行为

ulf*_*der 4 r dplyr

我试图在函数中的管道内部使用该n_distinct函数dplyr,并发现它以一种我意想不到的方式对我选择的语法敏感。这是一个玩具示例。

# preliminaries
library(tidyverse)
set.seed(123)
X <- data.frame(a1 = rnorm(10), a2 = rnorm(10), b = rep(LETTERS[1:5], times = 2), stringsAsFactors = FALSE)
print(X)
            a1         a2 b
1  -0.56047565  1.2240818 A
2  -0.23017749  0.3598138 B
3   1.55870831  0.4007715 C
4   0.07050839  0.1106827 D
5   0.12928774 -0.5558411 E
6   1.71506499  1.7869131 A
7   0.46091621  0.4978505 B
8  -1.26506123 -1.9666172 C
9  -0.68685285  0.7013559 D
10 -0.44566197 -0.4727914 E
Run Code Online (Sandbox Code Playgroud)

好吧,现在假设我想对该数据框中所选列的名称迭代一个函数(幽默一下)。在这里,我将使用所选列中的值来过滤初始数据集,计算剩余的唯一 id 的数量,并将结果作为单行 tibble 返回,然后将其绑定到新的 tibble 中。当我在函数内创建一个新的 tibble,然后将n_distinct其作为自己的步骤应用于该 tibble 中的选定列时,我从n_distinct、 5 和 4 中获得了预期结果。

bind_rows(map(str_subset(colnames(X), "a"), function(i) {

  subdf <- filter(X, !!sym(i) > 0)

  value <- n_distinct(subdf$b)

  tibble(y = i, n_uniq = value)

}))

# A tibble: 2 x 2
  y     n_uniq
  <chr>  <int>
1 a1         5
2 a2         4
Run Code Online (Sandbox Code Playgroud)

但是,如果我放入n_distinct管道并使用.来引用过滤后的小标题,代码会执行,但会得到不同且不正确的结果。

bind_rows(map(str_subset(colnames(X), "a"), function(i) {

  value <- filter(X, !!sym(i) > 0) %>% n_distinct(.$b)

  tibble(y = i, n_uniq = value)

}))

# A tibble: 2 x 2
  y     n_uniq
  <chr>  <int>
1 a1         5
2 a2         7
Run Code Online (Sandbox Code Playgroud)

那是怎么回事?.我是否误解了管道内部的使用?有什么时髦的吗n_distinct

Ice*_*can 6

n_distinct接受多个参数,在这里您实际上将 tibble 和b列作为参数传递,因为默认情况下会传递管道的左侧。以下是获得预期输出的一些其他方法:

filter(X, !!sym(i) > 0) %>% 
  {n_distinct(.$b)}

filter(X, !!sym(i) > 0) %>% 
  with(n_distinct(b))

library(magrittr)

filter(X, !!sym(i) > 0) %$% 
  n_distinct(b)
Run Code Online (Sandbox Code Playgroud)

另外,与你的问题没有直接关系,这种事情有一个方便的功能

map_dfr(str_subset(colnames(X), "a"), function(i) {

  value <- filter(X, !!sym(i) > 0) %>% {n_distinct(.$b)}

  tibble(y = i, n_uniq = value)

})
Run Code Online (Sandbox Code Playgroud)