David Tan
David Tan

Reputation: 311

MATLAB: Select elements in a vector multiple times with different index list

INPUT: a logical row vector u with length n, say [1,0,1,1,0]; and a logical matrix M of size m-by-n, say [1,0,0,1,1;0 0 0 1 1].

OUTPUT: a logical matrix of size m-by-n, the first row of which is obtained by applying the first row of matrix M as "selector", that is, [1,0,0,1,0]; and the second row is, similarly, [0 0 0 1 0].

The row vector is 20000 long, and the the matrix is 30-by-20000. This will repeat 1000 times, and I want something costs less than 1 second.

I've tried repmat, bsxfun, and element-wise multiplication, no luck. Guess there is a simple way to "choose" these elements all at once, since they are all logical values.

Upvotes: 4

Views: 1012

Answers (3)

Glen O
Glen O

Reputation: 733

Other solutions are overcomplicating things.

All you need to do is zero out the entries in the non-chosen columns...

M(:,~u)=0;

That's it. Ten measly characters. Chris Taylor's solution using bsxfun with @and is a little slower, other methods are worse.

octave:8> u = logical(rand(1,20000)<0.5);
octave:9> M = logical(rand(30,20000)<0.5);
octave:10> tic, for i=1:1000, N=M; N(:,~u)=0; end, toc
Elapsed time is 0.66 seconds.
octave:11> tic, for i=1:1000, N=M; N=bsxfun(@and,u,N); end, toc
Elapsed time is 0.82 seconds.
octave:12> tic, for i=1:1000, N=bsxfun(@and,u,M); end, toc
Elapsed time is 0.8 seconds.

Note that I've used "N=M" to standardise the result, because this method acts directly on the vector, but the assignment isn't adding anything significant to the time.

Upvotes: 0

Chris Taylor
Chris Taylor

Reputation: 47402

The fastest I can give you is 4 seconds at the moment (on my machine). I'm not 100% sure if you've tried this or not, but here you go anyway.

I have an m-file randbool.m with these contents, to generate the test data.

function x = randbool(m,n)
x = logical(rand(m,n) < 0.5);

Generate the data for testing:

>> u = randbool(1,20000);
>> M = randbool(30,20000);

I can think of three ways to loop over the rows of M (use bsxfun, use repmat, use a loop) and two ways to pull out the elements you want (conjunction &, or pointwise multiplication with .*). The fastest is the combination of bsxfun and conjunction:

Bsxfun / conjunction

>> tic, for i=1:1000, bsxfun(@and,u,M); end, toc
Elapsed time is 4.068684 seconds.

Bsxfun / multiplication

>> tic, for i=1:1000, bsxfun(@times,u,M); end, toc
Elapsed time is 4.856784 seconds.

Repmat / conjunction

>> tic, for i=1:1000, utmp=repmat(u,30,1); M&utmp; end, toc
Elapsed time is 7.305158 seconds.

Repmat / multiplication

>> tic, for i=1:1000, utmp=repmat(u,30,1); M.*utmp; end, toc
Elapsed time is 8.117164 seconds.

Looping / conjunction

>> tic, for i=1:1000, for j = 1:30, out(j,:)=u&M(j,:); end; end, toc
Elapsed time is 7.110872 seconds.

Looping / multiplication

>> tic, for i=1:1000, for j = 1:30, out(j,:)=u.*M(j,:); end; end, toc
Elapsed time is 8.322888 seconds.

Upvotes: 1

PearsonArtPhoto
PearsonArtPhoto

Reputation: 39728

This looks like a bitwise and operation. Perhaps something like this would work:

utemp=repmat(u,1,size(m,2));
output=M&utemp;

I should add that for that large of a matrix, you might run into memory problems. Essentially you need 3 copies of the 600K element matrix, which could add up.

Upvotes: 0

Related Questions