【问题标题】:sum by rows with specific columns selected选择特定列的行求和
【发布时间】:2021-12-06 11:41:12
【问题描述】:

在 R 数据表中,我想根据选定的列逐行求和。 示例:

iris = data.table(iris[,-5])
cols = c("Petal.Length","Petal.Width")

我是这样做的,但我不想使用 rowSums 函数:

    iris[, newSum := rowSums(.SD), by = .I, .SDcols = c("Petal.Length","Petal.Width")]

是否有人有一个技巧,可以轻松地将所选列的行相加?

谢谢

【问题讨论】:

  • 你有什么理由不想使用rowSums?它经过高度优化,可能是最快的方式。
  • 我真的很想好好理解数据表的具体语法:.I, .SD, ...做总和而不是 rowSums
  • 仅供参考,datatable 是完全不同的东西,它明确表示 “对于 data.table R 包,请使用 data.table”。 Stack 标签推荐系统不完善,请检查它的所有推荐并阅读他们的悬停文本以确保它确实适用。 (我相应地编辑了你的标签。)谢谢!
  • 坦率地说,我想不出一个解决方案可以做到rowSums 所做的(a)作为声明性的; (b) 更易于阅读,因此更易于维护;和/或 (c) 与rowSums 一样高效/快速。虽然写一些模仿其行为的东西当然是可能的,但当关于 SO 的问题说他们不想要函数 ABC 时,这是因为对所述函数(或正在处理的数据)的错误假设。我们或许可以提出替代方案,但请解释为什么您认为 rowSums 不适合此应用程序。 (顺便说一句,不要使用by=.I,这里效率低/不必要。)
  • 这里是一个简单的例子,只是逐行求和,但是如何使用 data.table 语法逐行应用最严格的自定义函数和选定的列??

标签: r sum data.table rows


【解决方案1】:

rowSums 有什么问题?这是这里最好的方法,顺便说一句,使用 base R 可能会更好:

iris$newSum <- rowSums(iris[, c("Petal.Length", "Petal.Width")])

> iris
     Sepal.Length Sepal.Width Petal.Length Petal.Width newSum
  1:          5.1         3.5          1.4         0.2    1.6
  2:          4.9         3.0          1.4         0.2    1.6
  3:          4.7         3.2          1.3         0.2    1.5
  4:          4.6         3.1          1.5         0.2    1.7
  5:          5.0         3.6          1.4         0.2    1.6
 ---                                                         
146:          6.7         3.0          5.2         2.3    7.5
147:          6.3         2.5          5.0         1.9    6.9
148:          6.5         3.0          5.2         2.0    7.2
149:          6.2         3.4          5.4         2.3    7.7
150:          5.9         3.0          5.1         1.8    6.9
> 

或者如果你真的讨厌和不喜欢rowSums:

iris$newSum <- apply(iris[, c("Petal.Length", "Petal.Width")], 1, sum)

【讨论】:

    【解决方案2】:

    这些不使用 rowSums:

    irisdt[, newSum := Reduce(`+`, .SD), .SDcols = cols]
    
    irisdt[, newSum := as.matrix(.SD) %*% rep(1, ncol(.SD)), .SDcols = cols]
    
    irisdt[, newSum := eval(parse(text = paste(cols, collapse = "+")))]
    
    irisdt[, newSum := apply(.SD, 1, sum), .SDcols = cols]
    
    irisdt[, newSum := sum(.SD), by = 1:ncol(.SD), .SDcols = cols]
    
    irisdt[, newSum := c(rep(1, ncol(.SD)) %*% t(.SD)), .SDcols = cols]
    
    library(purrr)
    irisdt[, newSum := pmap(.SD, sum), .SDcols = cols]
    
    irisdt[, newSum := do.call("mapply", c(sum, .SD)), .SDcols = cols]
    
    irisdt[, newSum := tapply(as.matrix(.SD), row(.SD), sum), .SDcols = cols]
    
    s <- sprintf("irisdt[, newSum := %s]", paste(cols, collapse = "+"))
    eval(parse(text = s))
    

    注意

    library(data.table)
    irisdt <- data.table(iris)    
    

    【讨论】:

      【解决方案3】:

      这本身并不是一个答案,只是对目前提供的答案的比较。

      bench::mark(
        nimliug = iris[, newSum := rowSums(.SD), by = .I, .SDcols = c("Petal.Length","Petal.Width")],
        `nimliug mod` = iris[, newSum := rowSums(.SD), .SDcols = c("Petal.Length","Petal.Width")],
        `U12-Forward 1` = { iris$newSum <- rowSums(iris[, c("Petal.Length", "Petal.Width")]); iris; },
        `U12-Forward 2` = { iris$newSum <- apply(iris[, c("Petal.Length", "Petal.Width")], 1, sum); iris; },
        `G.G 1` = iris[, newSum := Reduce(`+`, .SD), .SDcols = cols],
        `G.G 2` = iris[, newSum := as.matrix(.SD) %*% rep(1, ncol(.SD)), .SDcols = cols], 
        `G.G 3` = iris[, newSum := eval(parse(text = paste(cols, collapse="+")))],
        `G.G 4` = iris[, newSum := apply(.SD, 1, sum), .SDcols = cols],
        `G.G 5` = iris[, newSum := sum(.SD), by = 1:nrow(iris), .SDcols = cols], 
        `G.G 6` = iris[, newSum := c(rep(1, ncol(.SD)) %*% t(.SD)), .SDcols = cols],
        `G.G 7` = iris[, newSum := purrr::pmap_dbl(.SD, sum), .SDcols = cols], 
        `G.G 7 mod` = iris[, newSum := do.call(mapply, c(list(sum), .SD)), .SDcols = cols],
        min_iterations = 1000
      )
      # # A tibble: 12 x 13
      #    expression         min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                     memory                  time             gc                  
      #    <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                     <list>                  <list>           <list>              
      #  1 nimliug        425.4us  541.5us     1662.    52.4KB     0     1000     0   601.83ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [9 x 3]>  <bch:tm [1,000]> <tibble [1,000 x 3]>
      #  2 nimliug mod    387.2us  481.3us     1964.    52.4KB     0     1000     0   509.12ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [9 x 3]>  <bch:tm [1,000]> <tibble [1,000 x 3]>
      #  3 U12-Forward 1  169.8us  221.2us     4050.    45.7KB     3.25  1248     1   308.14ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [14 x 3]> <bch:tm [1,249]> <tibble [1,249 x 3]>
      #  4 U12-Forward 2    377us    503us     1837.    50.5KB     0     1000     0   544.43ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [18 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      #  5 G.G 1          320.6us  508.5us     1889.    66.2KB     1.89   999     1   528.86ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [10 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      #  6 G.G 2          360.1us  392.4us     2275.    52.4KB     0     1138     0   500.21ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [9 x 3]>  <bch:tm [1,138]> <tibble [1,138 x 3]>
      #  7 G.G 3          373.7us  443.4us     2148.    34.3KB     0     1074     0   499.96ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [8 x 3]>  <bch:tm [1,074]> <tibble [1,074 x 3]>
      #  8 G.G 4          540.3us  598.7us     1472.    57.3KB     1.47   999     1   678.56ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [13 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      #  9 G.G 5           4.99ms    5.5ms      177.    51.2KB     1.43   992     8      5.61s <data.table[,5] [150 x 5]> <Rprofmem[,3] [11 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      # 10 G.G 6          377.5us  492.2us     1991.      56KB     0     1000     0   502.26ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [11 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      # 11 G.G 7          707.7us  866.9us     1127.    66.2KB     1.13   999     1   886.81ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [10 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      # 12 G.G 7 mod      460.1us  586.1us     1669.    54.5KB     1.67   999     1   598.62ms <data.table[,5] [150 x 5]> <Rprofmem[,3] [12 x 3]> <bch:tm [1,000]> <tibble [1,000 x 3]>
      

      基准测试肯定是邪恶的,尤其是当基准测试使用的数据不能代表真实数据时(无论是类别还是大小/维度)。然而,由此看来,rowSums 本身显然是最快的(高 `itr/sec`)并且接近最节省内存(低 mem_alloc)。

      由于它们都导出相同的输出(bench::mark 默认为check=TRUE,这确保所有输出都相同),我相信这是一个合理的强度比较等。从这里开始,哪个最有意义?代码的优点不仅仅关乎正确的输出,还关乎可读性和可维护性,尤其是当未来的自己可能无法回忆起为什么选择一些晦涩难懂的代码而不是更直接和声明性的代码时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-06
        • 1970-01-01
        • 2012-01-02
        • 2022-12-03
        • 2020-10-07
        • 2014-05-20
        • 1970-01-01
        相关资源
        最近更新 更多