MATLAB Answers

1

How to replace arbitrary groups of matrix entries in a vectorized way?

Asked by Edward Schrom on 15 May 2019
Latest activity Commented on by Edward Schrom on 16 May 2019
I have two matrices of the same dimensions, and I would like to replace certain entries in the first matrix with the corresponding entries from the second matrix. In practice, both matrices have entries with a range of values, but for simplicity let's say:
mat1 = zeros(10,20); % This matrix should have some of its values replaced.
mat2 = ones(10,20); % This matrix provides the replacement values.
But, which entries I want to replace are specified in an odd way. A logical vector tells me which rows I want to do replacement in. A numeric vector gives a column index where replacement should start in each row (if replacement is to be performed in that row). A numeric scalar provides the column index where replacement should stop - this number is common to all rows in which replacement is to be performed. In each row where replacement is to be performed, every entry between the starting point (unique to each row) and ending point (common across all rows) should be replaced.
rows = boolean([0; 0; 1; 0; 0; 1; 0; 1; 0; 1]); % Logical vector specifying which rows to do replacement in.
first = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]; % Numeric vector giving column indices for where replacement should begin in each row.
last = 17; % Scalar giving the column index for where replacement should stop.
For example, for the code I've provided, I hope to end up with a matrix that looks like this:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0
Is there any way to perform this replacement in a vectorized way, to maximize speed? (My actual code involves matrices that are much larger!)
The closest I have gotten so far is this:
mat1(rows, first(rows):last) = mat2(rows, first(rows):last);
However, this is incorrect. While it does perform replacement in only the correct rows, and up to the correct last column index, it incorrectly applies the same first column index to each row (in this case, 3). So it's incorrect result is:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0

  0 Comments

Sign in to comment.

1 Answer

Answer by Sean de Wolski
on 15 May 2019
Edited by Sean de Wolski
on 15 May 2019
 Accepted Answer

<edited>
A simple for-loop will likely be the fastest and most comprehensible way to do this. For-loops are plenty fast in MATLAB. I wrote this seven years ago. In that time, the new language execution engine in R2015b came out and loops got even faster...
Of course since vectorizing is fun, here's as optimal as I can think of from a vectorized standpoint:
colidx = 1:size(mat1, 2);
ix = rows & (first <= colidx & colidx <= last);
mat1(ix) = mat2(ix)
Whether this is faster than a for-loop will depend highly on the density of replacement. This is doing the comparison for all rows and columns. If the number of rows and columns being compared is small, then a simple for-loop over the rows will be faster, if most rows need a replacement, then the vectorized approach may be faster.
rowix = find(rows);
for ii = 1:numel(rowix)
rowii = rowix(ii);
colix = first(rowii):last;
mat1(rowii, colix) = mat2(rowii, colix);
end
I'd encourage you to time both of these repeatedly with your large matrices.
(ps. Great question: fun, inputs, expected outputs, attempts - everything we look for!)

  3 Comments

In practice, my two matrices will be thousands x thousands, rather than just 10 x 20. And then I'll need to repeat the process thousands of times. That's why I've pursued a vectorized solution. But are for-loops still just as fast in this case? Maybe I've heard some unfair myths about their slow performance...
See edits to my original answer.
As a followup, I did time both of these approaches repeatedly. The most common matrix size I'll need to work with is 2301 x 1681. To my surprise, the for-loop approach consistently performed 30% faster than the vectorized version. Thanks so much for the thorough and thoughtful answer!

Sign in to comment.