Conditionally replace neighbouring cells - matlab

Lets say I have a matrix A:
A =
0 1 0 0
0 0 0 0
0 0 0 1
0 0 0 0
And I want to create a new matrix B of the same dimension where all ones and accompanying neighbours are replaced by the following matrix:
X =
1 1 1
1 2 1
1 1 1
The 2 in matrix X should be placed 'on top' of the 1 values as to get:
B =
1 2 1 0
1 1 2 1
0 0 1 2
0 0 1 1
Values should be added up where elements overlap and matrix X should be 'cut off' in places where it extends the dimensions of matrix A/B The idea is to eventually replace X by a 2d gaussian distribution and matrix A will be large containing many more ones. So it's essential that the code is efficient and fast. This is the code i came up with:
A = [0 1 0 0;0 0 0 0;0 0 0 1;0 0 0 0]
X = [1 1 1;1 2 1;1 1 1]
B = zeros(4,4);
t=1;
indA = find(A==1);
indX = find(X==2);
all = find(X>0);
[iall jall] = ind2sub(size(X),all);
[ia ja] = ind2sub(size(A),indA)
[ix jx] = ind2sub(size(X),indX)
iv = ia-ix
jv = ja-jx
for t=1:numel(iv),
ib = iall+iv(t);
jb = jall+jv(t);
ibjb = [ib(:), jb(:)]
c1 = (ibjb(:,1)>4)|(ibjb(:,1)<1); c2 = (ibjb(:,2)>4)|(ibjb(:,1)<1);
ibjb((c1|c2),:)=[]
isel = ibjb(:,1)-iv(t)
jsel = ibjb(:,2)-jv(t)
B(ibjb(:,1), ibjb(:,2)) = B(ibjb(:,1), ibjb(:,2))+ X(isel, jsel)
t=t+1;
end
Is there a more efficient/faster way (minimizing the loops) to code this function?

What you want is a (2D) convolution. So use conv2:
B = conv2(A, X, 'same');

Related

How to permute elements of a vector by another vector to obtain a matrix of permutations

I want to obtain all the possible permutations of one vector elements by another vector elements. For example one vector is A=[0 0 0 0] and another is B=[1 1]. I want to replace the elements of A by B to obtain all the permutations in a matrix like this [1 1 0 0; 1 0 1 0; 1 0 0 1; 0 1 1 0; 0 1 0 1; 0 0 1 1]. The length of real A is big and I should be able to choose the length of B_max and to obtain all the permutations of A with B=[1], [1 1], [1 1 1],..., B_max.
Thanks a lot
Actually, since A and B are always defined, respectively, as a vector of zeros and a vector of ones, this computation is much easier than you may think. The only constraints you should respect concerns B, which shoud not be empty and it's elements cannot be greater than or equal to the number of elements in A... because after that threshold A will become a vector of ones and calculating its permutations will be just a waste of CPU cycles.
Here is the core function of the script, which undertakes the creation of the unique permutations of 0 and 1 given the target vector X:
function p = uperms(X)
n = numel(X);
k = sum(X);
c = nchoosek(1:n,k);
m = size(c,1);
p = zeros(m,n);
p(repmat((1-m:0)',1,k) + m*c) = 1;
end
And here is the full code:
clear();
clc();
% Define the main parameter: the number of elements in A...
A_len = 4;
% Compute the elements of B accordingly...
B_len = A_len - 1;
B_seq = 1:B_len;
% Compute the possible mixtures of A and B...
X = tril(ones(A_len));
X = X(B_seq,:);
% Compute the unique permutations...
p = [];
for i = B_seq
p = [p; uperms(X(i,:).')];
end
Output for A_len = 4:
p =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
1 1 0 0
1 0 1 0
1 0 0 1
0 1 1 0
0 1 0 1
0 0 1 1
1 1 1 0
1 1 0 1
1 0 1 1
0 1 1 1

Perform "outer product" of 2-D matrix and return a 3-D array in MATLAB

I would like to do an operation on a 2-D matrix which somehow looks like the outer product of a vector. I already have written some codes for this task, but it is pretty slow, so I would like to know if there is anything I can do to accelerate it.
I would like to show the code I wrote first, followed by an example to illustrate the task I wanted to do.
My code, version row-by-row
function B = outer2D(A)
B = zeros(size(A,1),size(A,2),size(A,2)); %Pre-allocate the output array
for J = 1 : size(A,1)
B(J,:,:) = transpose(A(J,:))*A(J,:); %Perform outer product on each row of A and assign to the J-th layer of B
end
end
Using the matrix A = randn(30000,20) as the input for testing, it spends 0.317 sec.
My code, version page-by-page
function B = outer2D(A)
B = zeros(size(A,1),size(A,2),size(A,2)); %Pre-allocate the output array
for J = 1 : size(A,2)
B(:,:,J) = repmat(A(:,J),1,size(A,2)).*A; %Evaluate B page-by-page
end
end
Using the matrix A = randn(30000,20) as the input for testing, it spends 0.146 sec.
Example 1
A = [3 0; 1 1; 1 0; -1 1; 0 -2]; %A is the input matrix.
B = outer2D(A);
disp(B)
Then I would expect
(:,:,1) =
9 0
1 1
1 0
1 -1
0 0
(:,:,2) =
0 0
1 1
0 0
-1 1
0 4
The first row of B, [9 0; 0 0], is the outer product of [3 0],
i.e. [3; 0]*[3 0] = [9 0; 0 0].
The second row of B, [1 1; 1 1], is the outer product of [1 1],
i.e. [1; 1]*[1 1] = [1 1; 1 1].
The third row of B, [1 0; 0 0], is the outer product of [1 0],
i.e. [1; 0]*[1 0] = [1 0; 0 0].
And the same for the remaining rows.
Example 2
A =
0 -1 -2
0 1 0
-3 0 2
0 0 0
1 0 0
B = outer2D(A)
disp(B)
Then, similar to the example 1, the expected output is
(:,:,1) =
0 0 0
0 0 0
9 0 -6
0 0 0
1 0 0
(:,:,2) =
0 1 2
0 1 0
0 0 0
0 0 0
0 0 0
(:,:,3) =
0 2 4
0 0 0
-6 0 4
0 0 0
0 0 0
Because the real input in my project is like in the size of 30000 × 2000 and this task is to be performed for many times. So the acceleration of this task is quite essential for me.
I am thinking of eliminating the for-loop in the function. May I have some opinions on this problem?
With auto expansion:
function B = outer2D(A)
B=permute(permute(A,[3 1 2]).*A',[2 3 1]);
end
Without auto expansion:
function B = outer2Dold(A)
B=permute(bsxfun(#times,permute(A,[3 1 2]),A'),[2 3 1]);
end
Outer products are not possible in the matlab language.

Create a horizontically stretched upper triangular matrix

I'd like to create a 4x12 matrix which is very similar to a upper triangle matrix, it looks like this:
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
So my question is. What is the most efficient way to create it? no loops, no cellfun. Thanks.
One vectorized approach -
nrows = 4;
ncols = 12;
row_idx = repmat(1:nrows,ncols/nrows,1)
out = bsxfun(#le,[1:nrows]',row_idx(:).')
The Matlab R2015a and later approach using the newly introduced repelem:
n = 4;
m = 3;
out = repelem(triu(ones(n)),1,m);
out =
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
It even seems faster than the bsxfun approach, though I can't believe this ;)
Benchmark
Unfortunately I couldn't consider andrew's solution as it is not complete and I didn't got it totally.
function [t] = bench()
n = 4;
m = 12;
t = zeros(3,15);
for ii = 1:15
fcns = {
#() thewaywewalk(ii*n,ii*m);
#() Divakar(ii*n,ii*m);
#() LuisMendo(ii*n,ii*m);
};
% timeit
for jj = 1:100;
t(:,ii) = t(:,ii) + cellfun(#timeit, fcns);
end
end
plot(1:15,t(1,:)); hold on;
plot(1:15,t(2,:)); hold on;
plot(1:15,t(3,:)); hold on;
xlabel('Matrix size: n = x*4, m = x*12')
ylabel('timing')
legend({'thewaywewalk','Divakar','Luis Mendo'},'location','northwest')
end
function Z = thewaywewalk(n,m)
Z = repelem(triu(ones(n)),1,m/n);
end
function Z = Divakar(n,m)
row_idx = repmat(1:n,m/n,1);
Z = bsxfun(#le,[1:n]',row_idx(:).');
end
function Z = LuisMendo(n,m)
Z = reshape(repmat(permute(triu(ones(n,n)), [1 3 2]), [1 m/n 1]), [n m]);
end
First bottomline - small matrices:
The new repelem does a very good job, but also the reshape(repmat(permute... does not disappoint. The bsxfun approach stays a little behind for some medium-sized matrices, before it becomes the leader for large matrices:
Second bottomline - big matrices:
As predicted by Divakar, bsxfun is the fastest for larger matrices, actually as expected as bsxfun is always the fastest! Interesting that the other two align perfectly, on could guess they almost work the same internally.
Create an upper triangular matrix of ones, permute second and third dimensions, repeat along second dimension, and reshape into desired shape:
m = 4;
n = 12;
result = reshape(repmat(permute(triu(ones(m,m)), [1 3 2]), [1 n/m 1]), [m n]);
depending on your matlab version
m = 4;
n = 12;
dec2bin(bitshift(num,-1*[0:n/m:n-1])) %this prints out a string
these should be logical arrays (i don't have either of these so I cant test it)
decimalToBinaryVector(bitshift(num,-1*[0:n/m:n-1]))
de2bi(bitshift(num,-1*[0:n/m:n-1]))

Permute elements within rows of matrix

I have a matrix A
A = [0 0 0 0 1; 0 0 0 0 2; 0 1 2 3 4];
and I would like to randomly permute the elements within each row. For example, matrix A2
A2 = [1 0 0 0 0; 0 0 0 2 0; 4 1 3 2 0]; % example of desired output
I can do this with a vector:
Av = [0 1 2 3 4];
Bv = Av(randperm(5));
But I am unsure how to do this a row at time for a matrix and to only permute the elements within a given row. Is this possible to do? I could construct a matrix from many permuted vectors, but I would prefer not to do it this way.
Thanks.
You can use sort on a random array of any size (which is what randperm does). After that, all you need to do is some index-trickery to properly reshuffle the array
A = [0 0 0 0 1; 0 0 0 0 2; 0 1 2 3 4];
[nRows,nCols] = size(A);
[~,idx] = sort(rand(nRows,nCols),2);
%# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols);
%# rearrange A
B = A;
B(:) = B(idx)
B =
0 0 1 0 0
0 2 0 0 0
2 1 3 4 0

How to calculate the centroid of a matrix?

I have the following 5x5 Matrix A:
1 0 0 0 0
1 1 1 0 0
1 0 1 0 1
0 0 1 1 1
0 0 0 0 1
I am trying to find the centroid in MATLAB so I can find the scatter matrix with:
Scatter = A*Centroid*A'
If you by centroid mean the "center of mass" for the matrix, you need to account for the placement each '1' has in your matrix. I have done this below by using the meshgrid function:
M =[ 1 0 0 0 0;
1 1 1 0 0;
1 0 1 0 1;
0 0 1 1 1;
0 0 0 0 1];
[rows cols] = size(M);
y = 1:rows;
x = 1:cols;
[X Y] = meshgrid(x,y);
cY = mean(Y(M==1))
cX = mean(X(M==1))
Produces cX=3 and cY=3;
For
M = [1 0 0;
0 0 0;
0 0 1];
the result is cX=2;cY=2, as expected.
The centroid is simply the mean average computed separately for each dimension.
To find the centroid of each of the rows of your matrix A, you can call the mean function:
centroid = mean(A);
The above call to mean operates on rows by default. If you want to get the centroid of the columns of A, then you need to call mean as follows:
centroid = mean(A, 2);