Reputation: 211
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)?
Upvotes: 2
Views: 7955
Reputation: 47300
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
Upvotes: 4
Reputation: 20085
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
Upvotes: 2
Reputation: 211
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
Upvotes: 2
Reputation: 3264
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
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
Upvotes: 3
Reputation: 7600
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
Upvotes: 1