【问题标题】:Rowwise matrix multiplication in RR中的行矩阵乘法
【发布时间】:2019-11-20 18:26:32
【问题描述】:

我有一个维度为 1 亿条记录和 100 列的矩阵。

现在我想将该矩阵乘以行。

我的矩阵乘法示例代码是

df<-as.matrix(mtcars)
result<-apply(df,1,prod)

在我的情况下,上面的语法非常慢。

我尝试了 Rfast 包中的 rowprods 功能。

result<-rowprods(mtcars)

但是上面的函数给了我空间问题。

注意:我的系统中有 8 GB 内存。

【问题讨论】:

  • 真的是矩阵还是data.table? (我问是因为您添加了 data.table 标记)
  • 如果这是一个矩阵,试试matrixStats::rowProds(df)。还有,那些神秘的“太空问题”是什么?
  • 您是否尝试过按 1 或 1000 万块运行 rowprods ?
  • 它只是一个矩阵。我添加 data.table 的原因是,它执行的操作要快得多。
  • 1 亿条记录和 100 列是 76 GB。你能把你的数据放在内存中吗?

标签: r


【解决方案1】:

如果您的矩阵太大而无法放入内存,您可以使用包 bigstatsr(免责声明:我是作者)来使用存储在磁盘上的数据(而不是 RAM )。使用函数big_apply 使您可以在数据块上应用标准 R 函数(并组合它们)。

library(bigstatsr)
fbm <- FBM(10e6, 100)
# inialize with random numbers
system.time(
  big_apply(fbm, a.FUN = function(X, ind) {
    print(min(ind))
    X[, ind] <- rnorm(nrow(X) * length(ind))
    NULL
  }, a.combine = 'c')
) # 78 sec

# compute row prods, possibly in parallel
system.time(
  prods <- big_apply(fbm, a.FUN = function(X, ind) {
    print(min(ind))
    matrixStats::rowProds(X[ind, ])
  }, a.combine = 'c', ind = rows_along(fbm),
  block.size = 100e3, ncores = nb_cores())  
) # 22 sec with 1 core and 18 sec with 6 cores

【讨论】:

    【解决方案2】:

    尝试使用 data.tableReduce 打包。这可能会避免 1e10 长度向量的内部副本。

    library(data.table)
    df <- data.table(df, keep.rownames=TRUE)
    df[, rowprods:= Reduce("*", .SD), .SDcols = -1]
    df[, .(rn, rowprods)]
    #                     rn   rowprods
    # 1:           Mazda RX4          0
    # 2:       Mazda RX4 Wag          0
    # 3:          Datsun 710  609055152
    # 4:      Hornet 4 Drive          0
    # 5:   Hornet Sportabout          0
    # 6:             Valiant          0
    # 7:          Duster 360          0
    # 8:           Merc 240D          0
    # 9:            Merc 230          0
    #10:            Merc 280          0
    #11:           Merc 280C          0
    #12:          Merc 450SE          0
    #13:          Merc 450SL          0
    #14:         Merc 450SLC          0
    #15:  Cadillac Fleetwood          0
    #16: Lincoln Continental          0
    #17:   Chrysler Imperial          0
    #18:            Fiat 128  470578906
    #19:         Honda Civic  564655046
    #20:      Toyota Corolla  386281789
    #21:       Toyota Corona          0
    #22:    Dodge Challenger          0
    #23:         AMC Javelin          0
    #24:          Camaro Z28          0
    #25:    Pontiac Firebird          0
    #26:           Fiat X1-9  339825992
    #27:       Porsche 914-2          0
    #28:        Lotus Europa 1259677924
    #29:      Ford Pantera L          0
    #30:        Ferrari Dino          0
    #31:       Maserati Bora          0
    #32:          Volvo 142E 1919442833
    #                     rn    rowsums
    

    但是,如果您想处理这种大小的数据,8 GB RAM(减去您的操作系统和其他软件所需的内存)并不多。 R 有时需要制作内部副本才能使用您的数据。

    【讨论】:

    • 您不同意 David 的矩阵运算比 data.table 运算快吗?此外,您可能希望将 rowsums 列命名为 rowprods。
    • 我不同意大卫的这个具体例子。矩阵代数可能总是比其他方法更快(如果不需要额外的数据副本来应用它),但 OP 的示例不是矩阵代数,我认为数据被复制了。 (虽然不知道rowprods 函数。)在循环中使用* 99 次应该很快。
    • 我看到 matrixStats::rowProds 工作正常,但执行操作也需要大量时间。
    【解决方案3】:

    一些时间供参考

    library(matrixStats)
    library(inline)
    library(data.table)
    #devtools::install_github("privefl/bigstatsr")
    library(bigstatsr)
    library(RcppArmadillo)
    library(microbenchmark)
    set.seed(20L)
    N <- 1e6
    dat <- matrix(rnorm(N*100),ncol=100)
    
    fbm <- FBM(N, 100)
    big_apply(fbm, a.FUN = function(X, ind) {
        print(min(ind))
        X[, ind] <- rnorm(nrow(X) * length(ind))
        NULL
    }, a.combine = 'c')   
    
    bigstatsrMtd <- function() {
        prods <- big_apply(fbm, a.FUN = function(X, ind) {
            print(min(ind))
            matrixStats::rowProds(X[ind, ])
        }, a.combine = 'c', ind = rows_along(fbm),
            block.size = 100e3, ncores = nb_cores())  
    }
    
    df <- data.table(as.data.frame(dat), keep.rownames=TRUE)
    data.tableMtd <- function() {
        df[, rowprods:= Reduce("*", .SD), .SDcols = -1]
        df[, .(rn, rowprods)]    
    }
    
    code <- '
      arma::mat prodDat = Rcpp::as<arma::mat>(dat);
      int m = prodDat.n_rows;
      int n = prodDat.n_cols;
      arma::vec res(m);
      for (int row=0; row < m; row++) {
        res(row) = 1.0;
        for (int col=0; col < n; col++) {
          res(row) *= prodDat(row, col);
        }
      }
      return Rcpp::wrap(res);
    '
    rcppProd <- cxxfunction(signature(dat="numeric"), code, plugin="RcppArmadillo")
    
    rcppMtd <- function() {
        rcppData <- rcppProd(dat)                # generated by C++ code
    }
    
    baseMtd <- function() {
        apply(dat, 1, prod)   
    }
    
    microbenchmark(bigstatsrMtd(),
        data.tableMtd(),
        rcppMtd(),
        baseMtd(),
        times=5L
    )
    

    注意:编译cxxfunction中的函数似乎需要一些时间

    以下是计时结果:

    # Unit: milliseconds
    #            expr       min        lq      mean    median        uq       max
    #  bigstatsrMtd() 4519.1861 4993.0879 5296.7000 5126.2282 5504.3981 6340.5995
    # data.tableMtd()  443.1946  444.9686  690.3703  493.2399  513.4787 1556.9695
    #       rcppMtd()  787.9488  799.1575  828.3647  809.0645  871.0347  874.6178
    #       baseMtd() 5658.1424 6208.5123 6232.0040 6331.7431 6458.6806 6502.9417
    

    【讨论】:

    • 如果你有一个标准的 R 矩阵dat 你可以做fbm &lt;- big_copy(dat)
    • 谢谢,@F.Privé 我在时间安排中忽略了 fbm 的创建。
    • 请注意,也可以编写一些 Rcpp 代码用于 FBM。
    • 我尝试了 rcppMtd(),但出现错误。我的错误消息是:错误:Mat::operator(): index out of bounds rcppProd(dat) 中的错误:Mat::operator(): index out of bounds 我的代码是 'dat
    【解决方案4】:

    Rfast 命令 "rowprods" 接受矩阵,而不是 data.frame。其次,任何 row 或 colprods 命令都会出现数值溢出错误。所以最好使用 Rfast::colprods(x, method = "expsumlog").

    【讨论】:

      猜你喜欢
      • 2021-01-31
      • 2012-04-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-27
      • 1970-01-01
      • 2016-03-27
      相关资源
      最近更新 更多