How to find the index of the first element which is greater than a particular value in a matrix for all the columns.

119 views (last 30 days)
DARSHAN KUMAR BISWAS on 10 Jun 2022
Commented: Image Analyst on 11 Jun 2022
Suppose I have a 300 by 300 matrix, containg the values from 0 to 200. For each column, I want to find the position of the first element which is greater than 50. how do I do it. I have tried to write a code but can't do it any further.
no_row=size(c,1);
no_col=size(c,2);
for i=1:no_col
for j=1:no_row
if c(j,i)>=50
end
end
end

Stephen23 on 10 Jun 2022
Edited: Stephen23 on 10 Jun 2022
N = 5;
M = randi(9,5,7)
M = 5×7
9 3 7 2 8 1 2 1 7 6 4 4 2 9 7 1 6 1 7 6 2 7 3 5 1 1 6 2 6 8 2 9 1 1 7
Method one: CUMSUM and FIND:
X = M>N;
X = X & cumsum(X,1)<2;
[R,~] = find(X)
R = 7×1
1 2 1 5 1 3 2
Method two: FIND and ACCUMARRAY:
[Rx,Cx] = find(M>N);
R = accumarray(Cx,Rx,[],@min)
R = 7×1
1 2 1 5 1 3 2
Method three**: NUM2CELL and CELLFUN (fails if there is no such element in any column):
F = @(v)find(v>N,1,'first');
R = cellfun(F,num2cell(M,1))
R = 1×7
1 2 1 5 1 3 2
Jan on 11 Jun 2022
@Stephen23: Of course the methods shown above produce the same result. The dull loop/loop method is 305 times faster than the cellfun approach and does not fail on missing values. I am impressed by this speed, although I actually use Matlab to avoid such low level programming.
I assume, it is much more difficult for a beginner to write a working accumarray appraoch than to implement the nested loop appraoch correctly. So speed and valid code are arguments for the low level method. Unfortunately.

Image Analyst on 10 Jun 2022
Try this. It's a loop but only 300 iterations for it's fast, completing in 0.001 seconds:
% Prepare sample image because user forgot to upload the data.
grayImage = 200 * rand(300);
subplot(1, 2, 1);
imshow(grayImage, []);
[rows, columns] = size(grayImage);
% Now scan across columns finding the first row where the value exceeds 200.
tic
topRows = nan(1, columns);
for col = 1 : columns
t = find(grayImage(:, col) > 50, 1, "first");
if ~isempty(t)
topRows(col) = t;
end
end
toc % Takes 0.001 seconds for me
% Plot the location of the top rows.
subplot(1, 2, 2);
plot(topRows, 'r.', 'MarkerSize', 15);
grid on;
xlabel('Column');
ylabel('First row where value > 200')
Jan on 11 Jun 2022
Thanks, @Image Analyst, I've found the source of the different speeds: The online interpreter in the forum seems to let the JIT work more powerful in functions than in scripts:
s1 = 300;
s2 = 300;
M = randi([0, 200], s1, s2);
N = 50;
tic
for rep = 1:1e4
R = nan(s2, 1);
for k = 1:s2
v = find(M(:, k) > N, 1);
if ~isempty(v)
R(k) = v;
end
end
end
toc % Code in the main script:
Elapsed time is 3.724234 seconds.
tic;
R = fcn(M, s1, s2, N);
toc % Identical code inside a function:
Elapsed time is 1.392424 seconds.
function R = fcn(M, s1, s2, N)
for rep = 1:1e4
R = nan(s2, 1);
for k = 1:s2
v = find(M(:, k) > N, 1);
if ~isempty(v)
R(k) = v;
end
end
end
end
I'm surprised that this nested loop is 35 times faster (even inside a script):
R = nan(s2, 1);
for k = 1:s2
for kk = 1:s1
if M(kk, k) > N
R(k) = kk;
break;
end
end
end

Mathan on 10 Jun 2022
Edited: Mathan on 10 Jun 2022
[no_row,no_col] = size(c);
Index_final = [];
for ii = 1:no_col
Index =[];
for jj=1:no_row
if c(jj,ii)>=50
Index = [Index jj]; % Index contains all the elements greater than 50 in that particular column
end
end
if (sum(Index)>0)
Index_final = [Index_final Index(1)]; % Index_final should contain all the required elements in your case.
end
end
Mathan on 10 Jun 2022
Thanks Jan - makes sense!

Jan on 10 Jun 2022
Edited: Jan on 10 Jun 2022
s1 = 300;
s2 = 300;
C = randi([0, 200], s1, s2); % Test data
N = 50; % Search
R = nan(s2, 1); % Pre-allocate the output array
for i2 = 1:s2 % Loop over columns
for i1 = 1:s1 % Loop over rows
if C(i1, i2) >= N % Compare the element
R(i2) = i1; % Store in output
break; % Stop "for i1" loop
end
end
end
This is a loop approach equivalent to C or even Basic. Actually Matlab offers more elegant command, see Stephen23's answer. But these "vectorized" methods are slower in modern Matlab versions, because they have to create temporary arrays. The CPU is much faster than the RAM (especially if the data sizes exceed the 1st and 2nd level caches). So this ugly code is much faster than the other suggestions.
This is the case for larger arrays also: I've tested it with 30'000x30'000 matrices.

DARSHAN KUMAR BISWAS on 11 Jun 2022
A = [29 33 35 25 33 32 12;3 37 10 22 19 30 9;12 33 19 23 41 11 12;18 23 32 16 27 16 22;42 28 40 26 18 24 16;1 15 5 26 47 12 47;3 38 47 41 44 43 22; 9 10 39 40 28 10 10]
k=zeros(1,7);
for i=1:7
p=A(:,i);
for j=1:8
if p(j)>=40
k(i)=j
plot(7-k)
break
end
end
end
Image Analyst on 11 Jun 2022
Yeah, lots of room for improvement in that code.
1. I'd split A onto separate lines for readability, breaking after the semicolons.
2. To be more general and robust, I'd get the number of rows and columns of A using size, and use those sizes in the "for" lines and in the call to zeros().
3. You need "hold on" after plot(), plus you need an (x,y) pair.
4. No need to use a for loop and break when you can simply use find
See my Answer above to see better coding practice.

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

R2022a

Community Treasure Hunt

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

Start Hunting!

Translated by