tim
tim

Reputation: 10176

Matlab: Search rows in matrix with fixed first and last element with vectorization

I have a matrix like the following (arbitrary cols/rows):

1    0    0    0    0
1    2    0    0    0
1    2    3    0    0
1    2    3    4    0
1    2    3    4    5
1    2    5    0    0
1    2    5    3    0
1    2    5    3    4
1    4    0    0    0
1    4    2    0    0
1    4    2    3    0
1    4    2    5    0
1    4    2    5    3
1    4    5    0    0
1    4    5    3    0
2    0    0    0    0
2    3    0    0    0
2    3    4    0    0
2    3    4    5    0
2    5    0    0    0
2    5    3    0    0
2    5    3    4    0
3    0    0    0    0
3    4    0    0    0
3    4    2    0    0
3    4    2    5    0
3    4    5    0    0

and now I want to get all rows where the first element is a certain value X and the last element (that is the last element != 0) is a certain value Y, OR turned around: the first is Y and the last is X.

Can't see any speedful code which does NOT use a for-loop :( Thanks!

EDIT: To filter all rows with a certain first element is really easy, you don't need to help me here. So let's assume I only want to do the following: Filter all rows where the last element (i.e. the last element != 0 in each row) is either X or Y.

EDIT Thanks a lot for your posts. I benchmarked the three possible solutions with a matrix of 473408*10 elements. Here's the benchmarkscript: http://pastebin.com/9hEAWw9a

The results were:

t1 = 2.9425 Jonas
t2 = 0.0999 Brendan
t3 = 0.0951 Oli

So thanks a lot you guys, I'm sticking with Oli's solution and thus accept it. Thanks though for all the other solutions!

Upvotes: 1

Views: 1160

Answers (3)

Oli
Oli

Reputation: 16035

Here is a trick: Look for numbers with a 0 on the right, and sum them all:

H=[1 2 0 0 0;
   2 3 1 0 0;
   4 5 8 0 0;
   8 5 4 2 2];

lastNumber=sum(H.*[H(:,2:end)==0 true(size(H,1),1)],2)

ans =

     2
     1
     8
     2

The rest is easy:

firstNumber=H(:,1);

find( (firstNumber==f) & (lastNumber==l) )

Upvotes: 2

Brendan
Brendan

Reputation: 19353

This works only if the numbers in each row are x number of non-zeros followed by a series of zeros. i.e. it will not work if the following is possible 1 0 3 4 0 0, I assume that isn't possible based on the sample input you gave ...

% 'a' is your array
[nx, ny] = size(a);
inds = [0:ny:ny*(nx-1)]' + sum(a ~= 0, 2);
% Needs to transpose so that the indexing reads left-to-right
aT = a'; 
valid1 = aT(inds) == Y;
valid2 = a(:,1) == X;
valid = valid1 & valid2;
valid_rows = a(valid,:);

This is messy I know ...

Upvotes: 1

Jonas
Jonas

Reputation: 74930

All you need to do is to find the linear indices of the last non-zero element of every row. The rest is easy:

[nRows,nCols] = size(A);
[u,v] = find(A); %# find all non-zero elements in A
%# for each row, find the highest column index with accumarray
%# and convert to linear index with sub2ind
lastIdx = sub2ind([nRows,nCols],(1:nRows)',accumarray(u,v,[nRows,1],@max,NaN));

To filter rows, you can then write

goodRows = A(:,1) == X & A(lastIdx) == Y

Upvotes: 5

Related Questions