简体   繁体   中英

Find the first N non-zero elements in each row of a matrix

I have a matrix in MATLAB with zeroes and I would like to get another matrix with the first N non-zero elements in each row. Let's say for example N = 3 , and the matrix is

A = [ 0 0 2 0 6 7 9;
      3 2 4 7 0 0 6;
      0 1 0 3 4 8 6;
      1 2 0 0 0 1 3]

I'd like the result to be:

B = [2 6 7;
     3 2 4;
     1 3 4;
     1 2 1]

I have a huge matrix so I would like to do it without a loop, could you please help me? Thanks a lot!

Since MATLAB stores a matrix according to column-major order , I first transpose A , bubble up the non-zeros, and pick the first N lines, and transpose back:

N = 3;
A = [ 0 0 2 0 6 7 9;
      3 2 4 7 0 0 6;
      0 1 0 3 4 8 6;
      1 2 0 0 0 1 3];

Transpose and preallocate output B

At = A';
B = zeros(size(At));

At =
     0     3     0     1
     0     2     1     2
     2     4     0     0
     0     7     3     0
     6     0     4     0
     7     0     8     1
     9     6     6     3

Index zeros

idx = At == 0;

idx =
     1     0     1     0
     1     0     0     0
     0     0     1     1
     1     0     0     1
     0     1     0     1
     0     1     0     0
     0     0     0     0

Bubble up the non-zeros

B(~sort(idx)) = At(~idx);

B =
     2     3     1     1
     6     2     3     2
     7     4     4     1
     9     7     8     3
     0     6     6     0
     0     0     0     0
     0     0     0     0

Select first N rows and transpose back

B(1:N,:)'

You can do the bubbling in row-major order, but you would need to retrieve the row and column subscripts with find, and do some sorting and picking there. It becomes more tedious and less readable.

Using accumarray with no loops:

N = 3;
[ii,jj] = find(A); [ii,inds]=sort(ii); jj = jj(inds);
lininds = ii+size(A,1)*(jj-1);
C = accumarray(ii,lininds,[],@(x) {A(x(1:N)')}); %' cell array output
B = vertcat(C{:})
B =
     2     6     7
     3     2     4
     1     3     4
     1     2     1

Usually I don't go with a for loop solution, but this is fairly intuitive:

N = 3;
[ii,jj] = find(A);
B = zeros(size(A,1),N);
for iRow = 1:size(A,1),
    nzcols = jj(ii==iRow);
    B(iRow,:) = A(iRow,nzcols(1:N));
end

Since you are guaranteed to have more than N nonzeros per row of A , that should get the job done.

N = 3;
for ii=1:size(A,1);
    B(ii,:) = A( ii,find(A(ii,:),N) );
end

One-liner solution:

B = cell2mat(cellfun(@(c) c(1:N), arrayfun(@(k) nonzeros(A(k,:)), 1:size(A,1), 'uni', false), 'uni', false)).'

Not terribly elegant or efficient, but so much fun!

Actually , you can do it like the code blow:


N=3
 for n=1:size(A,1)
   [a b]=find(A(n,:)>0,N);
   B(n,:)=A(n,transpose(b));
end

Then I think this B matrix will be what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM