2

Given a matrix A, how do I get the elements (and their indices) larger than x in a specific range?

e.g.

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

A =

     1     2     3     4     5
     2     3     4     5     6
     3     4     5     6     7
     4     5     6     7     8
     5     6     7     8     9

And for instance I want all elements larger than 5 and appear in the range A(2:4,3:5). I should get:

elements:

6 , 6 , 7 , 6 , 7 , 8

indices:

14, 18, 19, 22, 23, 24

A(A>5) would give me all entries which are larger than 5.

A(2:4,3:5) would give all elements in the range 2:4,3:5.

I want some combination of the two. Is it possible or the only way is to put the needed range in another array B and only then perform B(B>5)? Obviously 2 problems here: I'd lose the original indices, and it will be slower. I'm doing this on a large number of matrices.

Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
Alaa M.
  • 4,961
  • 10
  • 54
  • 95

3 Answers3

3

Code. I'm trying to avoid matrix multiplication, so this may look a bit odd:

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

[r,c] = meshgrid(2:4,3:5);
n     = sub2ind(size(A), r(:), c(:));

indices = sort(n(A(n) > 5)); %'skip sorting if not needed'
values  = A(indices);

Explanation. The code converts the Cartesian product of the subscripts to linear indices in the A matrix. Then it selects the indices that respect the condition, then it selects the values.

However, it is slow.

Optimization. Following LuisMendo's suggestion, the code may be sped up by replacing the sub2ind-based linear index calculation with a handcrafted linear index calculation:

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

%'For column-first, 1-based-index array memory   '
%'layout, as in MATLAB/FORTRAN, the linear index '
%'formula is:                                    '
%'L = R + (C-1)*NR                               '
n = bsxfun(@plus, (2:4), (transpose(3:5) - 1)*size(A,1)); 

indices = n(A(n) > 5);
values  = A(indices);
Community
  • 1
  • 1
  • Wow! So fast! 2.5 times faster than using a temporary matrix! – Alaa M. Dec 31 '14 at 13:32
  • And sorting is not needed. – Alaa M. Dec 31 '14 at 13:33
  • 2
    Possibly faster: `n = bsxfun(@plus, (2:4), ((3:5).'-1)*size(A,1));` instead of `meshgrid` and `sub2ind` – Luis Mendo Dec 31 '14 at 13:37
  • @LuisMendo - Yes! faster by 2 seconds – Alaa M. Dec 31 '14 at 13:43
  • 1
    @LuisMendo It's a good idea indeed to replace the two expensive calls of `meshgrid` and `subs2ind` with handcrafted linear index calculation. –  Dec 31 '14 at 13:46
  • 1
    @AlaaM. [Here's](http://stackoverflow.com/a/27341488/3293881) a benchmark comparison on `meshgrid/ndgrid + sub2ind` vs `bsxfun` for a related problem and few insights into the possible reasons on better performance with `bsxfun` . – Divakar Dec 31 '14 at 13:50
2

If you only need the values (not the indices), it can be done using the third output of find and matrix multiplication. I don't know if it will be faster than using a temporary array, though:

[~, ~, values] = find((A(2:4,3:5)>5).*A(2:4,3:5));

Assuming you need the linear indices and the values, then if the threshold is positive you could define a mask. This may be a good idea if the mask can be defined once and reused for all matrices (that is, if the desired range is the same for all matrices):

mask = false(size(A));
mask(2:4,3:5) = true;
indices = find(A.*mask>5);
values = A(indices);
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
0

its a little clunky, but:

R = 2:4;
C = 3:5;
I = reshape(find(A),size(A))

indicies = nonzeros(I(R,C).*(A(R,C)>5))
values = A(indicies)
mdamkani
  • 305
  • 2
  • 12