Discussion:
Tricky indexing problem - possible to vectorize?
(too old to reply)
2***@gmail.com
2018-02-23 10:19:36 UTC
Permalink
Given a multidimensional (ND) array, e.g. 2D

x=rand(5,3)
x =

0.6035610 0.9810622 0.0839223
0.2315557 0.1302441 0.1922486
0.6666435 0.3593251 0.2620314
0.8997794 0.3196009 0.0587057
0.0431265 0.2920293 0.0053526


I want to extract 1D vector made up of a single element of each row (or column).

Basically the same as you would get from sum(x,1) or sum(x,2), however with the elements selected by an index vector.

E.g. the function "extract(x,index,dim)" should behave like this:

y = extract(x,[1 2 1],1) should return [0.6035610 0.1302441 0.0839223].

y = extract(x,[1 1 2 3 1],2) should return [0.6035610 0.2315557 0.3593251 0.0587057 0.0431265]'.

It's not hard to do the 2D case in a loop but I really want to vectorize it. Especially because this should work on high dimension arrays (up to 5D) with millions of elements.

Any suggestions welcome!
2***@gmail.com
2018-02-23 11:42:28 UTC
Permalink
Post by 2***@gmail.com
Given a multidimensional (ND) array, e.g. 2D
x=rand(5,3)
x =
0.6035610 0.9810622 0.0839223
0.2315557 0.1302441 0.1922486
0.6666435 0.3593251 0.2620314
0.8997794 0.3196009 0.0587057
0.0431265 0.2920293 0.0053526
I want to extract 1D vector made up of a single element of each row (or column).
Basically the same as you would get from sum(x,1) or sum(x,2), however with the elements selected by an index vector.
y = extract(x,[1 2 1],1) should return [0.6035610 0.1302441 0.0839223].
y = extract(x,[1 1 2 3 1],2) should return [0.6035610 0.2315557 0.3593251 0.0587057 0.0431265]'.
It's not hard to do the 2D case in a loop but I really want to vectorize it. Especially because this should work on high dimension arrays (up to 5D) with millions of elements.
Any suggestions welcome!
This is my loop version - it's about 100-1000x slower than the built-in sum, mean, median, max functions that do a similar traversal of the array dimensions.

sz = size(x);
sz(dim) = 1;
y = zeros(sz,class(x));

for k = 1:prod(sz)
[s1 s2 s3 s4] = ind2sub(sz,k);
switch dim
case 1; s1 = indx(k);
case 2; s2 = indx(k);
case 3; s3 = indx(k);
case 4; s4 = indx(k);
end
y(k) = x(s1,s2,s3,s4);
end

The main culprit is the ind2sub function which consumes about 90% of the runtime!
g***@gmail.com
2018-03-05 22:10:49 UTC
Permalink
Post by 2***@gmail.com
Given a multidimensional (ND) array, e.g. 2D
x=rand(5,3)
x =
0.6035610 0.9810622 0.0839223
0.2315557 0.1302441 0.1922486
0.6666435 0.3593251 0.2620314
0.8997794 0.3196009 0.0587057
0.0431265 0.2920293 0.0053526
I want to extract 1D vector made up of a single element of each row (or column).
Basically the same as you would get from sum(x,1) or sum(x,2), however with the elements selected by an index vector.
y = extract(x,[1 2 1],1) should return [0.6035610 0.1302441 0.0839223].
y = extract(x,[1 1 2 3 1],2) should return [0.6035610 0.2315557 0.3593251 0.0587057 0.0431265]'.
It's not hard to do the 2D case in a loop but I really want to vectorize it. Especially because this should work on high dimension arrays (up to 5D) with millions of elements.
Any suggestions welcome!
Just calculate where the values are. Matlab counts top to bottom, left to right, so first make a vector of values from zero to number-of-columns minus one:

(1:size(x,2))-1

Multiply that by the number of rows in the matrix:

size(x,1)*((1:size(x,2))-1)

And then add that to each index value you provide:

y = x([1, 2, 1] + size(x,1)*((1:size(x,2))-1))

It's awkward to do it the other way, on account of how Matlab encodes stuff, so it's easier to transpose your matrix first:

x = x.';

Then repeat as above for your new case:

y = x([1, 1, 2, 3, 1] + size(x,1)*((1:size(x,2))-1))

Whatever you're doing, if you're indexing this way, you're probably doing it wrong.

You would have to give an example of how you're expecting this to work for a higher-dimensional case.
dpb
2018-03-06 14:37:08 UTC
Permalink
Post by 2***@gmail.com
Given a multidimensional (ND) array, e.g. 2D
x=rand(5,3)
x =
0.6035610 0.9810622 0.0839223
0.2315557 0.1302441 0.1922486
0.6666435 0.3593251 0.2620314
0.8997794 0.3196009 0.0587057
0.0431265 0.2920293 0.0053526
I want to extract 1D vector made up of a single element of each row (or column).
Basically the same as you would get from sum(x,1) or sum(x,2), however with the elements selected by an index vector.
y = extract(x,[1 2 1],1) should return [0.6035610 0.1302441 0.0839223].
...
Post by 2***@gmail.com
ix=[1 2 1];
y=x(sub2ind(size(x),ix,1:size(x,2)))
y1 =

0.6036 0.1302 0.0839
Post by 2***@gmail.com
iz=[1 1 2 3 1];
y2=x(sub2ind(size(x),1:size(x,1),iz))
y2 =

0.6036 0.2316 0.3593 0.0587 0.0431
The problem is you're looking at storage order incorrectly as other
responder noted; ML is row-major.

SUB2IND() needs input rows, then columns for it's input order so a
generalized function will need to prepare those in the correct sequence
and the alternate dimension vector is 1:length(alternateDimension).

For higher dimensions this gets more complex but same idea holds.

I didn't have time to think about it as far as implementation but more
than likely PERMUTE() could be used to order the input array
consistently to keep the addressing vectors in the same sequence. For
2D this is the same thing as the other responder's suggestion to transpose.

--

Loading...