Is there any faster method for this MATLAB script? - performance

The original script is
Y = [1 2 3 3 2 1 1 2 3]';
n = length(Y);
Ym = zeros(n, n);
for i=1:n
index = find(Y==Y(i));
Ym(i, index') = 1;
end
then, the Ym is
Ym =
1 0 0 0 0 1 1 0 0
0 1 0 0 1 0 0 1 0
0 0 1 1 0 0 0 0 1
0 0 1 1 0 0 0 0 1
0 1 0 0 1 0 0 1 0
1 0 0 0 0 1 1 0 0
1 0 0 0 0 1 1 0 0
0 1 0 0 1 0 0 1 0
0 0 1 1 0 0 0 0 1

Yes! With bsxfun -
Ym = bsxfun(#eq,Y,Y.')

I find that logical indexing works faster than bsxfun on my computer. Here the sample times for different methods:
tic;
for j=1:10000
Y = [1 2 3 3 2 1 1 2 3]';
n = length(Y);
Ym = zeros(n, n);
for i=1:n
index = find(Y==Y(i));
Ym(i, index') = 1;
end
end
disp('Method 1:');
toc;
tic;
for j=1:10000
Y = [1 2 3 3 2 1 1 2 3]';
n = length(Y);
Ym = zeros(n, n);
for i=1:n
Ym(i, Y==Y(i)') = 1;
end
end
disp('Method 2:');
toc;
tic;
for j=1:10000
Y = [1 2 3 3 2 1 1 2 3]';
n = length(Y);
Ym = zeros(n, n);
a=repmat(Y,1,n);
b=repmat(Y',n,1);
Ym(a==b)=1;
end
disp('Method 3:');
toc;
tic;
for j=1:10000
Y = [1 2 3 3 2 1 1 2 3]';
Ym = bsxfun(#eq,Y,Y.');
end
disp('Method 4');
toc
OUTPUT:
Method 1:
Elapsed time is 0.111412 seconds.
Method 2:
Elapsed time is 0.069617 seconds.
Method 3:
Elapsed time is 0.246780 seconds.
Method 4
Elapsed time is 0.103120 seconds.

Related

Adjacent Elements in MATLAB with Mathematical Formulation

I have a set with elements and the possible adjacent combinations for this are:
So the total possible combinations are c=11 which can be calculated with the formula:
I can model this using a as below whose elements can be represented as a(n,c) are:
I have tried to implement this in MATLAB, but since I have hard-coded the above math my code is not extensible for cases where n > 4:
n=4;
c=((n^2)/2)+(n/2)+1;
A=zeros(n,c);
for i=1:n
A(i,i+1)=1;
end
for i=1:n-1
A(i,n+i+1)=1;
A(i+1,n+i+1)=1;
end
for i=1:n-2
A(i,n+i+4)=1;
A(i+1,n+i+4)=1;
A(i+2,n+i+4)=1;
end
for i=1:n-3
A(i,n+i+6)=1;
A(i+1,n+i+6)=1;
A(i+2,n+i+6)=1;
A(i+3,n+i+6)=1;
end
Is there a relatively low complexity method to transform this problem in MATLAB with n number of elements of set N, following my above mathematical formulation?
The easy way to go about this is to take a bit pattern with the first k bits set and shift it down n - k times, saving each shifted column vector to the result. So, starting from
1
0
0
0
Shift 1, 2, and 3 times to get
|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|0 0 0 1|
We'll use circshift to achieve this.
function A = adjcombs(n)
c = (n^2 + n)/2 + 1; % number of combinations
A = zeros(n,c); % preallocate output array
col_idx = 1; % skip the first (all-zero) column
curr_col = zeros(n,1); % column vector containing current combination
for elem_count = 1:n
curr_col(elem_count) = 1; % add another element to our combination
for shift_count = 0:(n - elem_count)
col_idx = col_idx + 1; % increment column index
% shift the current column and insert it at the proper index
A(:,col_idx) = circshift(curr_col, shift_count);
end
end
end
Calling the function with n = 4 and 6 we get:
>> A = adjcombs(4)
A =
0 1 0 0 0 1 0 0 1 0 1
0 0 1 0 0 1 1 0 1 1 1
0 0 0 1 0 0 1 1 1 1 1
0 0 0 0 1 0 0 1 0 1 1
>> A = adjcombs(6)
A =
0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1
0 0 1 0 0 0 0 1 1 0 0 0 1 1 0 0 1 1 0 1 1 1
0 0 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1 1 1
0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 0 1 1 1 1 1
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 1

Count the frequency of matrix values including 0

I have a vector
A = [ 1 1 1 2 2 3 6 8 9 9 ]
I would like to write a loop that counts the frequencies of values in my vector within a range I choose, this would include values that have 0 frequencies
For example, if I chose the range of 1:9 my results would be
3 2 1 0 0 1 0 1 2
If I picked 1:11 the result would be
3 2 1 0 0 1 0 1 2 0 0
Is this possible? Also ideally I would have to do this for giant matrices and vectors, so the fasted way to calculate this would be appreciated.
Here's an alternative suggestion to histcounts, which appears to be ~8x faster on Matlab 2015b:
A = [ 1 1 1 2 2 3 6 8 9 9 ];
maxRange = 11;
N = accumarray(A(:), 1, [maxRange,1])';
N =
3 2 1 0 0 1 0 1 2 0 0
Comparing the speed:
K>> tic; for i = 1:100000, N1 = accumarray(A(:), 1, [maxRange,1])'; end; toc;
Elapsed time is 0.537597 seconds.
K>> tic; for i = 1:100000, N2 = histcounts(A,1:maxRange+1); end; toc;
Elapsed time is 4.333394 seconds.
K>> isequal(N1, N2)
ans =
1
As per the loop request, here's a looped version, which should not be too slow since the latest engine overhaul:
A = [ 1 1 1 2 2 3 6 8 9 9 ];
maxRange = 11; %// your range
output = zeros(1,maxRange); %// initialise output
for ii = 1:maxRange
tmp = A==ii; %// temporary storage
output(ii) = sum(tmp(:)); %// find the number of occurences
end
which would result in
output =
3 2 1 0 0 1 0 1 2 0 0
Faster and not-looping would be #beaker's suggestion to use histcounts:
[N,edges] = histcounts(A,1:maxRange+1);
N =
3 2 1 0 0 1 0 1 2 0
where the +1 makes sure the last entry is included as well.
Assuming the input A to be a sorted array and the range starts from 1 and goes until some value greater than or equal to the largest element in A, here's an approach using diff and find -
%// Inputs
A = [2 4 4 4 8 9 11 11 11 12]; %// Modified for variety
maxN = 13;
idx = [0 find(diff(A)>0) numel(A)]+1;
out = zeros(1,maxN); %// OR for better performance : out(maxN) = 0;
out(A(idx(1:end-1))) = diff(idx);
Output -
out =
0 1 0 3 0 0 0 1 1 0 3 1 0
This can be done very easily with bsxfun.
Let the data be
A = [ 1 1 1 2 2 3 6 8 9 9 ]; %// data
B = 1:9; %// possible values
Then
result = sum(bsxfun(#eq, A(:), B(:).'), 1);
gives
result =
3 2 1 0 0 1 0 1 2

Convert adjacent elements of matrix in Matlab

I'm working on Brushfire algorithm and I need to make a loop which will scan through the matrix and find the adjacent zeros with ones and convert "1" to "2". Assume that I have a matrix 5 by 5:
0 0 0 0 0
0 1 1 1 1
0 0 1 1 1
0 0 1 1 1
0 0 1 1 1
Can I somehow make it:
0 0 0 0 0
0 2 2 2 2
0 0 2 1 1
0 0 2 1 1
0 0 2 1 1
Thank you
With the image processing toolbox, the algorithm would be:
A = [0 0 0 0 0
0 1 1 1 1
0 0 1 1 1
0 0 1 1 1
0 0 1 1 1];
B = A;
%# set pixels at border between 0 and 1 to 2
B(imdilate(~A,true(3)) & A>0) = 2;
You do it with 2D-convolution, using the standard function conv2. Denoting your matrix as X,
mask = [0 1 0; 1 1 1; 0 1 0]; %// or [1 1 1; 1 1 1; 1 1 1] to include diagonal adjacency
X(conv2(double(~X), mask, 'same') & X) = 2;

Count the number of rows between each instance of a value in a matrix

Assume the following matrix:
myMatrix = [
1 0 1
1 0 0
1 1 1
1 1 1
0 1 1
0 0 0
0 0 0
0 1 0
1 0 0
0 0 0
0 0 0
0 0 1
0 0 1
0 0 1
];
Given the above (and treating each column independently), I'm trying to create a matrix that will contain the number of rows since the last value of 1 has "shown up". For example, in the first column, the first four values would become 0 since there are 0 rows between each of those rows and the previous value of 1.
Row 5 would become 1, row 6 = 2, row 7 = 3, row 8 = 4. Since row 9 contains a 1, it would become 0 and the count starts again with row 10. The final matrix should look like this:
FinalMatrix = [
0 1 0
0 2 1
0 0 0
0 0 0
1 0 0
2 1 1
3 2 2
4 0 3
0 1 4
1 2 5
2 3 6
3 4 0
4 5 0
5 6 0
];
What is a good way of accomplishing something like this?
EDIT: I'm currently using the following code:
[numRow,numCol] = size(myMatrix);
oneColumn = 1:numRow;
FinalMatrix = repmat(oneColumn',1,numCol);
toSubtract = zeros(numRow,numCol);
for m=1:numCol
rowsWithOnes = find(myMatrix(:,m));
for mm=1:length(rowsWithOnes);
toSubtract(rowsWithOnes(mm):end,m) = rowsWithOnes(mm);
end
end
FinalMatrix = FinalMatrix - toSubtract;
which runs about 5 times faster than the bsxfun solution posted over many trials and data sets (which are about 1500 x 2500 in size). Can the code above be optimized?
For a single column you could do this:
col = 1; %// desired column
vals = bsxfun(#minus, 1:size(myMatrix,1), find(myMatrix(:,col)));
vals(vals<0) = inf;
result = min(vals, [], 1).';
Result for first column:
result =
0
0
0
0
1
2
3
4
0
1
2
3
4
5
find + diff + cumsum based approach -
offset_array = zeros(size(myMatrix));
for k1 = 1:size(myMatrix,2)
a = myMatrix(:,k1);
widths = diff(find(diff([1 ; a])~=0));
idx = find(diff(a)==1)+1;
offset_array(idx(idx<=numel(a)),k1) = widths(1:2:end);
end
FinalMatrix1 = cumsum(double(myMatrix==0) - offset_array);
Benchmarking
The benchmarking code for comparing the above mentioned approach against the one in the question is listed here -
clear all
myMatrix = round(rand(1500,2500)); %// create random input array
for k = 1:50000
tic(); elapsed = toc(); %// Warm up tic/toc
end
disp('------------- With FIND+DIFF+CUMSUM based approach') %//'#
tic
offset_array = zeros(size(myMatrix));
for k1 = 1:size(myMatrix,2)
a = myMatrix(:,k1);
widths = diff(find(diff([1 ; a])~=0));
idx = find(diff(a)==1)+1;
offset_array(idx(idx<=numel(a)),k1) = widths(1:2:end);
end
FinalMatrix1 = cumsum(double(myMatrix==0) - offset_array);
toc
clear FinalMatrix1 offset_array idx widths a
disp('------------- With original approach') %//'#
tic
[numRow,numCol] = size(myMatrix);
oneColumn = 1:numRow;
FinalMatrix = repmat(oneColumn',1,numCol); %//'#
toSubtract = zeros(numRow,numCol);
for m=1:numCol
rowsWithOnes = find(myMatrix(:,m));
for mm=1:length(rowsWithOnes);
toSubtract(rowsWithOnes(mm):end,m) = rowsWithOnes(mm);
end
end
FinalMatrix = FinalMatrix - toSubtract;
toc
The results I got were -
------------- With FIND+DIFF+CUMSUM based approach
Elapsed time is 0.311115 seconds.
------------- With original approach
Elapsed time is 7.587798 seconds.

I want replace the values of 1 in an adjacency matrix with weights given in another smaller matrix

How can I replace the values of 1 in an adjacency matrix with weights given in another matrix?
For example:
adjacent_matrix = [1 0 0 1; 0 0 1 1; 1 0 1 0; 0 1 1 0 ]
weight_matrix = [ 2 4 6 2; 4 5 1 3]
The final matrix should look like this: [2 0 0 4; 0 0 6 2; 4 0 5 0; 0 1 3 0]
Code -
out = adjacent_matrix';
out(out==1) = reshape(weight_matrix',1,numel(weight_matrix))';
out = out';
Inputs 'adjacent_matrix' and 'weight_matrix' stay the same, as suggested by #chappjc.
accumarray solution:
>> [ii,jj] = find(adjacent_matrix.');
>> out = accumarray([ii jj],reshape(weight_matrix.',[],1)).'
out =
2 0 0 4
0 0 6 2
4 0 5 0
0 1 3 0
sparse solution:
[ii,jj] = find(adjacent_matrix.');
out = full(sparse(ii,jj,weight_matrix.')).'

Resources