Find column number for every row in matrix

17 views (last 30 days)
Hello,
I'm trying to extract the column number of the first positive value in a matrix for every row, without using any loops.
For instance
-1 4 1
1 -1 -1
-5 4 -1
I want the output column vector to be:
2
1
2
I've attempted using find(), but I cannot restrict it to when there are multiple positive values in the same row.

Accepted Answer

Stephen23
Stephen23 on 15 Mar 2023
Edited: Stephen23 on 15 Mar 2023
M = [-1,4,1;1,-1,-1;-5,4,-1]
M = 3×3
-1 4 1 1 -1 -1 -5 4 -1
Method one: logical array, CUMSUM, and FIND:
X = M>=0;
Y = X&cumsum(X,2)==1;
[C,~] = find(Y.')
C = 3×1
2 1 2
Method two: NUM2CELL, ARRAYFUN, and FIND:
C = cellfun(@(v)find(v>=0,1,'first'),num2cell(M,2))
C = 3×1
2 1 2
Method three: CUMPROD and SUM:
C = 1+sum(cumprod(M<0,2),2)
C = 3×1
2 1 2
Method four: CUMPROD, ONES, and MTIMES:
C = 1+cumprod(M<0,2)*ones(size(M,2),1)
C = 3×1
2 1 2
Method five: CUMSUM and MAX:
[~,C] = max(cumsum(M>=0,2)==1,[],2)
C = 3×1
2 1 2
Method six: CUMMAX, SIGN, MIN, and SUM:
C = 1-sum(min(0,sign(cummax(M,2))),2)
C = 3×1
2 1 2
Method seven: FIND and ACCUMARRAY:
[R,C] = find(M>=0);
C = accumarray(R,C,[],@min)
C = 3×1
2 1 2
Method eight: is left as an exercise for the reader.

More Answers (4)

the cyclist
the cyclist on 15 Mar 2023
Here is one way:
M = [-1 4 1;
1 -1 -1;
-5 4 -1];
[r,c] = find(M>0);
[~,j] = unique(r);
out = c(j)
out = 3×1
2 1 2

David Hill
David Hill on 15 Mar 2023
a=[ -1 4 1
1 -1 -1
-5 4 -1];
A=(a>0)';
[b,d]=find(A);
[~,e]=unique(d);
b(e)
ans = 3×1
2 1 2

Les Beckham
Les Beckham on 15 Mar 2023
Edited: Les Beckham on 15 Mar 2023
A = [ -1 4 1
1 -1 -1
-5 4 -1 ];
[row, col] = find(A > 0)
row = 4×1
2 1 3 1
col = 4×1
1 2 2 3
[~,idx,~] = unique(row, 'first')
idx = 3×1
2 1 3
col(idx)
ans = 3×1
2 1 2

John D'Errico
John D'Errico on 15 Mar 2023
Edited: John D'Errico on 15 Mar 2023
Simple. One line.
A = randi([-5,5],[10,7])
A = 10×7
0 -2 -5 -1 -3 1 0 2 1 -5 5 5 5 -2 3 0 4 -5 -2 5 -2 -3 5 -5 1 3 5 0 5 4 -4 -3 4 -5 -1 4 1 -4 -1 -1 -3 -5 2 4 -1 0 -2 5 1 0 1 3 0 3 5 -5 3 4 -1 1 -1 1 2 -2 5 -4 -1 1 -3 -1
[~,col] = max(A > 0,[],2)
col = 10×1
6 1 1 2 1 1 1 2 1 2
Why does it work? A > 0 returns an array that is true (1) only when a number is positive.
Max returns the location of the first such element (among equal maxes) that it finds. And since the A>0 matrix is a boolean one, it is composed of only 0 and 1 elements.
Will this fail if there are no positive elements at all in a row? Well, yes. In that case, it will return 1 for those rows.
But that is true of almost any of the schemes suggested by others. If you want a scheme that will return perhaps a NaN for those rows where there were no positive elements, you could patch it by one more test. I'll regenerate a new matrix with only a few columns, but many negative elements, to insure at least one row will have all non-positive elements.
A = randi([-10,5],[10,3])
A = 10×3
-4 2 -8 -1 -3 -4 -6 5 -2 -5 -10 4 4 -1 2 -4 4 0 -2 -4 -4 -9 -9 -1 -7 3 2 3 3 -7
[~,col] = max(A > 0,[],2);
col(~any(A>0,2)) = NaN
col = 10×1
2 NaN 2 3 1 2 NaN NaN 2 1
As you can see, in this final result, rows {2,7,8} had no positive elements at all, so a NaN was generated for those rows.
Or, perhaps you don't want to see a NaN generated. What we might do here is to append an extra column, that is ALWAYS positive. This is a common trick one can employ.
[~,col] = max([A,ones(size(A,1),1)] > 0,[],2)
col = 10×1
2 4 2 3 1 2 4 4 2 1
So for a 3 column array, it returns the number 4 where there were NO positive elements.
In the end, it is important that you decide what to do when NO elements are positive in a row, as you should expect to see a bug in your code at some point.
  1 Comment
Les Beckham
Les Beckham on 15 Mar 2023
Edited: Les Beckham on 15 Mar 2023
Except that it doesn't quite work. Row 3 has no positive elements, but this approach returns a 1 for that row.

Sign in to comment.

Products


Release

R2022b

Community Treasure Hunt

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

Start Hunting!