'Unexpected behavior with n_distinct inside pipe
I am trying to use the n_distinct
function from dplyr
inside a pipe in a function and am finding it to be sensitive to my choice of syntax in a way I didn't expect. Here's a toy example.
# 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
Okay, now let's say I want to iterate a function over the names of selected columns in that data frame (humor me). Here, I'm going to use values in the selected column to filter the initial data set, count the number of unique ids that remain, and return the results as a one-row tibble that I then bind into a new tibble. When I create a new tibble inside the function and then apply n_distinct
to a selected column in that tibble as its own step, I get the expected results from n_distinct
, 5 and 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
If I put n_distinct
inside a pipe and use .
to refer to the filtered tibble, however, the code executes but I get a different and incorrect result.
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
What's up with that? Am I misunderstanding the use of .
inside a pipe? Is something funky with n_distinct
?
Solution 1:[1]
n_distinct
accepts multiple arguments and here you're actually passing both the tibble and the b
column as arguments, since the left-hand-side of pipe is passed by default. Here's some other ways of getting the expected output:
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)
Also, not directly related to your question, there's a convenience function for this kind of thing
map_dfr(str_subset(colnames(X), "a"), function(i) {
value <- filter(X, !!sym(i) > 0) %>% {n_distinct(.$b)}
tibble(y = i, n_uniq = value)
})
Solution 2:[2]
Here is minimal example of I think what you are seeing.
iris %>%
n_distinct(.$Species)
# 149
n_distinct(iris$Species)
# 3
The first option is actually doing as follows. The .$Species
is redundant.
n_distinct(iris, iris$Species)
# 149
I think to pipe it without doing weird syntax things you need to use this.
iris %>%
distinct(Species) %>%
count()
# 3
Solution 3:[3]
Agreed...if you're just looking for the number of distinct as in Adam's last example, you might be better off with
length(unique(iris$Species))
depending on what your goals are
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | T_R |