I am working with Matlab.
I have a binary square matrix. For each row, there is one or more entries of 1. I want to go through each row of this matrix and return the index of those 1s and store them in the entry of a cell.
I was wondering if there is a way to do this without looping over all the rows of this matrix, as for loop is really slow in Matlab.
For example, my matrix
M = 0 1 0
1 0 1
1 1 1
Then eventually, I want something like
A = [2]
[1,3]
[1,2,3]
So A is a cell.
Is there a way to achieve this goal without using for loop, with the aim of calculating the result more quickly?
At the bottom of this answer is some benchmarking code, since you clarified that you're interested in performance rather than arbitrarily avoiding for loops.
In fact, I think for loops are probably the most performant option here. Since the "new" (2015b) JIT engine was introduced (source) for loops are not inherently slow - in fact they are optimised internally.
You can see from the benchmark that the mat2cell option offered by ThomasIsCoding here is very slow...
If we get rid of that line to make the scale clearer, then my splitapply method is fairly slow, obchardon's accumarray option is a bit better, but the fastest (and comparable) options are either using arrayfun (as also suggested by Thomas) or a for loop. Note that arrayfun is basically a for loop in disguise for most use-cases, so this isn't a surprising tie!
I would recommend you use a for loop for increased code readability and the best performance.
Edit:
If we assume that looping is the fastest approach, we can make some optimisations around the find command.
Specifically
Make M logical. As the below plot shows, this can be faster for relatively small M, but slower with the trade-off of type conversion for large M.
Use a logical M to index an array 1:size(M,2) instead of using find. This avoids the slowest part of the loop (the find command) and outweighs the type conversion overhead, making it the quickest option.
Here is my recommendation for best performance:
function A = f_forlooplogicalindexing( M )
M = logical(M);
k = 1:size(M,2);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = k(M(r,:));
end
end
I've added this to the benchmark below, here is the comparison of loop-style approaches:
Benchmarking code:
rng(904); % Gives OP example for randi([0,1],3)
p = 2:12;
T = NaN( numel(p), 7 );
for ii = p
N = 2^ii;
M = randi([0,1],N);
fprintf( 'N = 2^%.0f = %.0f\n', log2(N), N );
f1 = #()f_arrayfun( M );
f2 = #()f_mat2cell( M );
f3 = #()f_accumarray( M );
f4 = #()f_splitapply( M );
f5 = #()f_forloop( M );
f6 = #()f_forlooplogical( M );
f7 = #()f_forlooplogicalindexing( M );
T(ii, 1) = timeit( f1 );
T(ii, 2) = timeit( f2 );
T(ii, 3) = timeit( f3 );
T(ii, 4) = timeit( f4 );
T(ii, 5) = timeit( f5 );
T(ii, 6) = timeit( f6 );
T(ii, 7) = timeit( f7 );
end
plot( (2.^p).', T(2:end,:) );
legend( {'arrayfun','mat2cell','accumarray','splitapply','for loop',...
'for loop logical', 'for loop logical + indexing'} );
grid on;
xlabel( 'N, where M = random N*N matrix of 1 or 0' );
ylabel( 'Execution time (s)' );
disp( 'Done' );
function A = f_arrayfun( M )
A = arrayfun(#(r) find(M(r,:)),1:size(M,1),'UniformOutput',false);
end
function A = f_mat2cell( M )
[i,j] = find(M.');
A = mat2cell(i,arrayfun(#(r) sum(j==r),min(j):max(j)));
end
function A = f_accumarray( M )
[val,ind] = ind2sub(size(M),find(M.'));
A = accumarray(ind,val,[],#(x) {x});
end
function A = f_splitapply( M )
[r,c] = find(M);
A = splitapply( #(x) {x}, c, r );
end
function A = f_forloop( M )
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = find(M(r,:));
end
end
function A = f_forlooplogical( M )
M = logical(M);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = find(M(r,:));
end
end
function A = f_forlooplogicalindexing( M )
M = logical(M);
k = 1:size(M,2);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = k(M(r,:));
end
end
You can try arrayfun like below, which sweep through rows of M
A = arrayfun(#(r) find(M(r,:)),1:size(M,1),'UniformOutput',false)
A =
{
[1,1] = 2
[1,2] =
1 3
[1,3] =
1 2 3
}
or (a slower approach by mat2cell)
[i,j] = find(M.');
A = mat2cell(i,arrayfun(#(r) sum(j==r),min(j):max(j)))
A =
{
[1,1] = 2
[2,1] =
1
3
[3,1] =
1
2
3
}
Using accumarray:
M = [0 1 0
1 0 1
1 1 1];
[val,ind] = find(M.');
A = accumarray(ind,val,[],#(x) {x});
You can use strfind :
A = strfind(cellstr(char(M)), char(1));
Edit: I added a benchmark, the results show that a for loop is more efficient than accumarray.
You can usefind and accumarray:
[c, r] = find(A');
C = accumarray(r, c, [], #(v) {v'});
The matrix is transposed (A') because find groups by column.
Example:
A = [1 0 0 1 0
0 1 0 0 0
0 0 1 1 0
1 0 1 0 1];
% Find nonzero rows and colums
[c, r] = find(A');
% Group row indices for each columns
C = accumarray(r, c, [], #(v) {v'});
% Display cell array contents
celldisp(C)
Output:
C{1} =
1 4
C{2} =
2
C{3} =
3 4
C{4} =
1 3 5
Benchmark:
m = 10000;
n = 10000;
A = randi([0 1], m,n);
disp('accumarray:')
tic
[c, r] = find(A');
C = accumarray(r, c, [], #(v) {v'});
toc
disp(' ')
disp('For loop:')
tic
C = cell([size(A,1) 1]);
for i = 1:size(A,1)
C{i} = find(A(i,:));
end
toc
Result:
accumarray:
Elapsed time is 2.407773 seconds.
For loop:
Elapsed time is 1.671387 seconds.
A for loop is more efficient than accumarray...
I need to compute the sum of this series
I need the output this way:
If n = 3;
x = function_name(n)
I need to get x = 11.
If n = 5;
x = function_name(n)
I need to get x = 45.
I believe I need a for-loop to iterate; but am finding it difficult to iterate the increment value itself.
I guess you want the sum of the cumsum of the differences d of the numbers:
d = 2;
n = 5;
s = d:d:d*(n-1)
cs = cumsum( [1 s] )
scs = sum(cs)
%// or as anonymous function
scsh = #(n,d) sum( cumsum( [1 d:d:d*(n-1)] ) )
scs =
45
scsh(5,2) =
45
No need for a loop!
inc=2;
sum=1;
next=1;
n=input('what is n?\n');
for i=2:n
next=next+inc;
sum=sum+next;
inc=inc+2;
end
disp('sum is ');
disp(sum);
function Total = abc(n)
nth_term=1;
Total = 1 ;
for d = 2:2:(2*(n-1))
nth_term = nth_term + d;
Total =Total + nth_term;
end
end
How can I easily create the following matrix in MATLAB?
With A (nxn), B (nxp) and N a positive integer.
Gamma = [B 0 0 ... 0 ;
A*B B 0 ... 0 ;
A^2*B A*B B ... 0 ;
... ... ... ... ... ;
A^(N-1)*B A^(N-2)*B A^(N-3)*B ... B];
How about
[g{1:N,1:N}] = deal( zeros(n,p) );
g{1,1} = B;
for ii = 2:N
g( ii, 2:end ) = g( ii-1, 1:end-1 );
g{ ii, 1 } = A * g{ ii-1, 1 };
end
Gamma = cell2mat( g );
This works, though it is less efficient than it could be.
n = size(A,1);
p = size(B,2);
N = 3;
Gamma = zeros(N*n, N*p);
for ii = 1:N
for jj = 1:N
if ii >= jj
Gamma((ii-1)*n+1:ii*n,(jj-1)*p+1:jj*p) = A^(ii-jj) * B;
end
end
end
Edit: Here's a more efficient version, that does the minimum amount of matrix multiplication.
n = size(A,1);
p = size(A,2);
N = 3;
Gamma = zeros(N*n, N*p);
# Pre-compute all the matrix multiplications we'll need.
memo = cell(1, N);
memo{1} = B;
for ii = 1:N-1
memo{ii+1} = A * memo{ii};
end
for ii = 1:N
for jj = 1:N
if ii >= jj
Gamma((ii-1)*n+1:ii*n,(jj-1)*p+1:jj*p) = memo{ii-jj+1};
end
end
end
I have an issue with a code performing some array operations. It is getting too slow, because I am using loops. I am trying for some time to optimize this code and to re-write it with less or without loops. Until now unsuccessfully. Can you please help me solve this:
YVal = 1:1:100000;
M_MAX = 1000;
N_MAX = 2000;
clear YTemp
tic
for M=1:1:M_MAX
for N = 1:1:N_MAX
YTemp(M,N) = sum(YVal (N+1:N+M) ) - sum(YVal (1:M) );
end
end
For large N_MAX and M_MAX the execution time of these two loops is very high. How can I optimize this?
Thank you!
Assuming YVal is larger than N_MAX+M_MAX
sum1 = cumsum( YVal(1:(M_MAX+N_MAX)) ); % sum1(M) = sum(YVal(1:M))
If I'm not mistaken, then
sum( YVal( N+1:N+M ) ) = sum1( N + M ) - sum1( N )
And therefore
YTemp( M, N ) = sum1( N + M ) - sum1( N ) - sum1( M )
Using ndgrid
[M N] = ndgrid( 1:M_MAX, 1:N_MAX );
YTemp = sum1( N + M ) - sum1( N ) - sum1( M );
Have I got it right?
EDIT:
Another go without ndgrid
sum1 = cumsum( YVal( 1 : (N_MAX+M_MAX) ) );
YTemp = bsxfun( #minus, ...
bsxfun( #minus, ...
sum1( bsxfun( #plus, 1:N_MAX, (1:M_MAX)' ) ) , sum1 ),...
sum1' );
You should be able to speed it up a little by hoisting the invariant term out of the inner loop, e.g.
for M=1:1:M_MAX
sum2 = sum(YVal(1:M));
for N = 1:1:N_MAX
YTemp(M,N) = sum(YVal(N+1:N+M)) - sum2;
end
end
Given these two matrices:
m1 = [ 1 1;
2 2;
3 3;
4 4;
5 5 ];
m2 = [ 4 2;
1 1;
4 4;
7 5 ];
I'm looking for a function, such as:
indices = GetIntersectionIndecies (m1,m2);
That the output of which will be
indices =
1
0
0
1
0
How can I find the intersection indices of rows between these two matrices without using a loop ?
One possible solution:
function [Index] = GetIntersectionIndicies(m1, m2)
[~, I1] = intersect(m1, m2, 'rows');
Index = zeros(size(m1, 1), 1);
Index(I1) = 1;
By the way, I love the inventive solution of #Shai, and it is much faster than my solution if your matrices are small. But if your matrices are large, then my solution will dominate. This is because if we set T = size(m1, 1), then the tmp variable in the answer of #Shai will be T*T, ie a very large matrix if T is large. Here's some code for a quick speed test:
%# Set parameters
T = 1000;
M = 10;
%# Build test matrices
m1 = randi(5, T, 2);
m2 = randi(5, T, 2);
%# My solution
tic
for m = 1:M
[~, I1] = intersect(m1, m2, 'rows');
Index = zeros(size(m1, 1), 1);
Index(I1) = 1;
end
toc
%# #Shai solution
tic
for m = 1:M
tmp = bsxfun( #eq, permute( m1, [ 1 3 2 ] ), permute( m2, [ 3 1 2 ] ) );
tmp = all( tmp, 3 ); % tmp(i,j) is true iff m1(i,:) == m2(j,:)
imdices = any( tmp, 2 );
end
toc
Set T = 10 and M = 1000, and we get:
Elapsed time is 0.404726 seconds. %# My solution
Elapsed time is 0.017669 seconds. %# #Shai solution
But set T = 1000 and M = 100 and we get:
Elapsed time is 0.068831 seconds. %# My solution
Elapsed time is 0.508370 seconds. %# #Shai solution
How about using bsxfun
function indices = GetIntersectionIndecies( m1, m2 )
tmp = bsxfun( #eq, permute( m1, [ 1 3 2 ] ), permute( m2, [ 3 1 2 ] ) );
tmp = all( tmp, 3 ); % tmp(i,j) is true iff m1(i,:) == m2(j,:)
indices = any( tmp, 2 );
end
Cheers!