Efficient method to product subvectors of a matrix - matlab

Suppose that we have a matrix
A = [1 2 ; 3 4 ; 5 6];
and a logical matrix
B = [false true ; true true ; true false ];
I would like to obtain the row product of elements in A such that the corresponding element in B is true. In the example above, the answer is
C = [2 ; 3*4, 5] = [2 ; 12 ; 5];
One method would be to: 1) Take the power of A with respect to B; 2) Take the row product of the power matrix:
C = prod(A.^B,2);
The above command seems to perform unnecessary computation. Is there a faster way of computing C above?

Your method seems quite fast to me. If you really have a bottleneck there, you can maybe try with cheaper operators, like addition and multiplication:
C = prod(A.*B + ~B, 2);
I only tested it with octave, but it's about twice as fast.
Another less compact way, also fast in octave:
C=A; C(~B)=1; C=prod(C,2);

Here's another way, using accumarray. I doubt it's faster:
[ii, ~] = find(B); % create grouping variable
C = accumarray(ii, A(B), [], #prod); % compute product of each group

Related

Is there a version of bsxfun that works on structure arrays?

Why can I do this:
a = [1 2];
b = [3 4];
bsxfun(#(ai,bj) ai + bj, a, b')
% 4 5
% 5 6
But not this:
a = struct('x', {1 2});
b = struct('x', {3 4});
bsxfun(#(ai,bj) ai.x + bj.x, a, b');
% Error using bsxfun
% Operands must be numeric arrays.
And does a replacement function exist that works in both cases?
This may not be a general solution* but for your particular example it is easy to convert your structure array to a numerical array inside bsxfun, by using comma-separated-list generator syntax, and then using your original anonymous function, i.e.
>> bsxfun(#(ai, bj) ai+bj, [a.x], [b.x]')
ans =
4 5
5 6
and this should still leverage the computational efficiency conferred by bsxfun (as opposed to the much slower "repmat+arrayfun" approach, for instance).
*e.g. it might not work as intended if your field contains an array instead of a scalar, since the expansion to a comma-separated-list will be different
A hacky workaround:
function res = bsxfun_struct(f, A, B)
% best way to ensure our output size matches the behaviour of bsxfun is just to call it
dummy = bsxfun(#(ai, bj) ai+bj, zeros(size(A)), zeros(size(B)));
res_size = size(dummy);
% repeat the matrices as needed
Arep = repmat(A, res_size ./ size(A));
Brep = repmat(B, res_size ./ size(B));
% now we can just apply the function pairwise
res = arrayfun(f, Arep, Brep);
end

Compare an array to several numbers at once

How do you compare an array of numbers to several given numbers? More precisely, I have an array given like so
inputArray = [1 2 2 3 4 6]
and I want to compare inputArray to the numbers 1:7 to ultimately count how many times a "1" is in inputArray, a "2", a "3" and so on.
Obviously I can do something like
res = zeros(7,1);
for i = 1:7
res(i) = sum(inputArray == i);
end
or more generally when I also might be interested in the locations of occurrences
res = zeros(7,length(inputArray));
for i = 1:7
res(i,:) = inputArray == i;
end
res2 = sum(res,1);
Out of curiosity and/or speed improvements I am wondering if this is possible without a for loop in a single statement?
It seems like you are looking for a histogram count, see here:
x = [1 3 10 1 8]
b = [1 2 3]
histc(x,b)
Will produce
[2 0 1]
Yet another possibility: use accumarray:
count = accumarray(inputArray(:), 1, [7 1]); %// Change "7" as needed
When you want more dimensions of vectorization than is built in to the functions you're working with, or want to collapse a simple loop into a function call, you can use bsxfun ("Binary Singleton eXpansion FUNction"). It's pretty general, reasonably fast, and produces concise code.
In this case, you could use it to construct that equality grid, and then sum them up.
a = [1 2 2 3 4 5];
i = [1:7]'; % Flip it so it's oriented perpendicular to a
res = bsxfun(#eq, a, i);
counts = sum(res,2)'; %'
% One-liner version
counts = sum(bsxfun(#eq, a, [1:7]'), 2)';
Though in the particular case you're working with, since you're doing simple arithmetic operations on primitive arrays, the for loops might actually be fastest with JIT optimizations, as long as you're careful to isolate the work in its own function so the JIT can do "in-place" optimizations.

Mapping ids of two vectors

I have two vectors with the same elements but their order is not same. For eg
A
10
9
8
B
8
9
10
I want to find the mapping between the two
B2A
3
2
1
How can I do this in matlab efficiently?
I think the Matlab sort is efficient. So:
[~,I]=sort(A); %sort A; we want the indices, not the values
[~,J]=sort(B); %same with B
%I(1) and J(1) both point to the smallest value, and a similar statement is true
%for other pairs, even with repeated values.
%Now, find the index vector that sorts I
[~,K]=sort(I);
%if K(1) is k, then A(k) is the kth smallest entry in A, and the kth smallest
%entry in B is J(k)
%so B2A(1)=J(k)=J(K(1)), where BSA is the desired permutation vector
% A similar statement holds for the other entries
%so finally
B2A=J(K);
if the above were in script "findB2A" the following should be a check for it
N=1e4;
M=100;
A=floor(M*rand(1,N));
[~,I]=sort(rand(1,N));
B=A(I);
findB2A;
all(A==B(B2A))
There are a couple of ways of doing this. The most efficient in terms of lines of code is probably using ismember(). The return values are [Lia,Locb] = ismember(A,B), where Locb are the indices in B which correspond to the elements of A. You can do [~, B2A] = ismember(A, B) to get the result you want. If your version of MATLAB does not allow ~, supply a throwaway argument for the first output.
You must ensure that there is a 1-to-1 mapping to get meaningful results, otherwise the index will always point to the first matching element.
Here a solution :
arrayfun(#(x)find(x == B), A)
I tried with bigger arrays :
A = [ 7 5 2 9 1];
B = [ 1 9 7 5 2];
It gives the following result :
ans =
3 4 5 2 1
Edit
Because arrayfun is usually slower than the equivalent loop, here a solution with a loop:
T = length(A);
B2A = zeros(1, length(A));
for tt = 1:T
B2A(1, tt) = find(A(tt) == B);
end
I would go for Joe Serrano's answer using three chained sort's.
Another approach is to test all combinations for equality with bsxfun:
[~, B2A] = max(bsxfun(#eq, B(:), A(:).'));
This gives B2A such that B(B2A) equals A. If you want it the other way around (not clear from your example), simply reverse A and B within bsxfun.

Does matrix contain a vector?

I'm looking for a fast / concise way to check whether some matrix contains given vector, e.g.:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
someFunction(bigMatrix, [1 1 1]) % = true
someFunction(bigMatrix, [3 3 3]) % = false
Is there such function/operator, or I need a loop?
I would suggest the following solution:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
Vec = [2 2 2];
Index = ismember(bigMatrix, Vec, 'rows');
The result?
Index =
0
1
0
0
ismember is an incredibly useful function that checks whether the elements of one set are in another set. Here, I exploit the rows option to force the function to compare rows, rather than individual elements.
UPDATE: On the other hand, it is always worth doing a few speed tests! I just compared the ismember approach to the following alternative method:
N = size(bigMatrix, 1);
Index2 = zeros(N, 1);
for n = 1:N
if all(bigMatrix(n, :) == Vec)
Index2(n) = 1;
end
end
My findings? The size of bigMatrix matters! In particular, if bigMatrix is on the small side (somewhat of a misnomer), then the loop is much faster. The first approach is preferable only when bigMatrix becomes big. Further, the results are also dependent on how many columns bigMatrix has, as well as rows! I suggest you test both approaches for your application and then go with whichever is faster. (EDIT: This was on R2011a)
General Note: I am continually surprised by how much faster Matlab's loops have gotten in the last few years. Methinks vectorized code is no longer the holy grail that it once was.

How to delete zero components in a vector in Matlab?

I have a vector for example
a = [0 1 0 3]
I want to turn a into b which equals b = [1 3].
How do I perform this in general? So I have a vector with some zero components and I want to remove the zeroes and leave just the non-zero numbers?
If you just wish to remove the zeros, leaving the non-zeros behind in a, then the very best solution is
a(a==0) = [];
This deletes the zero elements, using a logical indexing approach in MATLAB. When the index to a vector is a boolean vector of the same length as the vector, then MATLAB can use that boolean result to index it with. So this is equivalent to
a(find(a==0)) = [];
And, when you set some array elements to [] in MATLAB, the convention is to delete them.
If you want to put the zeros into a new result b, while leaving a unchanged, the best way is probably
b = a(a ~= 0);
Again, logical indexing is used here. You could have used the equivalent version (in terms of the result) of
b = a(find(a ~= 0));
but mlint will end up flagging the line as one where the purely logical index was more efficient, and thus more appropriate.
As always, beware EXACT tests for zero or for any number, if you would have accepted elements of a that were within some epsilonic tolerance of zero. Do those tests like this
b = a(abs(a) >= tol);
This retains only those elements of a that are at least as large as your tolerance.
I just came across this problem and wanted to find something about the performance, but I couldn't, so I wrote a benchmarking script on my own:
% Config:
rows = 1e6;
runs = 50;
% Start:
orig = round(rand(rows, 1));
t1 = 0;
for i = 1:runs
A = orig;
tic
A(A == 0) = [];
t1 = t1 + toc;
end
t1 = t1 / runs;
t2 = 0;
for i = 1:runs
A = orig;
tic
A = A(A ~= 0);
t2 = t2 + toc;
end
t2 = t2 / runs;
t1
t2
t1 / t2
So you see, the solution using A = A(A ~= 0) is the quicker of the two :)
I often ended up doing things like this. Therefore I tried to write a simple function that 'snips' out the unwanted elements in an easy way. This turns matlab logic a bit upside down, but looks good:
b = snip(a,'0')
you can find the function file at:
http://www.mathworks.co.uk/matlabcentral/fileexchange/41941-snip-m-snip-elements-out-of-vectorsmatrices
It also works with all other 'x', nan or whatever elements.
b = a(find(a~=0))
Data
a=[0 3 0 0 7 10 3 0 1 0 7 7 1 7 4]
Do
aa=nonzeros(a)'
Result
aa=[3 7 10 3 1 7 7 1 7 4]
Why not just, a=a(~~a) or a(~a)=[]. It's equivalent to the other approaches but certainly less key strokes.
You could use sparse(a), which would return
(1,2) 1
(1,4) 3
This allows you to keep the information about where your non-zero entries used to be.