Get the rows of a matrix using another matrix

146 views Asked by At

I'm trying to find a vectorized way to do the following -

X =
   0   1
   5   5
  -1   8

> w=[false true; true false; false true]  
w = 
      0  1
      1  0
      0  1

I want to index into X's rows using the columns of w, so the first column of w should retrieve row 2 of X, and the second column of w should retrieve rows 1 and 3 of X, and the result would be a single row matrix [5 5], and another matrix [0 1; -1 8], each of which I can then take mean() on to get [5 5], and [-0.5 4.5] (ideally forming a matrix [5 5; -0.5 4.5]).

How can I do this without loops? My gut feeling is it's possible by converting X into a 3D matrix, but not sure how.

2

There are 2 answers

1
AudioBubble On

Use find, which returns the indices of nonzero entries of a given vector. For example,

X(find(w(:,1)), :)

returns [5 5]

Similarly, X(find(w(:, 2)), :) returns

   0   1
  -1   8

You can then proceed with the mean, etc, and eventually stack the resulting matrices vertically like C = [A; B].

0
jadhachem On

You can use the following one-liner:

bsxfun(@rdivide, w'*X, sum(w)')

Result:

ans =

   5.00000   5.00000
  -0.50000   4.50000

bsxfun is your friend!

Explanation

w'*X generates a matrix whose rows are each a sum of the specified rows of X:

> w'*X
ans =

   5   5
  -1   9

However, you want the average, not the sum. For that, we should divide each row by the number of elements that were summed together. The operation sum(w)' generates a vector whose i-th element is the number of 1s in the i-th column of w, i.e., the number of rows of X added together in the i-th row of w'*X:

> sum(w)'
ans =

   1
   2

Now we want to divide the first by the second, and we use bsxfun to broadcast the division operation (rdivide, i.e., element-by-element right division ./) so that the dimensions of w'*X and sum(w)' match.

Some useful references: