8 views (last 30 days)

Show older comments

Hello

How to select first n nonzero elements from each row?

If I have matrix A, and I want result like B (three nonzero elements from each row):

Rusult also can be cell..

A=[1 3 0 2 5

0 2 0 1 0

2 0 0 0 0

3 1 1 0 0];

B=[1 3 2

2 1 0

2 0 0

3 1 1];

Stephen Cobeldick
on 24 Jun 2020

Edited: Stephen Cobeldick
on 24 Jun 2020

Linear indexing does this simply and efficiently. The trick is to work down the columns, which requires transposing:

>> A = [1,3,0,2,5;0,2,0,1,0;2,0,0,0,0;3,1,1,0,0]

A =

1 3 0 2 5

0 2 0 1 0

2 0 0 0 0

3 1 1 0 0

>> N = 3;

>> Z = A.';

>> S = size(Z);

>> [~,R] = sort(Z==0,1);

>> [~,C] = ndgrid(1:N,1:S(2));

>> X = sub2ind(S,R(1:N,:),C);

>> B = Z(X).'

B =

1 3 2

2 1 0

2 0 0

3 1 1

Probably the most efficient approach would be to use a simple loop, e.g. (not particularly optimized):

R = size(A,1);

B = zeros(R,N);

for k = 1:R

tmp = nonzeros(A(k,:));

idx = 1:min(N,numel(tmp));

B(k,idx) = tmp(idx);

end

Some timings (1e3 iterations):

Elapsed time is 5.358 seconds. % madhan ravi's with loop and CELLFUN

Elapsed time is 0.606 seconds. % my answer with SUB2IND

Elapsed time is 0.265 seconds. % my answer with loop and indexing

madhan ravi
on 23 Jun 2020

Edited: madhan ravi
on 23 Jun 2020

ix = cumprod(A ~= 0, 2);

B = A(:, max(ix) ~= 0)

%OR

ix = cumprod(A ~= 0, 2); % remove cumprod(...) if you don't expect n consecutive nonzero elements

n = 3;

idx = find(cumsum(ix,2) == n, 1);

[~, c] = ind2sub(size(A), idx);

B = A(:, 1:c)

% OR

% if you don't want to specify n by yourself

ix = cumprod(A ~= 0, 2); % remove cumprod(...) if you don't expect n consecutive nonzero elements

ix1 = cumsum(ix,2);

idx = find(ix1 == max(max(ix1)), 1); % use max(..., [], 'all') for later versions

[~, c] = ind2sub(size(A), idx);

B = A(:, 1:c)

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!