R - 用向量乘以df或矩阵的每一行

时间:2018-03-24 08:05:58

标签: r data.table matrix-multiplication

虽然看起来很简单,但我无法完成这项工作。 我想将矩阵(或数据帧或数据表)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

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

5 个答案:

答案 0 :(得分:3)

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的变换appart。

编辑:

似乎代表正在减缓我的解决方案。这是一个基准测试,其中包含一个预先计算的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

答案 1 :(得分:2)

选项#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

中的小数/实际值
#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

答案 2 :(得分:2)

在我这边,我会使用R的内置方法进行矩阵乘法%*%

考虑向量:[NB: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

如果您认为此解决方案对您的需求效率非常低,那么我建议使用内置函数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

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

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。如果它实际上是一个向量,那么它就更简单了:

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