Matlab - indexing - matlab

I have a matrix A which is 21x1 and contains only ones and twos.
Then I have a matrix B which is 6 * 600 matrix of numbers ranging between 0 and 21.
I want to generate a matrix C which is 6 * 600 matrix containing ones and twos such that:
If B matrix has a zero, matrix C should have a zero on that place. If B matrix has number 5, then matrix C should have the element on row 5 of matrix A and so on and so forth.
Please let me know if this is not clear.

Let us generate some sample inputs:
A = randi(2,21,1);
B = randi(22,6,600)-1;
The output C will then be:
C = B*0; %// preallocation + take care of the elements that need to be 0
C(B>0) = A(B(B>0)); %// logical indexing
The explanation of the 2nd line is as follows:
RHS
B>0 - return a logical array the size of B which has the meaning of whether this specific element of B is larger-than-0 value.
B(B>0) - return the elements of B for which there are true values in B>0 (i.e. numbers that can be used to index into A).
A(...) - return the elements of A that correspond to the valid indices from B.

% Generate matrices fitting the description
A = round(rand(21,1))+1;
B = round(rand(6,600)*21);
C = zeros(6,600);
% Indexing impossible since zeroes cannot be used as index. So treat per element using linear indexing.
for ii = 1:(6*600)
if B(ii) == 0
C(ii) = 0;
else
C(ii) = A(B(ii));
end
end
Although the piece of code could be optimized further this is the most clear way of creating understanding and speed is not needed if it's only this small matrix evaluated a limited number of times.

Related

Simplify how to find unique rows of a Matlab array

I would like your help to vectorise (or, more generally, make more efficient) a Matlab code where:
Step 1: I construct a matrix C of size sgxR, where each row contains a sequence of ones and zeros, according to whether certain logical conditions are satisfied.
Step 2: I identify the indices of the unique rows of C.
I now describe the code in more details.
Step 1: Creation of the matrix C. I divide this step in 3 sub-steps.
Step 1.a: Create the 1x3 cell U_grid. For j=1,2,3, U_grid{j} is a sg x K matrix of numbers.
clear
rng default
n_U_sets=3; %This parameter will not be changed
sg=4; %sg is usually quite large, for instance 10^6
K=5; %This parameter can range between 3 and 8
Ugrid=cell(n_U_sets,1);
for j=1:n_U_sets
Ugrid{j}=randn(sg,K);
end
Step 1.b: For each g=1,...,sg
Take the 3 rows Ugrid{1}(g,:), Ugrid{2}(g,:), Ugrid{3}(g,:).
Take all possible 1x3 rows that can be formed such that the first element is from Ugrid{1}(g,:), the second element is from
Ugrid{2}(g,:), and the third element is from Ugrid{3}(g,:). There are K^3 such rows.
Create the matrix D{g} storing row-wise all possible pairs of such 1x3 rows. D{g} will have size (K^3*(K^3-1)/2)x6
This is coded as:
%Row indices of all possible pairs of rows
[y, x] = find(tril(logical(ones(K^(n_U_sets))), -1));
indices_pairs = [x, y]; %K^3*(K^3-1)/2
%Create D{g}
for g=1:sg
vectors = cellfun(#(x) {x(g,:)}, Ugrid); %1x3
T_temp = cell(1,n_U_sets);
[T_temp{:}] = ndgrid(vectors{:});
T_temp = cat(n_U_sets+1, T_temp{:});
T = reshape(T_temp,[],n_U_sets);
D{g}=[T(indices_pairs(:,1),:) T(indices_pairs(:,2),:)]; %(K^3*(K^3-1)/2) x (6)
end
Step 1.c: From D create C. Let R=(K^3*(K^3-1)/2). R is the size of any D{g}. C is a sg x R matrix constructed as follows: for g=1,...,sg and for r=1,...,R
if D{g}(r,1)>=D{g}(r,5)+D{g}(r,6) or D{g}(r,4)<=D{g}(r,2)+D{g}(r,3)
then C(g,r)=1
otherwise C(g,r)=0
This is coded as:
R=(K^(n_U_sets)*(K^(n_U_sets)-1)/2);
C=zeros(sg,R);
for g=1:sg
for r=1:R
if D{g}(r,1)>=D{g}(r,5)+D{g}(r,6) || D{g}(r,4)<=D{g}(r,2)+D{g}(r,3)
C(g,r)=1;
end
end
end
Step 2: Assign the same index to any two rows of C that are equal.
[~,~,idx] = unique(C,"rows");
Question: Steps 1.b and 1.c are the critical ones. With sg large and K between 3 and 8, they take a lot of time, due to the loop and reshape. Do you see any way to simplify them, for instance by vectorising?

Comparing matrices of different size in matlab and storing values that are close

I have two matrices A and B. A(:,1) corresponds to an x-coordinate, A(:,2) corresponds to a y-coordinate, and A(:,3) corresponds to a certain radius. All three values in a row describe the same circle. Now let's say...
A =
[1,4,3]
[8,8,7]
[3,6,3]
B =
[1,3,3]
[1, 92,3]
[4,57,8]
[5,62,1]
[3,4,6]
[9,8,7]
What I need is to be able to loop through matrix A and determine if there are any rows in matrix B that are "similar" as in the x value is within a range (-2,2) of the x value of A (Likewise with the y-coordinate and radius).If it satisfies all three of these conditions, it will be added to a new matrix with the values that were in A. So for example I would need the above data to return...
ans =
[1,4,3]
[8,8,7]
Please help and thank you in advance to anyone willing to take the time!
You can use ismembertol.
result = A(ismembertol(A,B,2,'ByRows',1,'DataScale',1),:)
Manual method
A = [1,4,3;
8,8,7;
3,6,3];
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]; % example matrices
t = 2; % desired threshold
m = any(all(abs(bsxfun(#minus, A, permute(B, [3 2 1])))<=t, 2), 3);
result = A(m,:);
The key is using permute to move the first dimension of B to the third dimension. Then bsxfun computes the element-wise differences for all pairs of rows in the original matrices. A row of A should be selected if all the absolute differences with respect to any column of B are less than the desired threshold t. The resulting variable m is a logical index which is used for selecting those rows.
Using pdist2 (Statistics and Machine Learning Toolbox)
m = any(pdist2(A, B, 'chebychev')<=t, 2);
result = A(m,:);
Ths pdist2 function with the chebychev option computes the maximum coordinate difference (Chebychev distance, or L∞ metric) between pairs of rows.
With for loop
It should work:
A = [1,4,3;
8,8,7;
3,6,3]
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]
index = 1;
for i = 1:size(A,1)
C = abs(B - A(i,:));
if any(max(C,[],2)<=2)
out(index,:) = A(i,:);
index = index + 1
end
end
For each row of A, computes the absolute difference between B and that row, then checks if there exists a row in which the maximum is less than 2.
Without for loop
ind = any(max(abs(B - permute(A,[3 2 1])),[],2)<=2);
out = A(ind(:),:);

Index a vector by a matrix of conditions to obtain multiple selections of the target?

I have a vector T of length n and m other vectors of the same length with 0 or 1 used as condition to select elements of T. The condition vectors are combined into a matrix I of size n x m.
Is there a one liner to extract a matrix M of values from Tsuch that the i-th column of M are those elements in T that are selected by the condition elements of the i-th column in I?
Example:
T = (1:10)'
I = mod(T,2) == 0
T(I)'
yields
2 4 6 8 10
However
I = mod(T,2:4) == 0
T(I)'
yields an error in the last statement. I see that the columns might select a different number of elements which results in vectors of different lengths (as in the example). However, even this example doesn't work:
I = zeros(10,2)
I(:,1) = mod(T,2)==0
I(:,2) = mod(T,2)==1
Is there any way to achieve the solution in a one liner?
The easiest way I can think of to do something like this is to take advantage of the element-wise multiplication operator .* with your matrix I. Take this as an example:
% these lines are just setup of your problem
m = 10;
n = 10;
T = [1:m]';
I = randi([0 1], m, n);
% 1 liner to create M
M = repmat(T, 1, n) .* I;
What this does is expand T to be the same size as I using repmat and then multiplies all the elements together using .*.
Here is a one linear solution
mat2cell(T(nonzeros(bsxfun(#times,I,(1:numel(T)).'))),sum(I))
First logical index should be converted to numeric index for it we multiply T by each column of I
idx = bsxfun(#times,I,(1:numel(T)).');
But that index contain zeros we should extract those values that correspond to 1s in matrix I:
idx = nonzeros(idx);
Then we extract repeated elements of T :
T2 = T(idx);
so we need to split T2 to 3 parts size of each part is equal to sum of elements of corresponding column of I and mat2cell is very helpful
result = mat2cell(T2,sum(I));
result
ans =
{
[1,1] =
2
4
6
8
10
[2,1] =
3
6
9
[3,1] =
4
8
}
One line solution using cellfun and mat2cell
nColumns = size(I,2); nRows = size(T,1); % Take the liberty of a line to write cleaner code
cellfun(#(i)T(i),mat2cell(I,nRows,ones(nColumns,1)),'uni',0)
What is going on:
#(i)T(i) % defines a function handle that takes a logical index and returns elements from T for those indexes
mat2cell(I,nRows,ones(nColumns,1)) % Split I such that every column is a cell
'uni',0 % Tell cellfun that the function returns non uniform output

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!

Maximum of a subset of array (MATLAB)

Suppose in MATLAB I have a real matrix A which is n x m and a binary matrix B of the same size. The latter matrix defines the optimization set (all indices for which the element of B equals one): over this set I would like to find the maximal element of A. How can I do this?
The first idea I had is that I consider C = A.*B and look for the maximal element of C. This works fine for all matrices A which have at least one positive element, however it does not work for matrices with all negative elements.
You can do
C = A(B==1);
to give you an array of just the values of A corresponding to a value of 1 in B. And
max( C )
will give you the maximum value of A where B is 1
With this method you don't run into a problem when all values of A are negative as the zeros don't appear in C.
Obviously you can condense this to
desiredValue = max(A(B(:)==1));
I am using the colon operator to make sure that the result of A(B(:)==1) is a column vector - if B is all ones I am not sure if Matlab would return a vector or a nxm matrix (and I can't confirm right now).
update to get the index of the value, you can do:
f = find(B==1);
[m mi] = max(A(f));
maxIndex = f(mi);
And to get that back to the 2D elements:
[i j] = ind2sub(size(A), maxIndex);