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

1 Ansicht (letzte 30 Tage)
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

Akzeptierte Antwort

Sean de Wolski
Sean de Wolski am 15 Mai 2019
Bearbeitet: Sean de Wolski am 15 Mai 2019
<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 Kommentare
Sean de Wolski
Sean de Wolski am 15 Mai 2019
Bearbeitet: Sean de Wolski am 15 Mai 2019
See edits to my original answer.
Edward Schrom
Edward Schrom am 16 Mai 2019
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!

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by