如何将矩阵的每一行除以R中的向量元素

Posted

技术标签:

【中文标题】如何将矩阵的每一行除以R中的向量元素【英文标题】:How to divide each row of a matrix by elements of a vector in R 【发布时间】:2022-01-12 05:51:23 【问题描述】:

我想将矩阵的每一除以一个固定向量。例如

mat<-matrix(1,ncol=2,nrow=2,TRUE)
dev<-c(5,10)

mat/dev 将每个除以dev

     [,1] [,2]
[1,]  0.2  0.2
[2,]  0.1  0.1

但是,我希望得到这个结果,即执行 row-wise 操作:

rbind(mat[1,]/dev, mat[2,]/dev)

     [,1] [,2]
[1,]  0.2  0.1
[2,]  0.2  0.1

是否有明确的命令可以到达那里?

【问题讨论】:

请务必注意,mat/dev 只会按照您在length(dev) == nrow(mat) 中显示的那样划分每一列。这是因为 R 将其矩阵信息存储在 column major order 中。 将向量称为vec 会更清楚,就像矩阵是mat 一样,但现在为时已晚。 【参考方案1】:

不仅比最快的基本方法更快(使用矩阵乘法,请参阅@G. Grothendieck's answer),而且更具可读性的是使用rray package,它允许使用其%b/% 运算符进行类似numpy 的广播:

mat %b/% matrix(dev, nrow = 1)

#install.packages("rray")
library(rray)

set.seed(84789)
mat <- matrix(runif(1e6), nrow = 1e5)
dev <- runif(10)

bench::mark(rray = ret <- mat %b/% matrix(dev, nrow = 1); attr(ret, "dimnames") <- NULL; ret, 
            matmult = mat %*% diag(1/dev)
)
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 rray         8.24ms   8.82ms     108.     7.67MB     46.4
#> 2 matmult     11.17ms  12.01ms      77.6    7.66MB     13.9

【讨论】:

【参考方案2】:

这里有几种按代码长度递增的顺序:

t(t(mat) / dev)

mat / dev[col(mat)] #  @DavidArenburg & @akrun

mat %*% diag(1 / dev)

sweep(mat, 2, dev, "/")

t(apply(mat, 1, "/", dev))

plyr::aaply(mat, 1, "/", dev)

mat / rep(dev, each = nrow(mat))

mat / t(replace(t(mat), TRUE, dev))

mapply("/", as.data.frame(mat), dev)  # added later

mat / matrix(dev, nrow(mat), ncol(mat), byrow = TRUE)  # added later

do.call(rbind, lapply(as.data.frame(t(mat)), "/", dev))

mat2 <- mat; for(i in seq_len(nrow(mat2))) mat2[i, ] <- mat2[i, ] / dev

数据框

如果mat 是数据框并产生数据框结果,则所有以mat / 开头的解决方案也适用。 sweep 解决方案和最后一个,即mat2 解决方案也是如此。 mapply 解决方案适用于 data.frames,但会生成一个矩阵。

矢量

如果mat 是纯向量而不是矩阵,则其中任何一个都返回单列矩阵

t(t(mat) / dev)
mat / t(replace(t(mat), TRUE, dev))

这个返回一个向量:

plyr::aaply(mat, 1, "/", dev)

其他人给出错误、警告或不是想要的答案。

基准

代码的简洁明了可能比速度更重要,但为了完整起见,这里有一些使用 10 次重复和 100 次重复的基准。

library(microbenchmark)
library(plyr)

set.seed(84789)

mat<-matrix(runif(1e6),nrow=1e5)
dev<-runif(10)

microbenchmark(times=10L,
  "1" = t(t(mat) / dev),
  "2" = mat %*% diag(1/dev),
  "3" = sweep(mat, 2, dev, "/"),
  "4" = t(apply(mat, 1, "/", dev)),
  "5" = mat / rep(dev, each = nrow(mat)),
  "6" = mat / t(replace(t(mat), TRUE, dev)),
  "7" = aaply(mat, 1, "/", dev),
  "8" = do.call(rbind, lapply(as.data.frame(t(mat)), "/", dev)),
  "9" = mat2 <- mat; for(i in seq_len(nrow(mat2))) mat2[i, ] <- mat2[i, ] / dev,
 "10" = mat/dev[col(mat)])

给予:

Unit: milliseconds
 expr         min          lq       mean      median          uq        max neval
    1    7.957253    8.136799   44.13317    8.370418    8.597972  366.24246    10
    2    4.678240    4.693771   10.11320    4.708153    4.720309   58.79537    10
    3   15.594488   15.691104   16.38740   15.843637   16.559956   19.98246    10
    4   96.616547  104.743737  124.94650  117.272493  134.852009  177.96882    10
    5   17.631848   17.654821   18.98646   18.295586   20.120382   21.30338    10
    6   19.097557   19.365944   27.78814   20.126037   43.322090   48.76881    10
    7 8279.428898 8496.131747 8631.02530 8644.798642 8741.748155 9194.66980    10
    8  509.528218  524.251103  570.81573  545.627522  568.929481  821.17562    10
    9  161.240680  177.282664  188.30452  186.235811  193.250346  242.45495    10
   10    7.713448    7.815545   11.86550    7.965811    8.807754   45.87518    10

对所有耗时

microbenchmark(times=100L,
  "1" = t(t(mat) / dev),
  "2" = mat %*% diag(1/dev),
  "3" = sweep(mat, 2, dev, "/"),
  "5" = mat / rep(dev, each = nrow(mat)),
  "6" = mat / t(replace(t(mat), TRUE, dev)),
 "10" = mat/dev[col(mat)])

给予:

Unit: milliseconds
 expr       min        lq      mean    median        uq       max neval
    1  8.010749  8.188459 13.972445  8.560578 10.197650 299.80328   100
    2  4.672902  4.734321  5.802965  4.769501  4.985402  20.89999   100
    3 15.224121 15.428518 18.707554 15.836116 17.064866  42.54882   100
    5 17.625347 17.678850 21.464804 17.847698 18.209404 303.27342   100
    6 19.158946 19.361413 22.907115 19.772479 21.142961  38.77585   100
   10  7.754911  7.939305  9.971388  8.010871  8.324860  25.65829   100

所以在这两个测试中#2(使用diag)是最快的。原因可能在于它几乎直接吸引了 BLAS,而 #1 则依赖于更昂贵的 t

【讨论】:

我希望前两个选项中的一个最快。 不是最快但非常明确:scale(mat, center = FALSE, scale = dev) @flodel,注意scale 在内部使用sweep @tomka,它为问题中的示例提供了正确答案,但我认为其意图是 mat 可以是具有任意元素的通用矩阵,在这种情况下,它给出了错误的答案,一般。 @Alnair。实际上它确实适用于一列矩阵,但在您的代码中 mat[, -1] 不是一列矩阵。如果您想捕捉这种边缘情况,请使用mat[, -1, drop = FALSE]。请参阅 R 常见问题解答 7.5 -- cran.r-project.org/doc/FAQ/…【参考方案3】:

您正在寻找应用于行的apply 函数:

t(apply(mat, 1, function(x) x/dev))

【讨论】:

谢谢——好的,对于如此简单的操作来说,这似乎相当复杂。这是最简单/最短/最简洁的方式吗? @tomka: m.FUN.m..v &lt;- function(FUN) function(m, v) t(FUN(t(m), v)); '%m/v%' &lt;- m.FUN.m..v('/'); M9 &lt;- matrix(1:9, ncol=3); M9 %m/v% 1:3 足够简短吗?然后你几乎可以免费做m.FUN.m..v('+')等。 (感谢@g-grothendieck 和/或@anton 提供双重转置提示(+1);对引号感到抱歉——如何标记作为代码一部分的反引号?)

以上是关于如何将矩阵的每一行除以R中的向量元素的主要内容,如果未能解决你的问题,请参考以下文章

Python - 将每一行除以一个向量

从r中矩阵的每一行中减去一个常数向量

在犰狳中将矩阵除以向量

如何在sas中将每一行与另一个矩阵元素的每一行相乘?

将函数应用于矩阵或数据框的每一行

numpy用向量减去矩阵的每一行