Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 898
Z117 Avatar asked Dec 12 '25 08:12

Z117


2 Answers

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
like image 54
Moody_Mudskipper Avatar answered Dec 13 '25 22:12

Moody_Mudskipper


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
like image 39
Seymour Avatar answered Dec 13 '25 21:12

Seymour



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!