Matlab for loop replacement - matlab

I am trying to reduce the amount of loops in my code to speed up computation. I have encountered a portion of code I am completing with a loop which I cannot see a solution.
I have a matrix of x y coords of various particles.
For example, generated as rand(2,5)
0.8715 0.0363 0.0657 0.6289 0.3279
0.0272 0.4380 0.9794 0.6563 0.4755
I would like a matrix in (5,5,2) with a vector between each particle.
This would be a matrix of x lengths as (:,:,1) and y lengths as (:,:,2).

You can use bsxfun for this, though you'll also need permute to "3D-transpose" the coordinate matrix. permute turns coordinates into a 5-by-1-by-2, and a 1-by-5-by-2 array, respectively:
coordinates = rand(2,5);
%# subtract all coordinate pairs from one another
vectorArray = bsxfun(#minus,permute(coordinates,[2,3,1]),permute(coordinates,[3 2 1]));
size(vectorArray)
ans =
5 5 2
Note that the vectorArray is antisymmetric, so you may want to look into pdist if you run into space problems.

Here's an indexing-based solution that uses REPMAT to create the linear index into the coordinate matrix and CAT to create the 3-D matrix result:
>> xy = [0.8715 0.0363 0.0657 0.6289 0.3279; ... %# Your coordinates
0.0272 0.4380 0.9794 0.6563 0.4755];
>> N = size(xy, 2);
>> index = repmat(1:2:2*N, N, 1); %# A matrix of linear indices into xy
>> result = cat(3, xy(index)-xy(index.'), ... %.'# X differences
xy(index+1)-xy(index.'+1)) %.'# Y differences
result(:,:,1) =
0 -0.8352 -0.8058 -0.2426 -0.5436
0.8352 0 0.0294 0.5926 0.2916
0.8058 -0.0294 0 0.5632 0.2622
0.2426 -0.5926 -0.5632 0 -0.3010
0.5436 -0.2916 -0.2622 0.3010 0
result(:,:,2) =
0 0.4108 0.9522 0.6291 0.4483
-0.4108 0 0.5414 0.2183 0.0375
-0.9522 -0.5414 0 -0.3231 -0.5039
-0.6291 -0.2183 0.3231 0 -0.1808
-0.4483 -0.0375 0.5039 0.1808 0

Related

Finding equal rows in Matlab

I have a matrix suppX in Matlab with size GxN and a matrix A with size MxN. I would like your help to construct a matrix Xresponse with size GxM with Xresponse(g,m)=1 if the row A(m,:) is equal to the row suppX(g,:) and zero otherwise.
Let me explain better with an example.
suppX=[1 2 3 4;
5 6 7 8;
9 10 11 12]; %GxN
A=[1 2 3 4;
1 2 3 4;
9 10 11 12;
1 2 3 4]; %MxN
Xresponse=[1 1 0 1;
0 0 0 0;
0 0 1 0]; %GxM
I have written a code that does what I want.
Xresponsemy=zeros(size(suppX,1), size(A,1));
for x=1:size(suppX,1)
Xresponsemy(x,:)=ismember(A, suppX(x,:), 'rows').';
end
My code uses a loop. I would like to avoid this because in my real case this piece of code is part of another big loop. Do you have suggestions without looping?
One way to do this would be to treat each matrix as vectors in N dimensional space and you can find the L2 norm (or the Euclidean distance) of each vector. After, check if the distance is 0. If it is, then you have a match. Specifically, you can create a matrix such that element (i,j) in this matrix calculates the distance between row i in one matrix to row j in the other matrix.
You can treat your problem by modifying the distance matrix that results from this problem such that 1 means the two vectors completely similar and 0 otherwise.
This post should be of interest: Efficiently compute pairwise squared Euclidean distance in Matlab.
I would specifically look at the answer by Shai Bagon that uses matrix multiplication and broadcasting. You would then modify it so that you find distances that would be equal to 0:
nA = sum(A.^2, 2); % norm of A's elements
nB = sum(suppX.^2, 2); % norm of B's elements
Xresponse = bsxfun(#plus, nB, nA.') - 2 * suppX * A.';
Xresponse = Xresponse == 0;
We get:
Xresponse =
3×4 logical array
1 1 0 1
0 0 0 0
0 0 1 0
Note on floating-point efficiency
Because you are using ismember in your implementation, it's implicit to me that you expect all values to be integer. In this case, you can very much compare directly with the zero distance without loss of accuracy. If you intend to move to floating-point, you should always compare with some small threshold instead of 0, like Xresponse = Xresponse <= 1e-10; or something to that effect. I don't believe that is needed for your scenario.
Here's an alternative to #rayryeng's answer: reduce each row of the two matrices to a unique identifier using the third output of unique with the 'rows' input flag, and then compare the identifiers with singleton expansion (broadcast) using bsxfun:
[~, ~, w] = unique([A; suppX], 'rows');
Xresponse = bsxfun(#eq, w(1:size(A,1)).', w(size(A,1)+1:end));

Putting a smaller 3D matrix into a bigger 3D matrix (3D sub2ind)

I need to put a smaller 3D matrix into a bigger 3D matrix. Explaining with an example:
Suppose I have the following 3D matrices:
%A is the big matrix
A(:,:,1)=[ 0.3545 0.8865 0.2177
0.9713 0.4547 0.1257
0.3464 0.4134 0.3089];
A(:,:,2)=[ 0.7261 0.0098 0.7710
0.7829 0.8432 0.0427
0.6938 0.9223 0.3782];
A(:,:,3) = [0.7043 0.2691 0.6237
0.7295 0.6730 0.2364
0.2243 0.4775 0.1771];
%B is the small matrix
B(:,:,1) = [0.3909 0.5013
0.0546 0.4317];
B(:,:,2) =[0.4857 0.1375
0.8944 0.3900];
B(:,:,3) =[0.7136 0.3433
0.6183 0.9360];
Now to put B in A such that: use first dimension: [1 3], second dimension [2 3] and do this for [1,2,3] pages of A. For the given matrix, putting these values will result in:
NewA(:,:,1) = [ 0.3545 0.3909 0.5013 % putting the value of %B(1,:,1)
0.9713 0.4547 0.1257
0.3464 0.0546 0.4317; % putting the value of %B(2,:,1)
NewA(:,:,2)=[ 0.7261 0.4857 0.1375 % putting the value of %B(1,:,2)
0.7829 0.8432 0.0427
0.6938 0.8944 0.3900]; % putting the value of %B(2,:,2)
NewA(:,:,3) = [0.7043 0.7136 0.3433 % putting the value of %B(1,:,3)
0.7295 0.6730 0.2364
0.2243 0.6183 0.9360]; % putting the value of %B(2,:,3)
I won't necessarily have square matrices as 3D pages and the size of A to put B in can vary as well. But the matrices will always be 3D. Above is just a small example. What I actually have is dimensions as big as A -> [500,500,5] and B as -> [350,350,4].
This is what sub2ind do for 2D matrices but I am not yet able to manipulate into use for 3D matrices.
Something like:
NewA = A;
NewA(sub2ind(size(A), [1 3], [2 3], [1 2 3])) = B;
but it gives:
Error using sub2ind (line 69)
The subscript vectors must all be of the same size.
How can I do this?
You don't need sub2ind, just assign directly:
newA(1,2:3,:)=B(1,:,:)
If you want to use sub2ind, you need to specify each of the 3 dimensions, for each of the elements you want to replace:
dim1A=[1 1 1 1 1 1]; % always first row
dim2A=[2 3 2 3 2 3]; % second and third column, for each slice
dim3A=[1 1 2 2 3 3]; % two elements from each slice
newA(sub2ind(size(A),dim1A,dim2A,dim3A))=B(1,:,:)
newA(:,:,1) =
0.3545 0.3909 0.5013
0.9713 0.4547 0.1257
0.3464 0.4134 0.3089
newA(:,:,2) =
0.7261 0.4857 0.1375
0.7829 0.8432 0.0427
0.6938 0.9223 0.3782
newA(:,:,3) =
0.7043 0.7136 0.3433
0.7295 0.6730 0.2364
0.2243 0.4775 0.1771

Find the average value between each element of the array and its immediate neighbor

Suppose I have a matrix 1a1 which is 1 x n, and I want to find the average value between each element of a and its neighbors.
What's a smart way to do this?
EX:
If
a=[0 1 2 1 0 1];
Then the "average value matrix" is:
b=[0.5 1 1.33 1 0.5];
Where the first entry of b is:
b(1) = (0+1)/2 = 0.5
b(2) = (0+1+2)/3 = 1
etc.
I would suggest doing the middle as vector ops and handling the edge conditions as scalars.
b=zeros(size(a));
b(2:end-1)=(a(1:end-2)+a(2:end-1)+a(3:end))/3;
b(1)=(a(1)+a(2))/2;
b(end)=(a(end-1)+a(end))/2;
If you get into bigger averages...
% scale and sum elements with a sliding window 3 long.
b=conv(a,[1,1,1]/3)
%
% remove the tails
b=b(2:end-1)
%
% and rescale the edge cases.
b(1)=b(1)*3/2
b(end)=b(end)*3/2
I compared the first method above(vector), the convolution method, and the hankel method suggested by RDizzl3. (Sorry Luis, I don't have the Statistics package, though I expect the nanmean method to be slower due to the amount of condition checking.) The comparison was with a 10000 length random a vector, to make the timing significant. b was initialized to a zeros matrix of the correct size before these timings were done.The hankel matrix(h) of correct size was precomputed before the these timings as well.
% hankle method
tic; b(1)=mean(a([1,2])); b(2:(n-1))=mean(a(h),2); b(2)=mean(a([n-1,n])); toc
Elapsed time is 0.001698 seconds.
% convolution method
tic; c=conv(a,[1,1,1]/3) ; b=c(2:(2+n-1)); b(1)=b(1)*3/2; b(n)=b(n)*3/2; toc;
Elapsed time is 0.000339 seconds.
% vector method
tic; b(1)=mean(a([1,2])) ; b(2:(n-1))=(a(1:(n-2))+a(2:(n-1))+a(3:n))/3;b(2)=mean(a([n-1,n])); toc
Elapsed time is 0.000914 seconds.
I repeated the above 3 more times and sorted the results,
hankel convolution vector
9.2500e-04 3.3900e-04 7.2600e-04
1.3820e-03 5.2600e-04 8.7100e-04
1.6980e-03 5.5200e-04 9.1400e-04
2.1570e-03 5.5300e-04 2.6390e-03
I am a little surprised, I didn't expect the efficiency of the convolution approach to come out till larger window sizes. But it consistently did the best here.
Note that if you are using smaller data sets these timings probably aren't appropriate. I wouldn't at all be surprised if the hankel approach works better if the interest is in large numbers of shorter length vectors.
You can use this:
a=[0 1 2 1 0 1];
n = numel(a);
h = hankel(1:(n-2),(n-2):n);
b(1) = mean(a([1 2]))
b(2:(n-1)) = mean(a(h),2);
b(n) = mean(a([n-1 n]))
This will return the vector:
b = [0.5000 1.0000 1.3333 1.0000 0.6667 0.5000]
This takes the elements from the vector a and finds the average for its neighbors, so:
b(1) = (0+1)/2 = 0.5
b(2) = (0+1+2)/3 = 1
b(3) = (1+2+1)/3 = 1.3333
b(4) = (2+1+0)/3 = 1
b(5) = (1+0+1)/3 = 0.6667
b(6) = (0+1)/2 = 0.5 % last element
a = [0 1 2 1 0 1]; %// data
n = 1; %// how many neighbours to consider on each side
a2 = [NaN(1,n) a NaN(1,n)]; %// pad with NaN's (which will be ignored by nanmean)
b = arrayfun(#(k) nanmean(a2(k-n:k+n)), n+1:n+numel(a)); %// apply a
%// sliding-window mean ignoring NaN's
Easiest way to use smooth filter
output=smooth(A,3,'moving');
where 3 is the window size (should be odd value)
check documentation for smooth function
https://www.mathworks.com/help/curvefit/smooth.html

Adjacency matrix from Euclidean distance matrix given in matlab

I wanted to know how to create adjacency matrix from euclidean distance matrix i've created before.
for example :
Edm = [0 7.7466 7.7534 0 3.7296 2.8171;
7.7466 0 0.0068 7.7466 4.0170 4.9295;
7.7534 0.0068 0 7.7534 4.0239 4.9364;
0 7.7466 7.7534 0 3.7296 2.8171;
3.7296 4.0170 4.0239 3.7296 0 0.9125;
2.8171 4.9295 4.9364 2.8171 0.9125 0 ]
Edm shows conectivity node 1-6 based on their euclidean distance between each other. Diagonal must be 0 because distance from the same node is zero.
is there a way for me to retrieve an adjaceny matrix with 2 nearest neighbor from Edm above?
I don't get Mohsen's answer to work, so here's my (more cumbersome) suggestion:
sz = size(Edm,1);
n = 2; % Number of desired smallest distances
E = Edm + diag(Inf(1,sz));
[~, mm] = sort(E);
mmi = mm(1:n,:)'; % n smallest distances (in your example, n = 2)
Edm_idx = sparse(mmi(:),repmat(1:sz,1,n),1,sz,sz);
Adj = full(Edm.*Edm_idx);
Not that there are non-diagonal values in Edm that are 0. If these are suppose to be Inf, (as in not connected), you must account for that as well.
Set the diagonal to Inf and use bsxfun to compare the elements in each column with the minimum value in that column:
E = Edm + diag(Inf(1,size(Edm,1)));
A = bsxfun(#eq, E, min(E));

Multiply by random number each iteration

I'd like to multiply a random number vector PT(n)=rand(1,n) by a matrix M(mxn) but want to have a different random vector for each column multiplication. Is it possible in Matlab?
E.g. PT=rand(1,4);
`PT*(1 0 0 0;...
0 0 0 1;...
0 1 0 0;...
0 0 0 1);
but where PT changes for each column multiplication. The only way I can think of is make PT=rand(4,4)and then take diag(PT*M) but it's very expensive if my matrix M is large.
Any thoughts?
Cheers
Suplemental
using #Nasser arrayfun code takes 3 times longer than a for loop. I see it's normal but why the big difference?
I am not sure if I understood exactly what you are asking.
But if you mean you have a matrix of vectors, and you want to multiply another matrix by each one of these vectors then one way is to use arrayfun.
For example: Here we multiply a 5 by 4 matrix with 3 vectors, each is 4 by 1.
The result is 3 vectors, each is 5 by 1
pt = rand(4,3);
M = rand(5,4);
r = arrayfun(#(i) M*pt(:,i),1:size(pt,2),'UniformOutput',false)
gives
r =
[5x1 double] [5x1 double] [5x1 double]
cell2mat(r)
ans =
0.1463 0.4386 0.4638
0.4104 0.8105 0.6455
0.9503 1.0145 1.0369
1.3011 1.4583 1.5233
0.4688 0.7405 0.7492
If I'm following you, how about
M = rand(4,4); % your matrix
PT = rand(4,4); % your random row vectors
rslt = sum(PT'.*M,2); % your desired result
I ma not sure, but according to your example, it looks like you want to do a random permutation of the columns of PT. If that's the case, you can do:
PT=PT(:,randperm(size(PT,2)));