1

This is very closely related to this other question, but that question wanted to avoid sub2ind because of performance concerns. I am more concerned about the "unelegance" of using sub2ind.

Let's suppose I want to create another MxN matrix which is all zeros except for one entry in each column that I want to assign from the corresponding entry in a vector, and choice of row in each column is based on another vector. For example:

z = zeros(10,4);
rchoice = [3 1 8 7];
newvals = [123 456 789 10];
% ??? I would like to set z(3,1)=123, z(1,2)=456, z(8,3)=789, z(7,4)=10

I can use sub2ind to accomplish this (which I used in an answer to a closely related question):

z(sub2ind(size(z),rchoice,1:4)) = newvals

but is there another alternative? Seems like logical addressing could be used in some way but I'm stumped, because in order to set the elements of a logical matrix to 1, you're dealing with the same element positions as in the matrix you actually want to address.

Community
  • 1
  • 1
Jason S
  • 184,598
  • 164
  • 608
  • 970

2 Answers2

2

You can just add the number of rows in previous columns to rchoice to get the linear index directly.

nRows = size(z,1); %# in case you don't know this already
nCols2write = length(newvals);
z(rchoice+[0:nRows:(nRows*(nCols2write-1)]) = newvals;
Jonas
  • 74,690
  • 10
  • 137
  • 177
  • That works for 2D (and the answer I linked to, or thought I linked to, mentions this approach as a performance improvement over sub2ind), so +1, but seems tricky to get right for 3D or greater. – Jason S May 04 '11 at 21:02
  • @Jason: Although not as simple as the 2D case, it is certainly possible to calculate the indices in a straightforward manner for N-D matrices... See the answers to [this](http://stackoverflow.com/questions/5598900/how-can-i-index-the-diagonals-of-a-3-d-matrix-in-matlab/5598991#5598991) question and [this](http://stackoverflow.com/questions/5319178/how-can-i-assign-a-value-to-the-diagonals-of-a-4-d-matrix-using-linear-indexing-i/5319338#5319338) for examples on linear indexing in 3D and 4D matrices. – abcd May 04 '11 at 21:09
2

There's a much simpler way of doing it.

nCols=size(z,2);
z(rchoice,1:nCols)=diag(newvals);
abcd
  • 41,765
  • 7
  • 81
  • 98
  • does this actually create an NxN matrix, or is MATLAB smart enough to know what I mean? If I have 1000 values, does this work or does it run out of memory? – Jason S May 04 '11 at 21:00
  • `diag` will create an `NxN` matrix. I didn't bother using sparse, as this was just a conceptual example. If you have a large number of values, you can use `spdiags(newvals',0,N,N)`. This only uses `N` elements, so won't run out of memory. – abcd May 04 '11 at 21:04
  • hmm... I like the answer, and it solves the literal problem I posed, but it relies on the side-effects of being able to set other values to zero (rather than only changing other values), so I wouldn't be able to use it in other circumstances. – Jason S May 04 '11 at 21:08
  • you can still do that as `dummy=z(...); dummy(1:N+1:N^2)=newvals; z(...)=dummy;` Here I've pulled the block out, replaced its diagonals and plugged it back in. This will involve an `NxN` matrix, so if you're dealing with supersized matrices, performance might be hit. But then again, this is only because you've tied our hands by not allowing `sub2ind`. So, perhaps in such large matrices, it's good to use Jonas' solution or use `sub2ind`. – abcd May 04 '11 at 21:15