简体   繁体   中英

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

I can't get to make this work, although it seems fairly simple. I would like to multiply every row in matrix (or dataframe or datatable) b, with vector a.

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

Desired output (in matrix, dataframe or datatable form):

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

Can anyone help me how to do this (efficiently)?

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

or just b*rep(a,each=nrow(b)) if you define a <- 1:4

It's just a vectorised element wise multiplication with no transformation appart from rep .

edit:

It seems that rep is what is slowing down my solution. Here's a benchmark where I include an option with precomputed rep, and some improvements on the sweep option (taking only relevant parts from source code).

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

Option#1: Using data.table features:

Note:it works because column number and value matches for 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

Option#2: could be:

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

UPDATE

Option#3: To handle decimal/actual values in 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

On my side I would use the built-in method of R for Matrix Multiplication %*% .

Considering the vector : [NB: data.table is not vector ]

a <- c(1:4)

and considering the matrix:

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

Your output is given by:

output <- b %*% diag(a)

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

If you think this solution is very inefficient for your needs, then I suggest to use the built-in function 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

Thanks for the replies. I've tested the suggested solutions above on speed (with actual size of my vector and matrix) to use the most efficient one:

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
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

The tricky part is that your a is a data.table. If it's actually a vector, then it's much simpler:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM