npj*_*pjc 8 performance r dplyr
en bref:
我想知道如何从工作流data.table的某个地方获取唯一的行dplyr.从v0.2开始我可以使用row_number==1(参见: 使用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)
Run Code Online (Sandbox Code Playgroud)
> Error in rank(x, ties.method = "first") :
> argument "x" is missing, with no default
Run Code Online (Sandbox Code Playgroud)
这就是我实际上发现它被打破的方式.我在问:
我可以使用这个unique.data.table方法:
dt_u <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
unique(.) %>%
tbl_dt(.) }
Run Code Online (Sandbox Code Playgroud)
我可以用summarise那么select远的山坳新:
dt_ss <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
summarise( n = n() ) %>%
select( -(n) ) }
Run Code Online (Sandbox Code Playgroud)
我可以使用row_number() == 1 #DOES NOT WORK for tbl_dt!
dt_rn <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
filter( row_number() == 1 ) }
Run Code Online (Sandbox Code Playgroud)
用tbl_df()等价物等等.
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
Run Code Online (Sandbox Code Playgroud)
有趣.你的基准刺激了我的兴趣.我觉得有点奇怪,你不与之比较的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
Run Code Online (Sandbox Code Playgroud)
它比所有运行中最快的解决方案快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
Run Code Online (Sandbox Code Playgroud)
而且,它的速度提高了2.6倍.
注意:我没有时间在
dt这里创建,因为在实际使用情况下,您既可以使用fread直接获取data.table,也可以使用setDT转换data.table引用或直接使用data.table(.)而不是data.fame(.)- 也不是时间.
但是,为什么都dt_u和dt_ss慢呢?
通过查看文件grouped-dt.r和manip-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..
Run Code Online (Sandbox Code Playgroud)
在Hadey回答这个讨论之后,我不确定为什么没有实施临时分组.
## equivalent ad-hoc by
DT[, j, by=<group_cols] ## no copy, no setkey
Run Code Online (Sandbox Code Playgroud)
它避免了副本和设置键.
如果你变异,那就更糟了.它有效地做了:
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..
Run Code Online (Sandbox Code Playgroud)
在这里,ad-hoc解决方案简单地说:
DT = copy(DT)
DT[, `:=`(...), by=group_cols]
Run Code Online (Sandbox Code Playgroud)
它避免了2个副本和设置密钥.. 唯一的副本是为了满足dplyr的原则,即不在原地修改对象.所以,这总是比较慢+占用两倍的内存dplyr.
同样,我在这里评论过,可以避免某些联接的副本.
NEWS项目dplyr v0.2说:
- dplyr在设置数据表的键时更加小心,因此它不会意外地修改它不拥有的对象.它还避免了不必要的密钥设置,从而对性能产生负面影响.(#193,#255).
但显然有一些讨论的案例没有成功.
到目前为止,我在你的问题下写了关于性能标签的文章 也就是说,如果你正在寻找性能,你应该避免所有使(不必要的)副本(和设置键)的情况,直到修复.
在这个本质中,在这种特殊情况下,我能想出的最佳答案就是unique.data.table直接以dplyrish方式调用:
tbl_dt(little) %>% unique(.)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7849 次 |
| 最近记录: |