How to sum up weights of equal rows of a matrix without using 'for loops' - matlab

I have a m x n matrix 'A' with a m x 1 vector of Weights corresponding to A. I have used 'unique' to find the unique matrix and to find IA and IC. How I can sum up the weights of equal rows in A in a faster way than using two 'for loops'? So far, I have
[Dis_Good_path,IA,IC]=unique(Good_path,'rows','stable');
for i=1:length(IA) % Summing up the weights corresponding to equal paths
Dis_R2(i)=0;
for j=1:length(IC)
if IA(i)==IC(j)
Dis_R2(i)= Dis_R2(i)+R2(j);
end
end
end

An answer with one loop less would look like this :)
[Dis_Good_path,ia,ic]=unique(Good_path,'rows','stable');
Dis_R2 = R2(ia);
rIA = setxor(ia,1:size(Good_path,1));
rIC = ic(rIA);
for i = 1:numel(rIC)
Dis_R2(rIC(i)) = Dis_R2(rIC(i)) + R2(rIA(i));
end

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

Partial sum of a vector

For a vector v (e.g. v=[1,2,3,4,5]), and two index vectors (e.g. a=[1,1,1,2,3] and b=[3,4,5,5,5], with all a(i)<b(i)), I would like to construct w=sum(v(a:b)), which gives the values
w = zeros(length(a),1);
for i = 1:length(a)
w(i)=sum(v(a(i):b(i)));
end
It is slow when length(a) is large. Can I compute w without the for loop?
Yes! The nth element of cumsum(v) is the sum of the first n elements in v, so just take that and subtract the sum of the elements that you don't want to include:
v=[1,2,3,4,5]
a=[1,1,1,2,3]
b=[3,4,5,5,5]
C=cumsum(v)
C(b)-C(a)+v(a)
%// or alternatively
C=cumsum([0 v])
C(b+1)-C(a)
The following code works, but it is of course much less readable:
% assume v is a column vector
units = 1:length(v); units = units'; %units is a column vector
units_matrix = repmat(units, [1 length(a)]);
a_matrix = repmat(a, [length(v) 1]); % assuming a is is a row vector
b_matrix = repmat(b, [length(v) 1]);
weights = (units_matrix>=a_matrix) & (units_matrix<=b_matrix);
v_matrix = repmat(v, [1 length(a)]);
w = sum(v_matrix.*weights);
Explanation:
v_matrix contains copies of v. The summation will be done along
the column, so we need to prepare the other needed information in
vectorized form. units_matrix contains the indexes in v along the
columns. The columns are identical. a_matrix and b_matrix, in
each of their column, contains the indexes that are relevant for each
partial summation. All rows are identical. weights is a logical
matrix where, for each column, the indexes contained in
units_matrix between the corresponding a and b are 1 (true),
and the rest is 0. The element-wise multiplication thus filters the
"right" values, and all the indexes outside the range (again, for
each different column) is multiplied by zero. w is then he result
of the sum function, i.e. a row vector (every column of the
"filtered" matrix is summed).

Find the product of all entries of vector x

Here is what I am trying to do:
Let x be a vector with n entries x1,x2,...xn. Write a mat-lab program which computes the vector p with entries defined by
pk = X1*X2....Xk-1*Xk+1...Xn.
for each k =1,2,...n.
pk is the product of all the entries of x except xk. (use prod command of compute the product of all the entries, then divide by xk). Take the appropriate special action if either one of more the entries of x is zero. Using vectors throughout and no 'for' loop.
I spent too much time to figure out this problem. I still could not get it. Please help!
Brute force:
n = numel(x);
X = repmat(x(:),1,n); %// put vector in column form and repeat
X(1:n+1:end) = 1; %// make diagonal 1
result = prod(X); %// product of each column
Saving computations:
ind = find(x==0);
if numel(ind)>1 %// result is all zeros
result = zeros(size(x));
elseif numel(ind)==1 %// result is all zeros except at one entry
result = zeros(size(x));
result(ind) = prod(nonzeros(x));
else %// compute product of all elements and divide by each element
result = prod(x)./x;
end

how to transfer the pairwise distance value in a distance matrix

I am pretty new to Matlab, now i want to use the matlab to do some clustering job.
if I have 3 columns values
id1 id2 distvalue1
id1 id3 distvalue2
....
id2 id4 distvalue i
.....
5000 ids in total, but some ids pairs are missing the distance value
in python I can make loops to import these distance value into a matrix form. How I can do it in matlab?
and also let the matlab knows id1,...idx are identifies and the third column is the value
Thanks!
Based on the comments, you know how to get the data into the form of an N x 3 matrix, called X, where X(:,1) is the first index, X(:,2) is the second index, and X(:,3) is the corresponding distance.
Let's assume that the indices (id1... idx) are arbitrary numeric labels.
So then we can do the following:
% First, build a list of all the unique indices
indx = unique([X(:,1); X(:,2)]);
Nindx = length(indx);
% Second, initialize an empty connection matrix, C
C = zeros(Nindx, Nindx); %or you could use NaN(Nindx, Nindx)
% Third, loop over the rows of X, and map them to points in the matrix C
for n = 1:size(X,1)
row = find(X(n,1) == indx);
col = find(X(n,2) == indx);
C(row,col) = X(n,3);
end
This is not the most efficient method (that would be to remap the indices of X to the range [1... Nindx] in a vectorized manner), but it should be fine for 5000 ids.
If you end up dealing with very large numbers of unique indices, for which only very few of the index-pairs have assigned distance values, then you may want to look at using sparse matrices -- try help sparse -- instead of pre-allocating a large zero matrix.