简体   繁体   中英

Matlab: vectorize assignment of values in matrix based on index

Apologies in advance if this question is a duplicate, or if the solution to this question is very straightforward in Matlab. I have a M x N matrix A , a 1 x M vector ind , and another vector val . For example,

A = zeros(6,5);
ind = [3 4 2 4 2 3];
val = [1 2 3];

I would like to vectorize the following code:

for i = 1 : size(A,1)
    A(i, ind(i)-1 : ind(i)+1) = val;
end

>> A

A =

 0     1     2     3     0
 0     0     1     2     3
 1     2     3     0     0
 0     0     1     2     3
 1     2     3     0     0
 0     1     2     3     0

That is, for row i of A , I want to insert the vector val in a certain location, as specificied by the i'th entry of ind . What's the best way to do this in Matlab without a for loop?

It can be done using bsxfun 's masking capability : build a mask telling where the values will be placed, and then fill those values in. In doing this, it's easier to work with columns instead of rows (because of Matlab's column major order), and transpose at the end.

The code below determines the minimum number of columns in the final A so that all values fit at the specified positions.

Your example applies a displacement of -1 with respect to ind . The code includes a generic displacement , which can be modified.

%// Data
ind = [3 4 2 4 2 3]; %// indices
val = [1 2 3]; %// values
d = -1; %// displacement for indices. -1 in your example

%// Let's go
n = numel(val);
m = numel(ind);
N = max(ind-1) + n + d;  %// number of rows in A (rows before transposition)
mask = bsxfun(@ge, (1:N).', ind+d) & bsxfun(@le, (1:N).', ind+n-1+d); %// build mask
A = zeros(size(mask)); %/// define A with zeros
A(mask) = repmat(val(:), m, 1); %// fill in values as indicated by mask
A = A.'; %// transpose

Result in your example:

A =
     0     1     2     3     0
     0     0     1     2     3
     1     2     3     0     0
     0     0     1     2     3
     1     2     3     0     0
     0     1     2     3     0

Result with d = 0 (no displacement):

A =
     0     0     1     2     3     0
     0     0     0     1     2     3
     0     1     2     3     0     0
     0     0     0     1     2     3
     0     1     2     3     0     0
     0     0     1     2     3     0

If you can handle a bit of bsxfun overdose , here's one with bsxfun's adding capability -

N = numel(ind);
A(bsxfun(@plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)

Sample run -

>> ind
ind =
     3     4     2     4     2     3
>> val
val =
     1     2     3
>> A = zeros(6,5);
>> N = numel(ind);
>> A(bsxfun(@plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)
A =
     0     1     2     3     0
     0     0     1     2     3
     1     2     3     0     0
     0     0     1     2     3
     1     2     3     0     0
     0     1     2     3     0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM