Efficient way of mapping similar inputs to similar outputs - matlab

Is there a efficient way of approaching this particular problem in matlab.
I am trying to map this matrix or possible array BeansRice (see below)
Beans={0:1,0:1,0:2,0:2,0:2,0:2,0:1,0:1,0:2,0:2}
[a b c d e f g h i j ] = ndgrid(Beans{:})
BeansRice = [a(:) b(:) c(:) d(:) e(:) f(:) g(:) h(:) i(:) j(:)]
into a matrix/array BR (see below)
BR=[abc, de, fg, hij];
where if columns a, b and c each have values 0 (ties preference), I have preference for c>b>a. If all columns a, b and c each have values 1 (ties no preference), BR(1)=1. If columns a and b have values 0 and column c has value 2, BR(1)=2. If columns a and b have values 1 and column c has value 2, BR(1)=1.
I have an if function with indexing but I was thinking if it is possible to improve it, using the rank/order of the values in the matrix to break ties. Looking for a more efficient process as this is only a sub of a large problem.

You can use logical indexing instead of if conditions. For example
BR1(a==1 & b==1 & c==1)=1
BR1(a==0 & b==0 & c==2)=2
BR1(a==1 & b==1 & c==2)=1
...
then process the other parts, BR2(d==... & e>...)=##, then concatenate to obtain what you need
BR=[BR1(:) BR2(:) ...]
etc...

Related

How to get the union of the two 3D matrix?

I have two 3D matrix A and B. The size of A and B are both 40*40*20 double.
The values in matrix A and B are either 0 or 1. The number of "1" in A are 100,
the number of "1" in B are 50. The "1" in matrix A and B may or may not be in
the same coordinates. I want to get the union of matrix A and B, called C. The values in 3D matrix C is either "1" or "0". The number of "1" in C is less than or equal to 150. My question is how to get the 3D matrix C in Matlab?
You can use the operator or, which is a logical or. So or(a,b) is equivalent to the logical operation a | b.
C = or(A,B);
C = a | b;
| and or are the same operator in MatLab, it's just two different way to call it.
I think this is the best solution as long as it's integrated into MatLab. However, you have plenty different ways to do it.
Just as an example, you can do
C = logical(a+b);
logical is an operator that convert every value into logical values. Long story short, it will replace any value different of 0 by 1.
You can approach it in 2 ways. The more efficient one is using vectors but you can also do it in classical nested for loops.
A = rand(40,40,20);
A = A > 0.01; # Get approximate 320 ones and rest zeros
B = rand(40,40,20);
B = B > 0.005; # Get approximate 160 ones and rest zeros
C = zeros(size(A));
for iter1 = 1:size(A,1)
for iter2 = 1:size(A,2)
for iter3 = 1:size(A,3)
C(iter1,iter2,iter3) = A(iter1,iter2,iter3)|B(iter1,iter2,iter3)
end
end
end
This method will be very slow. You can vectorized it to improve performance
C = A|B

Compare elements of two different size matrix and get corresponding indices

How to compare A,B matrices with different size, one-by-one elements have to be compared with each other and get the corresponding indices of either A or B in C matrix.
A={2 4 1};
B={8 7 2 5 4 6};
output : C={1 2}; or C={3 6};
here it is semi-equivalent of what I asked:
for i=1:s
for j=s+1:nGen
if(tOS(p,i)==tOS(p,j))
f1=f1+1;
ti(1,f1)=i;
end
if(tOS(p+1,i)==tOS(p+1,j))
f2=f2+1;
ti(2,f2)=i;
end
end
end
but I'm looking for shorter and optimized bult-in function in Matlab
I assume you want to compare each element of matrix A with each element matrix B (your question is not clear).
You can do this very simply with bsxfun. It's easier to compare linearized (column-vector) versions of A and B, and then reshape the result:
A = randi(5, 3,2); %// example A, size 3x2
B = randi(5, 2,4); %// example B, size 2x4
C = reshape(bsxfun(#eq, A(:), B(:).'), [size(A) size(B)]);
This gives C as a 4D-array (of size 3x2x2x4 in the example). The entry C(m,n,p,q) is 1 if A(m,n) equals B(p,q), and 0 otherwise.

element by element matrix multiplication in Matlab

So I have the following matrices:
A = [1 2 3; 4 5 6];
B = [0.5 2 3];
I'm writing a function in MATLAB that will allow me to multiply a vector and a matrix by element as long as the number of elements in the vector matches the number of columns. In A there are 3 columns:
1 2 3
4 5 6
B also has 3 elements so this should work. I'm trying to produce the following output based on A and B:
0.5 4 9
2 10 18
My code is below. Does anyone know what I'm doing wrong?
function C = lab11(mat, vec)
C = zeros(2,3);
[a, b] = size(mat);
[c, d] = size(vec);
for i = 1:a
for k = 1:b
for j = 1
C(i,k) = C(i,k) + A(i,j) * B(j,k);
end
end
end
end
MATLAB already has functionality to do this in the bsxfun function. bsxfun will take two matrices and duplicate singleton dimensions until the matrices are the same size, then perform a binary operation on the two matrices. So, for your example, you would simply do the following:
C = bsxfun(#times,mat,vec);
Referencing MrAzzaman, bsxfun is the way to go with this. However, judging from your function name, this looks like it's homework, and so let's stick with what you have originally. As such, you need to only write two for loops. You would use the second for loop to index into both the vector and the columns of the matrix at the same time. The outer most for loop would access the rows of the matrix. In addition, you are referencing A and B, which are variables that don't exist in your code. You are also initializing the output matrix C to be 2 x 3 always. You want this to be the same size as mat. I also removed your checking of the length of the vector because you weren't doing anything with the result.
As such:
function C = lab11(mat, vec)
[a, b] = size(mat);
C = zeros(a,b);
for i = 1:a
for k = 1:b
C(i,k) = mat(i,k) * vec(k);
end
end
end
Take special note at what I did. The outer-most for loop accesses the rows of mat, while the inner-most loop accesses the columns of mat as well as the elements of vec. Bear in mind that the number of columns of mat need to be the same as the number of elements in vec. You should probably check for this in your code.
If you don't like using the bsxfun approach, one alternative is to take the vector vec and make a matrix out of this that is the same size as mat by stacking the vector vec on top of itself for as many times as we have rows in mat. After this, you can do element-by-element multiplication. You can do this stacking by using repmat which repeats a vector or matrices a given number of times in any dimension(s) you want. As such, your function would be simplified to:
function C = lab11(mat, vec)
rows = size(mat, 1);
vec_mat = repmat(vec, rows, 1);
C = mat .* vec_mat;
end
However, I would personally go with the bsxfun route. bsxfun basically does what the repmat paradigm does under the hood. Internally, it ensures that both of your inputs have the same size. If it doesn't, it replicates the smaller array / matrix until it is the same size as the larger array / matrix, then applies an element-by-element operation to the corresponding elements in both variables. bsxfun stands for Binary Singleton EXpansion FUNction, which is a fancy way of saying exactly what I just talked about.
Therefore, your function is further simplified to:
function C = lab11(mat, vec)
C = bsxfun(#times, mat, vec);
end
Good luck!

Creating a matrix from a function handle (MATLAB)

What I intend to do is very simple but yet I haven't found a proper way to do it. I have a function handle which depends on two variables, for example:
f = #(i,j) i+j
(mine is quite more complicated, though)
What I'd like to do is to create a matrix M such that
M(i,j) = f(i,j)
Of course I could use a nested loop but I'm trying to avoid those. I've already managed to do this in Maple in a quite simple way:
f:=(i,j)->i+j;
M:=Matrix(N,f);
(Where N is the dimension of the matrix) But I need to use MATLAB for this. For now I'm sticking to the nested loops but I'd really appreciate your help!
Use bsxfun:
>> [ii jj] = ndgrid(1:4 ,1:5); %// change i and j limits as needed
>> M = bsxfun(f, ii, jj)
M =
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
If your function f satisfies the following condition:
C = fun(A,B) accepts arrays A and B of arbitrary, but equal size and returns output of the same size. Each element in the output array C is the result of an operation on the corresponding elements of A and B only. fun must also support scalar expansion, such that if A or B is a scalar, C is the result of applying the scalar to every element in the other input array.
you can dispose of ndgrid. Just add a transpose (.') to the first (i) vector:
>> M = bsxfun(f, (1:4).', 1:5)
Function handles can accept matrices as inputs. Simply pass a square matrix of size N where the values corresponds to the row number for i, and a square matrix of size N where the values correspond to the column number for j.
N = 5;
f = #(i,j) i+j;
M = f(meshgrid(1:N+1), meshgrid(1:N+1)')

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.