en bref:
我想知道如何在data.table
工作流程的某个地方从dplyr
获取唯一行。从v0.2开始,我可以使用row_number==1
(参见: Remove duplicated rows using dplyr)
BUT!
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
远离新的col:
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()
等价物。
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
答案 0 :(得分:7)
有趣。你的基准刺激了我的兴趣。我觉得有点奇怪的是你不能直接与data.table
的{{1}}进行比较。所以这里的结果也包含在我的系统中。
unique.data.table
比所有跑步中最快的解决方案快1.8倍。
现在,让我们将 676 中的唯一值数量增加到大约10,000,看看会发生什么。
# 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
在这里,它快了2.6倍。
注意:我没有在此处创建
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
,因为在实际使用案例中,您可以使用dt
直接获取data.table,也可以使用fread
通过引用转换setDT
或直接使用data.table
代替data.table(.)
- 这也不是时间。
但为什么data.fame(.)
和dt_u
都比较慢?
通过查看文件dt_ss
和grouped-dt.r
,这是因为1)副本和2)设置密钥。 (1)基本上是因为必须做(2)。如果您使用manip-grouped-dt.r
执行汇总操作,则相当于:
dplyr
我不确定为什么在this discussion under Hadey's answer之后没有实现 ad-hoc 分组。
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..
它可以避免复制和设置密钥。
如果 mutate ,情况会更糟。它正在有效地做:
## equivalent ad-hoc by
DT[, j, by=<group_cols] ## no copy, no setkey
再次,ad-hoc解决方案就是:
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..
它避免了2个副本并设置了密钥.. only copy 是为了满足dplyr的原则,即不在原地修改对象。所以,这总是比较慢+占用DT = copy(DT)
DT[, `:=`(...), by=group_cols]
内存的两倍。
同样,可以避免某些联接的副本as I've commented here。
来自dplyr
的新闻项目说:
- dplyr在设置数据表的键时更加小心,因此它不会意外地修改它不拥有的对象。 它还避免了对性能产生负面影响的不必要的密钥设置。 (#193,#255)。
但显然有一些讨论的案例没有成功。
到目前为止,我在你的问题下写了关于性能标签的文章。也就是说,如果你正在寻找性能,你应该避免所有使(不必要的)副本(和设置键)的情况,直到修复。
就本质而言,在这种特殊情况下,我能想出的最佳答案就是直接以dplyr v0.2
方式致电unique.data.table
:
dplyr
答案 1 :(得分:0)
我遇到了这个问题并找到了一个新的解决方案here,即使用slice
。
我在您的数据集上运行了一些额外的基准测试,包括新的切片功能和更改dt_rn
,以便它首先强制转换为data.frame
:
little <- expand.grid(rep(letters,121),rep(letters,121)) # my 10M row dataset.
dt_u <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
unique(.) %>%
tbl_dt(.) }
dt_rn_df <- function() {
tbl_dt(little) %>%
data.frame() %>%
group_by(Var1,Var2) %>%
filter( row_number() == 1 ) }
dt_slice <- function() {
tbl_dt(little) %>%
group_by(Var1,Var2) %>%
slice(1) }
dt_direct <- function() {
unique(tbl_dt(little), by = c('Var1', 'Var2'))
}
基准测试给出:
> microbenchmark(dt_u(), dt_rn_df(), dt_slice(), dt_direct())
Unit: milliseconds
expr min lq mean median uq max neval
dt_u() 167.4490 173.1940 204.5868 194.1807 212.0271 459.0929 100
dt_rn_df() 639.9241 648.6655 683.3479 683.9603 702.5198 810.2335 100
dt_slice() 163.1873 167.5379 190.2340 195.3807 201.0327 314.0943 100
dt_direct() 156.9491 159.8089 184.3647 184.4096 188.6268 326.4955 100
在您的数据集中,我发现除了强制转换为data.frame
之外的所有函数大约需要相同的时间。特别是我发现使用dt_direct
时Arun发现的速度没有显着提高,注意我的这个函数版本调用了unique(..., by = ...)
。
但是,在另一个包含160万行和28列的数据表中,我发现强制转换为data.frame
实际上更快,请参阅(未提供数据):
> microbenchmark(df2_processed_f <-
+ df2_processed %>%
+ data.frame() %>%
+ dplyr::group_by(v21, v23, v26) %>%
+ filter(row_number() == 1),
+
+
+ df2_processed_dt <-
+ df2_processed %>%
+ dplyr::group_by(v21, v23, v26) %>%
+ dplyr::slice(1))
Unit: milliseconds
expr
df2_processed_f <- df2_processed %>% data.frame() %>% dplyr::group_by(v21, v23, v26) %>% filter(row_number() == 1)
df2_processed_dt <- df2_processed %>% dplyr::group_by(v21, v23, v26) %>% dplyr::slice(1)
min lq mean median uq max neval
411.7717 428.4515 491.9315 462.4099 515.8156 702.2607 100
663.5383 676.7824 699.3258 683.2407 693.7743 933.8118 100