【问题标题】:How do I add a column to a data.table and return multiple columns without modifying underlying data?如何在 data.table 中添加一列并返回多列而不修改基础数据?
【发布时间】:2022-01-22 09:14:33
【问题描述】:

我在R 中有以下data.table

dt <- data.table(gender = c("Male", "Female"), Prop = c(0.49, 0.51))
#   gender Prop
# 1:   Male  0.49
# 2: Female  0.51

我想计算一个Freq = Prop * 1000 列,然后只返回genderFreq 列。如何在一行代码中明确引用gender 列并且修改dt

我能做到的最好的是:

onsdist$gender[, c(.SD, Freq = Prop * 1000)][, .SD, .SDcols = - "Prop"]
#    gender Freq1 Freq2
# 1:   Male   490   490
# 2: Female   510   510

但我最终得到了重复的 Freq 列。

(我不想引用gender的原因是因为它在data.tables之间发生了变化。我不想修改dt的原因是因为我需要重新使用原始版本稍后)。

【问题讨论】:

    标签: r dataframe data.table


    【解决方案1】:

    我们可以使用data.table语法来获取输出格式

    dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = -"Prop"]
    

    -输出

       gender Freq
    1:   Male  490
    2: Female  510
    

    【讨论】:

      【解决方案2】:

      1) 使用带有 Prop = NULL 的变换

      dt[, transform(.SD, Freq = Prop * 1000, Prop = NULL)]
      ##    gender Freq
      ## 1:   Male  490
      ## 2: Female  510
      

      2) 或此变体

      transform(dt, Freq = Prop * 1000, Prop = NULL)
      ##    gender Freq
      ## 1:   Male  490
      ## 2: Female  510
      

      3) 我们可以通过将 transform 替换为 collapse 包中的 ftransform 来显着加快速度。

      library(collapse)
      dt[, ftransform(.SD, Freq = Prop * 1000, Prop = NULL)]
      

      4)类似

      library(collapse)
      ftransform(dt, Freq = Prop * 1000, Prop = NULL)
      

      基准测试

      使用问题中的数据,我们看到上面的 (4),下面标记为 ex4,它使用 ftransform from collapse 而没有 [.data.table 比上面的其他方法快得多。

      library(collapse)
      library(data.table)
      library(microbenchmark)
      
      microbenchmark(
          ex1 = dt[, transform(.SD, Freq = Prop * 1000, Prop = NULL)],
          ex2 = transform(dt, Freq = Prop * 1000, Prop = NULL),
          ex3 = dt[, ftransform(.SD, Freq = Prop * 1000, Prop = NULL)],
          ex4 = ftransform(dt, Freq = Prop * 1000, Prop = NULL)
      )
      
      Unit: microseconds
       expr      min       lq       mean    median       uq      max neval  cld
        ex1 1847.601 1927.402 2046.04098 2015.4015 2093.251 2706.200   100    d
        ex2  959.700 1000.701 1074.93098 1046.1510 1122.601 1606.201   100  b  
        ex3 1048.201 1090.351 1139.57598 1121.6005 1174.201 1381.602   100   c 
        ex4   68.401   85.551   93.08802   89.2515  100.551  168.400   100 a   
      

      【讨论】:

      • 添加了从折叠中用 ftransform 替换 transform 的代码。
      【解决方案3】:

      另一种解决方案

      dt[, .(dt[, 1], Freq = Prop * 1000)]
      
         gender Freq
      1:   Male  490
      2: Female  510
      

      所有答案中给出的选项的一些基准

      请注意,我增加了相当多的样本数据,但我也只是好奇其他数据集的方法之间的差异。

      这里的转换非常慢,不推荐使用,其他方法非常相似,.SD 和 .SDcols 的功能是最快的,尽管在这种情况下,保留所有行并且不使用第一个引用更新任何内容方法几乎不会慢。

      set.seed(42)
      
      dt <- data.table(
        gender = rep(LETTERS[1:25], 40000),
        Prop = runif(n = 1000000))
      
      library(rbenchmark)
      
      benchmark(
        "dt[, .(dt[, 1], Freq = Prop * 1000)]" = {
          dt[, .(dt[, 1], Freq = Prop * 1000)]
        },
        "dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = 1]" = {
          dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = 1]
        },
        "dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = -\"Prop\"]" = {
          dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = -"Prop"]
        },
        "dt[, transform(.SD, Freq = Prop * 1000, Prop = NULL)]" = {
          dt[, transform(.SD, Freq = Prop * 1000, Prop = NULL)]
        },
        "transform(dt, Freq = Prop * 1000, Prop = NULL)" = {
          transform(dt, Freq = Prop * 1000, Prop = NULL)
        },
        replications = 1000,
        columns = c("test", "replications", "elapsed", "relative")
      )
      
      #                                                     test replications elapsed relative
      # 1                   dt[, .(dt[, 1], Freq = Prop * 1000)]         1000   18.66    1.112
      # 3 dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = -"Prop"]         1000   17.02    1.014
      # 2       dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = 1]         1000   16.78    1.000
      # 4  dt[, transform(.SD, Freq = Prop * 1000, Prop = NULL)]         1000  333.51   19.875
      # 5         transform(dt, Freq = Prop * 1000, Prop = NULL)         1000  329.41   19.631
      

      旁注

      请记住,通过引用创建列的速度快了 5 倍 dt[, Freq := Prop * 1000] 和 OP 使用该表稍后重新使用的参数。我建议始终在速度加快时通过参考表格进行所有计算和准备工作。您始终可以从那里对输出进行子集化。

      #                                               test replications elapsed relative
      # 1             dt[, .(dt[, 1], Freq = Prop * 1000)]         1000   16.25    5.783
      # 2 dt[, c(.SD, .(Freq = Prop * 1000)), .SDcols = 1]         1000   13.33    4.744
      # 3                         t[, Freq := Prop * 1000]         1000    2.81    1.000
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-28
        • 2011-04-13
        • 2016-06-01
        相关资源
        最近更新 更多