我试图在函数中的管道内使用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
[好,现在我想在该数据框中的选定列的名称上迭代一个函数(让我感到惊讶)。在这里,我将使用所选列中的值来过滤初始数据集,计算剩余的唯一ID的数量,并将结果作为单行标题返回,然后绑定到新标题中。当我在函数内创建一个新的小标题,然后将n_distinct
应用于该小标题中的选定列作为其自己的步骤时,我从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
如果将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
这是怎么回事?我是否误解了管道内使用.
? n_distinct
有点时髦吗?
n_distinct
接受多个参数,实际上,您在这里同时传递了小标题和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)
而且,与您的问题没有直接关系,这种事情有一个便捷功能
map_dfr(str_subset(colnames(X), "a"), function(i) {
value <- filter(X, !!sym(i) > 0) %>% {n_distinct(.$b)}
tibble(y = i, n_uniq = value)
})
这是我认为您所看到的内容的最小示例。
iris %>%
n_distinct(.$Species)
# 149
n_distinct(iris$Species)
# 3
第一个实际上与执行此操作相同。 .$Species
实际上是多余的。
iris %>%
n_distinct()
# 149
我认为无需使用奇怪的语法就可以使用它来传递它。
iris %>%
distinct(Species) %>%
count()
# 3