选择n_distinct()中的多个列和R中的length(unique())

dkqlctbz  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(119)

我在R中编写了一个函数,它将列名变量的向量作为字符串,并使用n_distinct执行调整后的计数,如下所示:

library(tidyverse)
> packageVersion("tidyverse")
[1] ‘1.3.2’

vars <- c("Sepal.Length", "Petal.Width")

foo <- iris |>
  group_by(Species) |>
  mutate(
    raw_count = n(),
    adjusted_count = n_distinct(across(all_of(vars)))
    )

对于Species“setosa”,这将导致原始计数为50,调整后的计数为28
然而,我有一个大数据集,并且这个函数正在R Shiny应用程序中使用,所以我尝试尽可能优化。
我读到过length(unique())n_distinct()更快,并且我在其他函数中看到了一些加速,但是对于这种使用,我遇到了两个问题。

bar <- iris |>
  group_by(Species) |>
  mutate(
    raw_count = n(),
    adjusted_count = length(unique(across(all_of(vars))))
  )

在本例中替换n_distinct()会导致length(unique())计算vars向量(2)中不同字符串的数量,这显然不是所需的结果。
所以我用实际的变量名进行了测试。

baz <- iris |>
  group_by(Species) |>
  mutate(
    raw_count = n(),
    adjusted_count = length(unique(Sepal.Length, Petal.Width))
  )

对于Species“setosa”,这会导致原始计数为50,调整后的计数为15,我不确定为什么这会产生与n_distinct不同的结果。
如果有人能解释结果的差异,以及如何将列名的字符向量传递给length(unique()),我们将不胜感激。

c9qzyr3d

c9qzyr3d1#

如果您正在寻找一个快速的解决方案,您可以尝试data.table::uniqueN
当x是原子向量时,uniqueN等效于length(unique(x));当x是data.frame或data. table时,uniqueN等效于nrow(unique(x))。直接计算唯一行的数量,而无需具体化中间唯一data.table,因此速度更快,内存效率更高。

iris |>
  group_by(Species) |>
  mutate(
    raw_count = n(),
    adjusted_count = data.table::uniqueN(across(all_of(vars))))
  )

该文档还告诉您,在 Dataframe 中,n_distinct的基R等价物是nrow(unique(x)),而不是length(unique(x))

iris |>
  group_by(Species) |>
  mutate(
    raw_count = n(),
    adjusted_count = nrow(unique(across(all_of(vars))))
  )

这是因为应用于 Dataframe 的length计算列数,而不是行数:

length(iris)
#[1] 5

使用大型 Dataframe (30,000行,100组)进行基准测试:

b <- data.frame(group = gl(300, 100),
                var1 = rbinom(30000, 1, .5),
                var2 = rbinom(30000, 1, .5)) |>
  group_by(group)
vars <- c("var1", "var2")

bench::mark(baseR = mutate(b, adjusted_count = nrow(unique(across(all_of(vars))))),
            dplyr = mutate(b, adjusted_count = n_distinct(across(all_of(vars)))),
            data.table = mutate(b, adjusted_count = uniqueN(across(all_of(vars)))))

#  expression    min median itr/s…¹ mem_a…² gc/se…³ n_itr  n_gc total…⁴
#  <bch:expr> <bch:> <bch:>   <dbl> <bch:b>   <dbl> <int> <dbl> <bch:t>
#1 baseR       131ms  136ms    7.36  1.87MB    7.36     2     2   272ms
#2 dplyr       120ms  124ms    7.18  1.35MB    2.39     3     1   418ms
#3 data.table   88ms  109ms    9.19  1.12MB    4.60     2     1   218ms

相关问题