Subtract an array from another array based on elements in both columns - matlab

I am looking for an efficient way to do this:
(a=1 2; 1 3; 2 3) - b=(1 2; 2 3) = (c=1 3)
or:
a=nchoosek([1 2 3 4 5 6],2) - b=(1 2; 1 3; 2 4;3 5;3 6) = (c=1 4;1 5;1 6;2 3;2 5;2 6;3 4;4 5;4 6; 5 6)
a and b will be given and I want to obtain c; all data is in double. There will always be two columns.

I am looking for an efficient way to do this:
(a=1 2; 1 3; 2 3) - b=(1 2; 2 3) = (c=1 3)
your notation is a bit inconsistent, but it looks you want to understand a and b as a set of tuples of two numbers. That's fine, do just exactly that.
In Python, this would be super easy; I'm only using Python to illustrate the concept¹:
a = {(1,2), (1,3), (2,3)}
b = {(1,2), (2,3)}
c = a - b
In matlab, setdiff is to be used; basically, you need to have the 2-tuples as rows of a and b (so a will be a 3×2 matrix).
¹ That's not 100% true. I'm also here to tell you that if your code depends on Matlab doing a lot of set operations, then you will have a long, slow day of annoying coding, and another one of slow, annoying matlab running. It's really not something matlab is good at, neither from the development perspective, nor from the execution side. Might be worth looking at other tools than matlab!

Related

Finding the most recent indices with different values

I am familiar with Matlab but am still having trouble with vectorized methods in my intuition, so I was wondering if anyone could demonstrate how they would manage this problem.
I have an array, for example A = [1 1 2 2 1 3 3 3 4 3 4 4 5].
I want to return an array B such that each element is the index of A's most 'recent' element with a different value than the previous ones.
So for our array A, B would equal [x x 2 2 4 5 5 5 8 9 10 10 12], where the x's can be any consistent value you like, because there is no previous index satisfying those characteristics.
I know how I would code it as a for-loop, and I bet the for-loop is probably faster, but can anyone vectorize this to faster than the for-loop?
Here's my for-loop:
prev=0;
B=zeros(length(A),1);
for i=2:length(A)
if A(i-1)~=A(i)
prev=i-1;
end
B(i)=prev;
end
Find the indices of the entries where the value changes:
ind = find(diff(A) ~= 0);
The values that should appear in B are therefore:
val = [0 ind];
Construct the diff of B: fill in the difference between the values that should appear at the right places:
Bd = zeros(size(B))';
Bd(ind + 1) = diff(val);
Now use cumsum to construct B:
B = cumsum(Bd)
Not sure whether this results in a speed-up though.

Matlab matrices dimension

I am new to matlab and just wondering if you guys can help me out with this problem.
For instance, I have two matrices:
A = [X1 X2 X3 X4]
B = [Y1; Y2; Y3]
now what I really want to achieve is to multiply these two matrices in this way:
[X1Y1 X2Y1 X3Y1 X4Y1;
X1Y2 X2Y2 X3Y2 X4Y2;
X1Y3 X2Y3 X3Y3 X4Y3;
.... and so on]
I tried using A(1,:).*B(:,1) but matlab is saying that matrix dimensions must agree.
I just don't know how to manipulate this on matlab but in excel is possible.
This is a simple outer product. kron is not needed (although it will work.) bsxfun is wild overkill, although will yield what you have asked for. repmat is inappropriate, because while it will help you do what you wish, it replicates the arrays in memory, using more resources than are needed. (Avoid using inefficient programming styles when there are good ones immediately at your disposal.)
All you need use is the simple * operator.
A is a row vector. B a column vector.
C = B*A
will yield the result C(i,j)=B(i)*A(j), which is exactly what you are looking for. Note that this works because B is 3x1 and A is 1x4, so the "inner" dimensions of B and A do conform.
In MATLAB, IF you are unsure if something works, TRY IT!
A = [1 2 3 4];
B = [1;2;3];
C = B*A
ans =
1 2 3 4
2 4 6 8
3 6 9 12
See that kron did indeed work, although I'd bet that use of kron here is probably less efficient than is the simple outer product multiply.
C = kron(B,A)
C =
1 2 3 4
2 4 6 8
3 6 9 12
As well, bsxfun will work here too, although since we are using a general tool to do something that a basic operator will do, I'd bet it is slightly less efficient.
C = bsxfun(#times,B,A)
C =
1 2 3 4
2 4 6 8
3 6 9 12
The WORST choice is repmat. Again, since it artificially replicates the vectors in memory FIRST, it must go out and grab big chunks of memory in the case of large vectors.
C = repmat(B,1,4).*repmat(A,3,1)
C =
1 2 3 4
2 4 6 8
3 6 9 12
I suppose for completeness, you could also have used meshgrid or ndgrid. See that it is doing exactly what repmat did, but here it explicitly creates new matrices. Again, this is a poor programming style when there are good tools to do exactly what you wish.
[BB,AA] = ndgrid(B,A)
BB =
1 1 1 1
2 2 2 2
3 3 3 3
AA =
1 2 3 4
1 2 3 4
1 2 3 4
C = BB.*AA
C =
1 2 3 4
2 4 6 8
3 6 9 12
What you need to understand is exactly why each of these tools COULD have been used for the job, and why they are different.
In Matlab there is * and .* and they are very different.
* is normal matrix multiplication which is what you want i.e. B*A, note the B must come first as the inner dimension must match. You can multiply a column by a row but not a row by a column (unless they have the same number of elements).
.* is element by element multiplication in which case the matrices must be exactly the same size and shape so for example [1 2 3].*[4 5 6] = [1*4 2*5 3*6] = [4 10 18]
Do not do a ".*". You should rather do a "*".
The ".*" is for index by index multiplication and should have given you [X1Y1 X2Y2 X3Y3] were they vectors have been equal in size.
If you do the regular multiplication "*", this is actually matrix multiplication.
I think you just need to transpose one of the vectors. You are multiplying a column vector (A(1,:)) with a row vector (B(:,1)). This should work:
C = A(1,:).*B(:,1)';

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.

splitting a Matrix into column vectors and storing it in an array

My question has two parts:
Split a given matrix into its columns
These columns should be stored into an array
eg,
A = [1 3 5
3 5 7
4 5 7
6 8 9]
Now, I know the solution to the first part:
the columns are obtained via
tempCol = A(:,iter), where iter = 1:end
Regarding the second part of the problem, I would like to have (something like this, maybe a different indexing into arraySplit array), but one full column of A should be stored at a single index in splitArray:
arraySplit(1) = A(:,1)
arraySplit(2) = A(:,2)
and so on...
for the example matrix A,
arraySplit(1) should give me [ 1 3 4 6 ]'
arraySplit(2) should give me [ 3 5 5 8 ]'
I am getting the following error, when i try to assign the column vector to my array.
In an assignment A(I) = B, the number of elements in B and I must be the same.
I am doing the allocation and access of arraySplit wrongly, please help me out ...
Really it sounds like A is alread what you want--I can't imagine a scenario where you gain anything by splitting them up. But if you do, then your best bet is likely a cell array, ie.
C = cell(1,3);
for i=1:3
C{i} = A(:,i);
end
Edit: See #EitanT's comment below for a more elegant way to do this. Also accessing the vector uses the same syntax as setting it, e.g. v = C{2}; will put the second column of A into v.
In a Matlab array, each element must have the same type. In most cases, that is a float type. An your example A(:, 1) is a 4 by 1 array. If you assign it to, say, B(:, 2) then B(:, 1) must also be a 4 by 1 array.
One common error that may be biting you is that a 4 by 1 array and a 1 by 4 array are not the same thing. One is a column vector and one is a row vector. Try transposing A(:, 1) to get a 1 by 4 row array.
You could try something like the following:
A = [1 3 5;
3 5 7;
4 5 7;
6 8 9]
arraySplit = zeros(4,1,3);
for i =1:3
arraySplit(:,:,i) = A(:,i);
end
and then call arraySplit(:,:,1) to get the first vector, but that seems to be an unnecessary step, since you can readily do that by accessing the exact same values as A(:,1).

What is the quickest way to keep the non dominated elements and omit the rest in MATLAB?

For example [2 , 5] dominates [3 , 8] cause (2 < 3) and (5 < 8)
but [2 , 5] does not dominates [3 , 1] cause though (2 < 3) but (5 > 1) so these two vectors are non dominated
now for example assume that I have a matrix like this :
a =[ 1 8;
2 6;
3 5;
4 6];
here the first three are non dominated but the last one is dominated by (3,5), I need a code which can omit it and give me this output:
ans =
[ 1 8;
2 6;
3 5]
note that there may be lots of non dominated elements in a Nx2 matrix
Compare one row with other rows using bsxfun
Do this for every row using arrayfun (or a loop if you prefer that) and transform the output back to a matrix with cell2mat
use any and all to check which rows are dominated
remove these rows
code:
a=[1 8;2 6;3 5;4 6];
dominated_idxs = any(cell2mat(arrayfun(#(ii) all(bsxfun(#(x,y) x>y,a,a(ii,:)),2),1:size(a,1),'uni',false)),2);
a(dominated_idxs,:) = [];
edit
If you want to use >= instead of > comparison, each row will dominate itself and will be removed, so you'll end up with an empty matrix. Filter these false-positives out by adjusting the code as follows:
a=[1 8;2 6;3 5;4 6];
N = size(a,1);
compare_matrix = cell2mat(arrayfun(#(ii) all(bsxfun(#(x,y) x>=y,a,a(ii,:)),2),1:N,'uni',false));
compare_matrix(1:N+1:N^2)=false; % set diagonal to false
dominated_idxs = any(compare_matrix,2);
a(dominated_idxs ,:) = [];
This problem is identical to identifying the so-called Pareto front.
If the number of elements N grows large and/or you need to carry out this sort of operation often (as I suspect you do), you might want to give a thought to a fully optimized MEX file for this purpose (available on the Mathworks File Exchange):
Compiling this, putting the mex in your Matlab path, and then using something like
a = a(paretofront(a));
will accomplish your task much quicker than any combination of Matlab-builtins is able to.