How to vectorize this Matlab loop - matlab

I need some help to vectorize the following operation since I'm a little confused.
So, I have a m-by-2 matrix A and n-by-1 vector b. I want to create a n-by-1 vector c whose entries should be the values of the second column of A whose line is given by the line where the correspondent value of b would fall...
Not sure if I was clear enough. Anyway, the code below does compute c correctly so you can understand what is my desired output. However, I want to vectorize this function since my real n and m are in the order of many thousands.
Note that values of bare non-integer and not necessarily equal to any of those in the first column of A (these ones could be non-integers too!).
m = 5; n = 10;
A = [(0:m-1)*1.1;rand(1,m)]'
b = (m-1)*rand(n,1)
[bincounts, ind] = histc(b,A(:,1))
for i = 1:n
c(i) = A(ind(i),2);
end

All you need is:
c = A(ind,2);

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?

Extract values from a vector and sort them based on their original squence

I have a vector of numbers (temperatures), and I am using the MATLAB function mink to extract the 5 smallest numbers from the vector to form a new variable. However, the numbers extracted using mink are automatically ordered from lowest to largest (of those 5 numbers). Ideally, I would like to retain the sequence of the numbers as they are arranged in the original vector. I hope my problem is easy to understand. I appreciate any advice.
The function mink that you use was introduced in MATLAB 2017b. It has (as Andras Deak mentioned) two output arguments:
[B,I] = mink(A,k);
The second output argument are the indices, such that B == A(I).
To obtain the set B but sorted as they appear in A, simply sort the vector of indices I:
B = A(sort(I));
For example:
>> A = [5,7,3,1,9,4,6];
>> [~,I] = mink(A,3);
>> A(sort(I))
ans =
3 1 4
For older versions of MATLAB, it is possible to reproduce mink using sort:
function [B,I] = mink(A,k)
[B,I] = sort(A);
B = B(1:k);
I = I(1:k);
Note that, in the above, you don't need the B output, your ordered_mink can be written as follows
function B = ordered_mink(A,k)
[~,I] = sort(A);
B = A(sort(I(1:k)));
Note: This solution assumes A is a vector. For matrix A, see Andras' answer, which he wrote up at the same time as this one.
First you'll need the corresponding indices for the extracted values from mink using its two-output form:
[vals, inds] = mink(array);
Then you only need to order the items in val according to increasing indices in inds. There are multiple ways to do this, but they all revolve around sorting inds and using the corresponding order on vals. The simplest way is to put these vectors into a matrix and sort the rows:
sorted_rows = sortrows([inds, vals]); % sort on indices
and then just extract the corresponding column
reordered_vals = sorted_rows(:,2); % items now ordered as they appear in "array"
A less straightforward possibility for doing the sorting after the above call to mink is to take the sorting order of inds and use its inverse to reverse-sort vals:
reverse_inds = inds; % just allocation, really
reverse_inds(inds) = 1:numel(inds); % contruct reverse permutation
reordered_vals = vals(reverse_inds); % should be the same as previously

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(:),:);

Finding indices of matrices while traversing

I have a matrix:
1|2|3|4
4|5|6|7
7|8|9|10
10|11|12|13
I want to multiply the indices of this matrix with indices of another matrix of different size:
7|8|9
9|10|10
10|11|11
for these two matrices I have used the following for loops:
for x=1:4
for y=1:4
for m=1:3
for n=1:3
c=(m*x+n*y);
end
end
end
end
Is there any way to rewrite the above code without using loops? If the indices of each element can be generated in the above matrices, I think it can be done. Please help
mx = m'*x;
mx = mx(:);
ny = n'*y;
ny = ny(:);
mxe = repmat(mx, [length(ny), 1]);
nye = repmat(ny, [length(mx), 1]);
c = mxe+nye;
This will result in c containing all the values that get put in during that loop you have there (note that in your loop, value gets assigned and overwritten).

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);