【问题标题】:R data.table: How to optimize calculation of the value differences between two data tables for each corresponding group?R data.table:如何优化计算每个对应组的两个数据表之间的值差异?
【发布时间】:2016-02-20 17:40:34
【问题描述】:

我有很多预订数据(数百万行),并且想要计算存储在两个单独数据表中的不同年份的相同组之间的预订金额的变化(差异 = 减法)。

我可以使用出色的 data.table 来做到这一点,如下面的代码所示,但是 如何优化代码(关于性能和内存消耗),因为我正在复制数据(表格)并且有几个计算步骤可以同时完成吗?

# Calculate value differences for the same group of data in two different data.tables
cur <- data.table(company=c("A", "B", "New"), booking.date=seq(from=as.Date("2011/01/01"), by="week", length.out=12), sales.amount = 201:212, vat.amount = 11:22)
cur

prev <- data.table(company=c("A", "B"), booking.date=seq(from=as.Date("2010/01/01"), by="month", length.out=10), sales.amount = 101:110, vat.amount = 1:10)
prev

diff <- copy(prev)   # copy to keep the original data.table unchanged
diff[, `:=`(sales.amount = -sales.amount, vat.amount = -vat.amount)]   # negate the amounts so that the sum will be the difference
diff <- rbind(diff, cur)  # combine negative previous amounts with positive current amounts so that the sum will be difference
diff  # show raw data
diff[, .(last.booking.date=max(booking.date), sales.amount.diff=sum(sales.amount), vat.amount.diff=sum(vat.amount)), by=company] # calculate the difference

# Look at company "A" to verify the result:
cur[company=="A",]
prev[company=="A",]

示例数据和预期输出如下所示:

数据表1:当年的预订量:

> cur
    company booking.date sales.amount vat.amount
 1:       A   2011-01-01          201         11
 2:       B   2011-01-08          202         12
 3:     New   2011-01-15          203         13
 4:       A   2011-01-22          204         14
 5:       B   2011-01-29          205         15
 6:     New   2011-02-05          206         16
 7:       A   2011-02-12          207         17
 8:       B   2011-02-19          208         18
 9:     New   2011-02-26          209         19
10:       A   2011-03-05          210         20
11:       B   2011-03-12          211         21
12:     New   2011-03-19          212         22

数据表2:上一年的预订量:

> prev
   company booking.date sales.amount vat.amount
 1:       A   2010-01-01          101          1
 2:       B   2010-02-01          102          2
 3:       A   2010-03-01          103          3
 4:       B   2010-04-01          104          4
 5:       A   2010-05-01          105          5
 6:       B   2010-06-01          106          6
 7:       A   2010-07-01          107          7
 8:       B   2010-08-01          108          8
 9:       A   2010-09-01          109          9
10:       B   2010-10-01          110         10

预期结果(每家公司每个预订年度总和的差异):

   company last.booking.date sales.amount.diff vat.amount.diff
1:     A 1        2011-03-05               297              37
2:     B 1        2011-03-12               296              36
3:   New 1        2011-03-19               830              70

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    @Jaap 的好方法

    另一种不将原始表绑定在一起的方法是:

    # aggregate tables by company
    cur_co <- cur[, .(last.booking.date = max(booking.date),
                      sales.amount = sum(sales.amount),
                      vat.amount   = sum(vat.amount)),
                  by=company]
    
    prev_co <- prev[, .(sales.amount = sum(sales.amount),
                        vat.amount = sum(vat.amount)),
                    by=company]
    
    
    # join & get difference
    cur_co[prev_co, c("sales.amount.diff", "vat.amount.diff") :=
               .(sales.amount - i.sales.amount, vat.amount - i.vat.amount),
           on="company"]
    
    # fill NA's (companies missing in previuos year)
    cur_co[is.na(sales.amount.diff),
             c("sales.amount.diff", "vat.amount.diff") :=
               .(sales.amount, vat.amount)]
    
    # drop unused columns
    cur_co[, c("sales.amount", "vat.amount") := NULL]
    

    给出完全相同的输出:

       company last.booking.date sales.amount.diff vat.amount.diff
    1:       A        2011-03-05               297              37
    2:       B        2011-03-12               296              36
    3:     New        2011-03-19               830              70
    

    【讨论】:

    • 不错的选择!但是有两个注意事项:1)这仅在您有两个数据表时才有效,并且 2)cur_coprev_co 数据表是新副本,因此获得了新的内存地址
    • 1) 没错,这就是 OP 所要求的。你的解决方案虽然更灵活。 2)正确,这些是副本,但由于聚合可能会更小。 rbindlist() 是否阻止分配新内存?
    • @Christian Borck 感谢您的回答,首先聚合是一个好方法。我猜rbindlist 将始终分配新内存,因为它必须显着增加列的向量大小。
    • @CristianBorck @Jaap 我已经完成了 system.time 基准测试,有 11 个 mio 行和一个令人惊讶的结果(所有时间都“过去了”,并且是几次试验中最好的):我最初的实现:0,28 s Christian Brock 的版本:0,18 s Jaap 的版本:5,8 s(第一个版本)和 2,8 s(具有“移位”功能的第二个版本) Jaap 版本最慢的原因是仅用于by 用于分组。我的“经验教训”:按表达式分组可能很昂贵(慢)!无论如何感谢大家的回答:-) !!!
    • @RYoda 因为对于你的情况,Christian 的回答显然是最快的,你应该接受他的回答 imo。
    【解决方案2】:

    这可能是最简单的方法,将原始数据表绑定在一起,然后进行计算:

    # bind the data.table's together into one
    dt.all <- rbindlist(list(cur,prev))
    # set the key to 'company' and 'booking.date'
    # the data.table is now also ordered by these two columns
    setkey(dt.all, company, booking.date)
    
    dt.all[, .(last.booking.date = booking.date[.N],
               sales.amount.diff = sum(sales.amount[year(booking.date)==2011]) - sum(sales.amount[year(booking.date)==2010]),
               vat.amount.diff = sum(vat.amount[year(booking.date)==2011]) - sum(vat.amount[year(booking.date)==2010])),
           company]
    

    给予:

       company last.booking.date sales.amount.diff vat.amount.diff
    1:       A        2011-03-05               297              37
    2:       B        2011-03-12               296              36
    3:     New        2011-03-19               830              70
    

    如果您有多年的时间,更好的方法可能是:

    dt.all[, .(last.booking.date = booking.date[.N],
               sum.sales = sum(sales.amount),
               sum.vat = sum(vat.amount)),
           .(company, year(booking.date))
           ][, `:=` (last.booking.date = last.booking.date[.N],
                     sales.amount.diff = sum.sales - shift(sum.sales),
                     vat.amount.diff = sum.vat - shift(sum.vat)),
             company][]
    

    给出:

       company year last.booking.date sum.sales sum.vat sales.amount.diff vat.amount.diff
    1:       A 2010        2011-03-05       525      25                NA              NA
    2:       A 2011        2011-03-05       822      62               297              37
    3:       B 2010        2011-03-12       530      30                NA              NA
    4:       B 2011        2011-03-12       826      66               296              36
    5:     New 2011        2011-03-19       830      70                NA              NA
    

    fill = 0 添加到shift 参数将导致:

       company year last.booking.date sum.sales sum.vat sales.amount.diff vat.amount.diff
    1:       A 2010        2011-03-05       525      25               525              25
    2:       A 2011        2011-03-05       822      62               297              37
    3:       B 2010        2011-03-12       530      30               530              30
    4:       B 2011        2011-03-12       826      66               296              36
    5:     New 2011        2011-03-19       830      70               830              70
    

    【讨论】:

    • 将原始数据表绑定在一起对我来说似乎也是一个必要的先决条件!不知道shift,谢谢解答,我去试试!
    • @RYoda 让我知道它是否有效。 shiftdata.table 的快速 lag/lead 功能实现。有关详细信息,请参阅?shift
    • 关于你的第一行,rbind(cur,prev) 调用 rbindlist 如果两者都是 data.tables。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    相关资源
    最近更新 更多