58

我有一个大约 300 万行和 40 列的 data.table。我想按组内的降序对该表进行排序,如下面的 sql 模拟代码:

sort by ascending Year, ascending MemberID, descending Month 

data.table 中是否有等效的方法来执行此操作?到目前为止,我必须将其分解为两个步骤:

setkey(X, Year, MemberID)

这非常快,只需几秒钟。

X <- X[,.SD[order(-Month)],by=list(Year, MemberID)]

此步骤需要更长的时间(5 分钟)。

更新:有人发表了评论X <- X[sort(Year, MemberID, -Month)],后来被删除。这种方法似乎要快得多:

user  system elapsed 
5.560  11.242  66.236 

我的方法: setkey() 然后 order(-Month)

   user  system elapsed 
816.144   9.648 848.798 

我现在的问题是:如果我想在 sort(Year, MemberID, Month) 之后按 Year、MemberId 和 Month 进行汇总,data.table 是否识别排序顺序?

更新 2:回复 Matthew Dowle:

在使用 Year、MemberID 和 Month 设置键之后,我仍然每组有多个记录。我想要为每个组进行总结。我的意思是:如果我使用 X[order(Year, MemberID, Month)],求和是否利用了 data.table 的二进制搜索功能:

monthly.X <- X[, lapply(.SD[], sum), by = list(Year, MemberID, Month)]

更新 3:Matthew D 提出了几种方法。第一种方法的运行时间比 order() 方法快:

   user  system elapsed 
  7.910   7.750  53.916 

马修:让我感到惊讶的是转换月份的符号需要大部分时间。没有它,setkey 的速度非常快。

4

2 回答 2

86

2014 年 6 月 5 日更新:

data.table v1.9.3 的当前开发版本实现了两个新功能,即:setordersetorderv,这正是您所需要的。这些函数通过选择在每列上选择升序或降序来对data.table 按引用重新排序。查看?setorder更多信息。

此外,DT[order(.)]默认情况下还优化为使用data.table's internal fast order而不是base:::order. 与 不同setorder,这将制作数据的完整副本,因此内存效率较低,但仍将比使用 base 的顺序操作快几个数量级。

基准:

这是使用setorder, data.table 的内部快速顺序和 with的速度差异说明base:::order

require(data.table) ## 1.9.3
set.seed(1L)
DT <- data.table(Year     = sample(1950:2000, 3e6, TRUE), 
                 memberID = sample(paste0("V", 1:1e4), 3e6, TRUE), 
                 month    = sample(12, 3e6, TRUE))

## using base:::order
system.time(ans1 <- DT[base:::order(Year, memberID, -month)])
#   user  system elapsed 
# 76.909   0.262  81.266 

## optimised to use data.table's fast order
system.time(ans2 <- DT[order(Year, memberID, -month)])
#   user  system elapsed 
#  0.985   0.030   1.027

## reorders by reference
system.time(setorder(DT, Year, memberID, -month))
#   user  system elapsed 
#  0.585   0.013   0.600 

## or alternatively
## setorderv(DT, c("Year", "memberID", "month"), c(1,1,-1))

## are they equal?
identical(ans2, DT)    # [1] TRUE
identical(ans1, ans2)  # [1] TRUE

在此数据上,基准测试表明 data.table 的顺序比这里约 79 倍,base:::order比这里快约 135 倍。setorderbase:::order

data.table总是在 C 语言环境中排序/排序。如果您需要在其他语言环境中订购,那么您才需要使用DT[base:::order(.)].

所有这些新的优化和功能共同构成了FR #2405还添加了 bit64::integer64 支持


注意:请参阅历史/修订以获取较早的答案和更新。

于 2012-12-03T15:47:49.957 回答
14

评论是我的,所以我会发布答案。我删除了它,因为我无法测试它是否等同于你已经拥有的。很高兴听到它更快。

X <- X[order(Year, MemberID, -Month)]

总结不应取决于行的顺序。

于 2012-12-03T15:45:05.347 回答