R - 将 df 或矩阵的每一行与一个向量相乘

R - Multiply every row of df or matrix with a vector

我无法完成这项工作,尽管它看起来相当简单。 我想将矩阵(或数据框或数据表)b 中的每一行与向量 a 相乘。

a <- data.table(t(1:4))
b <- matrix(data=2, nrow=3, ncol=4)

期望的输出(矩阵、数据框或数据表形式):

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8  
[2,]    2    4    6    8
[3,]    2    4    6    8

任何人都可以帮助我如何(有效地)做到这一点吗?

选项#1: 使用 data.table 功能:

注意:之所以有效,是因为列号和值与 a

匹配
a[,lapply(.SD,function(x)(x*b[,x]))]
#   V1 V2 V3 V4
#1:  2  4  6  8
#2:  2  4  6  8
#3:  2  4  6  8

选项#2: 可以是:

t(t(b) * (as.matrix(a)[1,]))
     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8

更新

选项#3: 处理 a

中的 decimal/actual 值
#Cases when `a` contains decimal values can be handled as
a <- data.table(t(c(1, 0.24, 3, 4)))
b <- matrix(data=2, nrow=3, ncol=4)

a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))]
#   V1   V2 V3 V4
#1:  2 0.48  6  8
#2:  2 0.48  6  8
#3:  2 0.48  6  8
dplyr::bind_rows(apply(b, 1, `*`, a))
   V1 V2 V3 V4
1:  2  4  6  8
2:  2  4  6  8
3:  2  4  6  8

棘手的部分是您的 a 是 data.table。如果真的是vector,那就简单多了:

apply(b, 1, `*`, 1:4)
     [,1] [,2] [,3]
[1,]    2    2    2
[2,]    4    4    4
[3,]    6    6    6
[4,]    8    8    8

在我这边,我会使用 R 的 built-in 方法进行矩阵乘法 %*%

考虑到 向量:[注意:data.table 不是 vector]

a <- c(1:4)

并考虑矩阵:

b <- matrix(data=2, nrow=3, ncol=4)

您的输出为:

output <- b %*% diag(a)

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8

如果您认为此解决方案无法满足您的需求,那么我建议使用 built-in 函数 sweep:

sweep(b, 2, a, FUN = "*")

     [,1] [,2] [,3] [,4]
[1,]    2    4    6    8
[2,]    2    4    6    8
[3,]    2    4    6    8
b*rep(unlist(a),each=nrow(b))
#      [,1] [,2] [,3] [,4]
# [1,]    2    4    6    8
# [2,]    2    4    6    8
# [3,]    2    4    6    8

或者只是 b*rep(a,each=nrow(b)) 如果你定义 a <- 1:4

这只是一个向量化的元素明智的乘法,没有来自 rep 的转换应用程序。

编辑:

似乎是 rep 拖慢了我的解决方案。这是一个基准测试,其中我包含一个带有预计算代表的选项,以及对扫描选项的一些改进(仅从源代码中获取相关部分)。

a <- data.table(t(1:200))
b <- matrix(data=2, nrow=100000, ncol=200)

a_vec <- unlist(a)
rep_a <- rep(a_vec,each=nrow(b))
microbenchmark::microbenchmark(
  mkr1 = a[,lapply(.SD,function(x)(x*b[,x]))],
  mkr2 = t(t(b) * (as.matrix(a)[1,])),
  mkr_update = a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))],
  mm = b*rep(unlist(a),each=nrow(b)),
  mm_cheat = b*rep_a,
  regular_sweep = sweep(b,2,unlist(a),`*`),
  regular_sweep2 = sweep(b,2,a_vec,`*`),
  improved_sweepA1 = b*aperm(array(unlist(a),rev(dim(b)))),
  improved_sweepA2 = b*aperm(array(a_vec,rev(dim(b)))),
  improved_sweepB1 = b*a[rep_len(1,nrow(b)),],
  improved_sweepB2 = b*t(a_vec)[rep_len(1,nrow(b)),],
  unit = "relative",
  times=50)


Unit: relative
             expr       min        lq      mean    median        uq       max neval
             mkr1  42.12228  44.15266  50.23959  46.35240  57.20280  65.07289    50
             mkr2 114.58427 124.19653 125.25660 131.08677 124.17058 114.91137    50
       mkr_update   1.00000   1.00000   1.00000   1.00000   1.00000   1.00000    50
               mm 231.34331 223.74365 217.50145 225.91117 215.90765 165.64814    50
         mm_cheat  13.38838  13.22556  14.94682  13.36649  12.95260  25.15564    50
    regular_sweep  96.15758 124.26746 121.04428 128.67282 129.19407 119.20210    50
   regular_sweep2  97.79001 124.69191 124.74650 134.64249 134.97407 107.47152    50
 improved_sweepA1  96.57837 124.86189 116.93736 127.08909 124.92805 105.83318    50
 improved_sweepA2  96.27737 122.49773 118.45262 128.13369 126.15029 106.58669    50
 improved_sweepB1 214.95773 227.39523 226.04339 248.38553 232.50401 161.45341    50
 improved_sweepB2  31.20967  32.61873  37.74552  33.70969  41.52149  55.93362    50

感谢您的回复。我已经在速度上测试了上面建议的解决方案(使用我的向量和矩阵的实际大小)以使用最有效的解决方案:

a <- data.table(t(1:200))
b <- matrix(data=2, nrow=100000, ncol=200)

system.time(sweep(b, MARGIN=2, t(a), "*"))
#   user  system elapsed 
#   0.31    0.06    0.39 

system.time(a[,lapply(.SD,function(x)(x*b[,x]))])
#   user  system elapsed 
#    0.2     0.0     0.2 

#system.time(bind_rows(apply(b,1,`*`,a)))     
#took 100+ so stopped it manually

system.time(t(t(b)*(as.matrix(a)[1,])))
#   user  system elapsed 
#   0.31    0.05    0.36 

system.time(apply(b, 1, `*`, 1:200))
#   user  system elapsed 
#   1.20    0.11    1.31 

system.time(b*rep(unlist(a),each=nrow(b)))
#   user  system elapsed 
#   0.83    0.05    0.89 

system.time(b*rep((1:200),each=nrow(b)))
#   user  system elapsed 
#   0.36    0.06    0.42