gpt4 book ai didi

r - dplyr : row_number() from tbl_dt inconsistent to tbl_df 中的唯一行

转载 作者:行者123 更新时间:2023-12-04 07:53:42 24 4
gpt4 key购买 nike

英文缩写:

我想知道如何从 data.table 中获取唯一的行在某处沿着 dplyr工作流程。从 v0.2 开始,我可以使用 row_number==1 (见:Remove duplicated rows using dplyr)

但!
tbl_df(data) %>% group_by(Var1,Var2) %>% filter(row_number() == 1)作品。
tbl_dt(data) %>% group_by(Var1,Var2) %>% filter(row_number() == 1)没有。 这是一个错误吗?

设置:

library(dplyr)
library(data.table)
library(microbenchmark)

little <- expand.grid(rep(letters,121),rep(letters,121)) # my 10M row dataset.
tbl_dt(little) %>% group_by(Var1,Var2) %>% filter(row_number() == 1)

结果:
> Error in rank(x, ties.method = "first") : 
> argument "x" is missing, with no default

这就是我实际上发现它坏了的方式。我是问:

这样还是那样?

我可以使用 unique.data.table方法:
 dt_u <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
unique(.) %>%
tbl_dt(.) }

我可以用 summarise然后 select离开新上校:
dt_ss <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
summarise( n = n() ) %>%
select( -(n) ) }

我可以用 row_number() == 1 # 不适用于 tbl_dt!
 dt_rn <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
filter( row_number() == 1 ) }

以此类推 tbl_df()等价物。

对等价的 data.table/data.frame 方法进行基准测试 microbenchmark(...,times=20) :
> Unit: milliseconds
> expr min lq median uq max neval
> dt_ss() 579.0385 618.0002 661.9056 694.0705 764.2221 20
> dt_u() 690.1284 729.8723 756.5505 783.7379 897.4799 20
> df_ss() 419.7841 436.9871 448.1717 461.7023 523.2798 20
> df_u() 3971.1699 4044.3663 4097.9848 4168.3468 4245.8346 20
> df_rn() 646.1497 687.3472 711.3924 724.6235 754.3166 20

最佳答案

有趣的。你的基准测试激起了我的兴趣。我觉得你不和 data.table 比较有点奇怪的 unique.data.table直接地。因此,这是将其也包含在我的系统中的结果。

# extra function with which the benchmark shown below was run
dt_direct <- function() unique(dt) # where dt = as.data.table(little)

# Unit: milliseconds
# expr min lq median uq max neval
# dt_u() 1472.2460 1571.0871 1664.0476 1742.5184 2647.2118 20
# df_u() 6084.2877 6303.9058 6490.1686 6844.8767 7370.3322 20
# dt_ss() 1340.8479 1485.4064 1552.8756 1586.6706 1810.2979 20
# df_ss() 799.5289 835.8599 884.6501 957.2208 1251.5994 20
# df_rn() 1410.0145 1576.2033 1660.1124 1770.2645 2442.7578 20
# dt_direct() 452.6010 463.6116 486.5015 568.0451 670.3673 20

它比所有运行中最快的解决方案快 1.8 倍。

现在,让我们将唯一值的数量从 676 增加到大约 10,000,看看会发生什么。
val = paste0("V", 1:100)
little <- data.frame(Var1=sample(val, 1e7, TRUE), Var2=sample(val, 1e7, TRUE))
dt <- as.data.table(little)

# Unit: milliseconds
# expr min lq median uq max neval
# dt_u() 1709.458 1776.3510 1892.7761 1991.6339 2562.9171 20
# df_u() 7541.364 7735.4725 7981.3483 8462.9093 9552.8629 20
# dt_ss() 1555.110 1627.6519 1791.5219 1911.3594 2299.2864 20
# df_ss() 1436.355 1500.1043 1528.1319 1649.3043 1961.9945 20
# df_rn() 2001.396 2189.5164 2393.8861 2550.2198 3047.7019 20
# dt_direct() 508.596 525.7299 577.6982 674.2288 893.2116 20

在这里,速度提高了 2.6 倍。

Note: I don't time the creation of dt here because, in real use cases, you can either use fread to get a data.table directly, or use setDT to convert a data.table by reference or directly use data.table(.) instead of data.fame(.) - which is not timed as well.



但为什么两者都是 dt_udt_ss那么慢吗?

通过查看文件 grouped-dt.rmanip-grouped-dt.r ,这是由于 1) 副本和 2) 设置键而发生的。 (1)基本上是因为不得不做(2)。如果您使用 dplyr 进行汇总操作,它等价于:
DT <- copy(DT);
setkey(DT, <group_cols> ## these two are in grouped_dt
DT[, j, by=<group_cols>] ## this is in summarise.grouped_dt
DT <- copy(DT) ## because it calls grouped_dt AGAIN!
## and sets key again - which is O(n) now as DT checked if sorted first..

我不确定为什么在 this discussion under Hadey's answer 之后没有实现临时分组.
## equivalent ad-hoc by
DT[, j, by=<group_cols] ## no copy, no setkey

它避免了副本和设置 key 。

如果你发生变异,那就更糟了。它有效地做:
DT <- copy(DT)
setkey(DT, <group_cols>) ## these two are in grouped_dt
DT <- copy(DT) ## mutate.grouped_dt copies copied data again
DT[, `:=`(...), by=<group_cols>] ## this is in mutate.grouped_dt
DT = copy(DT) ## because of another call to grouped_dt!!!
## and sets key again - which is O(n) now as DT is checked if sorted first..

同样,临时解决方案很简单:
DT   = copy(DT)
DT[, `:=`(...), by=group_cols]

它避免了 2 个副本和设置 key 。唯一的副本是为了满足 dplyr 不就地修改对象的理念。所以,这总是会更慢 + 占用 dplyr 中两倍的内存.

同样,可以避免某些连接上的副本 as I've commented here .

来自 dplyr v0.2 的新闻项目说:

  • dplyr is more careful when setting the keys of data tables, so it never accidentally modifies an object that it doesn't own. It also avoids unnecessary key setting which negatively affected performance. (#193, #255).


但很明显,相当多的讨论案例没有成功。

到目前为止,我在你的问题下写了关于性能标签的文章。也就是说,如果您正在寻找性能,则应该避免所有会生成(不必要的)副本(和设置 key )的情况,直到修复为止。

从本质上讲,在这种特殊情况下,我能想出的最佳答案就是调用 unique.data.table直接在 dplyr方式:
tbl_dt(little) %>% unique(.)

关于r - dplyr : row_number() from tbl_dt inconsistent to tbl_df 中的唯一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23861047/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com