【问题标题】:Sort a data.table fast by Ascending/Descending order按升序/降序快速排序 data.table
【发布时间】:2012-11-21 00:56:52
【问题描述】:

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

sort by ascending Year, ascending MemberID, descending Month 

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

setkey(X, Year, MemberID)

这非常快,只需几秒钟。

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

这一步需要更长的时间(5 分钟)。

更新: 有人发表评论做X &lt;- 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 

我现在的问题是:如果我想在排序后(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 速度很快。

【问题讨论】:

    标签: r performance data.table


    【解决方案1】:

    2014 年 6 月 5 日更新:

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

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

    基准测试:

    这是使用setorder、data.table 的内部快速顺序和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 的顺序比 base:::order~79 倍setorderbase:::order~135 倍 .

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

    所有这些新的优化和功能共同构成FR #2405bit64::integer64 support also has been added.


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

    【讨论】:

    • @AdamNYC NP。当您的意思是order 时,您似乎在写sort?二进制搜索与i 而不是by 有关,所以我不确定您在编辑中的意思。我不认为它会很慢,而且你所做的似乎是正确的。
    • 谢谢,马修。我更正了我的更新。你的解释很清楚。
    • @AdamNYC 好的,但是 66s 感觉很慢。请尝试选项 1,只是为了完整性。
    • 嗨,马修,我根据您的建议更新了我的问题。顺便说一句,我非常感谢你提供了这么棒的包裹。
    • @MatthewDowle 是否可以在setkey 中添加将排序顺序设置为降序的功能?例如,选项 1.b 就是 setkey(X,Year,MemberID,-Month)
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-08
      • 2023-03-07
      • 2012-12-21
      • 2017-10-03
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多