4

I know that in MATLAB, in the 1D case, you can select elements with indexing such as a([1 5 3]), to return the 1st, 5th, and 3rd elements of a. I have a 2D array, and would like to select out individual elements according to a set of tuples I have. So I may want to get a(1,3), a(1,4), a(2,5) and so on. Currently the best I have is diag(a(tuples(:,1), tuples(:,2)), but this requires a prohibitive amount of memory for larger a and/or tuples. Do I have to convert these tuples into linear indices, or is there a cleaner way of accomplishing what I want without taking so much memory?

sas4740
  • 4,510
  • 8
  • 26
  • 23
  • 1
    you might want to look at http://stackoverflow.com/questions/10146082/indexing-of-unknown-dimensional-matrix, as it covers the same problem you have – Gunther Struyf Oct 23 '12 at 06:46
  • Or you could look [here](http://stackoverflow.com/questions/12294232/changing-multiple-elements-of-known-coordinates-of-a-matrix-without-a-for-loop/12294606#12294606) to see essentially the same answers. – angainor Oct 23 '12 at 08:26
  • Thanks for pointing them out; I searched and only found cases of logical indexing. – sas4740 Oct 23 '12 at 12:18

3 Answers3

6

Converting to linear indices seems like a legitimate way to go:

indices = tuples(:, 1) + size(a,1)*(tuples(:,2)-1);
selection = a(indices);

Note that this is also implement in the Matlab built-in solution sub2ind, as in nate'2 answer:

a(sub2ind(size(a), tuples(:,1),tuples(:,2)))

however,

a = rand(50);
tuples = [1,1; 1,4; 2,5];

start = tic;
for ii = 1:1e4
    indices = tuples(:,1) + size(a,1)*(tuples(:,2)-1); end
time1 = toc(start);


start = tic;
for ii = 1:1e4
    sub2ind(size(a),tuples(:,1),tuples(:,2)); end
time2 = toc(start);

round(time2/time1)

which gives

ans =   
    38

so although sub2ind is easier on the eyes, it's also ~40 times slower. If you have to do this operation often, choose the method above. Otherwise, use sub2ind to improve readability.

Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
Brian L
  • 3,201
  • 1
  • 15
  • 15
  • 1
    NOTE: Matlab is 1-based, so you'll have to use `m*(tuples(:,2)-1)`. Otherwise, this solution is some ~40 times faster than `sub2ind`, so it has my preference :) – Rody Oldenhuis Oct 23 '12 at 05:51
  • @RodyOldenhuis I would also prefer this one. However, [I previously got mildly criticized](http://stackoverflow.com/questions/12294232/changing-multiple-elements-of-known-coordinates-of-a-matrix-without-a-for-loop/12294606#12294606) on SO for that, and `sub2ind` was the way to go. Interesting how people change their mind in what they like :) – angainor Oct 23 '12 at 08:24
  • @angainor: Ah well, there are as many opinions as there are people. I just wonder what the heck `sub2ind` does to make it consume so much time...You know, we should start a new toolbox, something like [lightspeed](http://research.microsoft.com/en-us/um/people/minka/software/lightspeed/), especially for things like this :) – Rody Oldenhuis Oct 23 '12 at 08:41
  • 1
    @RodyOldenhuis You know how the story goes: "... MATLAB has to take care of the general cases, and your case is special, so of course MATLAB can be slower ...". But it is much more general ;) – angainor Oct 23 '12 at 08:49
3

if x and y are vectors of the x y values of matrix a, then sub2und should solve your problem:

a(sub2ind(size(a),x,y))

For example

a=magic(3)

a =

 8     1     6
 3     5     7
 4     9     2
x = [3 1];
y = [1 2];


a(sub2ind(size(a),x,y))

ans =

 4     1
bla
  • 25,846
  • 10
  • 70
  • 101
  • Both methods are good. This one also seamlessly works for higher dimensions, +1. Although for faster execution the other is better. – angainor Oct 23 '12 at 08:20
0

you can reference the 2D matlab position with a 1D number as in:

a = [3 4 5;
     6 7 8;
     9 10 11;];
a(1) = 3;
a(2) = 6;
a(6) = 10;

So if you can get the positions in a matrix like this:

a([(col1-1)*(rowMax)+row1, (col2-1)*(rowMax)+row2, (col3-1)*(rowMax)+row3])

note: rowmax is 3 in this case

will give you a list of the elements at col1/row1 col2/row2 and col3/row3.

so if

row1 = col1 = 1
row2 = col2 = 2
row3 = col3 = 3

you will get:

[3, 7, 11]

back.

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175