4
$\begingroup$

I want to use lists to easily replace parts of a matrix. As an example, I want to replace the code

ReplacePart[{{1, 0}, {0, 1}}, {{1, 1} -> 13, {1, 2} -> 14}]

by something like

ReplacePart[{{1, 0}, {0, 1}}, {{1, 1}, {1, 2}} -> {13, 14}]

which doesn't work. For a general matrix m, I want to, given a list of indices of the type (x,y), li and a list of replacement values, lr, use something like

ReplacePart[m, li -> lr}]

Any idea how to do this?

$\endgroup$
7
$\begingroup$
ReplacePart[{{1, 0}, {0, 1}}, Thread[{{1, 1}, {1, 2}} -> {13, 14}]]

so

ReplacePart[m, Thread[li -> lr]]

in general.

But a CompiledFunction will probably do the job quicker because Thread[li -> lr] unpacks arrays. Let's see.

cReplacePart = 
  Compile[{{A0, _Real, 2}, {pos, _Integer, 2}, {val, _Real, 1}},
   Block[{A = A0},
    If[
      1 <= Max[pos[[All, 1]]] <= Dimensions[A0][[1]] && 
      1 <= Max[pos[[All, 2]]] <= Dimensions[A0][[2]],
     Do[
      A[[Compile`GetElement[pos, i, 1], 
        Compile`GetElement[pos, i, 2]]] = Compile`GetElement[val, i],
      {i, 1, Min[Length[pos], Length[val]]}
      ]
     ];
    A
    ],
   CompilationTarget -> "C",
   RuntimeOptions -> "Speed"
   ];

And indeed, it's 100 times as fast:

n = 1000;
m = 1000000;
A = RandomReal[{-1, 1}, {n, n}];
li = RandomInteger[{1, n}, {m, 2}];
lr = RandomReal[{-1, 1}, m];

B = ReplacePart[A, Thread[li -> lr]]; // AbsoluteTiming // First
cB = cReplacePart[A, li, lr]; // AbsoluteTiming // First
Max[Abs[B - cB]]

4.91402

0.047307

0.

One may also use SparseArray; this is only a tiny bit slower than the compiled approach:

spB = A SparseArray[li -> 0., Dimensions[A], 1.] + 
     SparseArray[Reverse@li -> Reverse@lr, Dimensions[A], 0.]; // 
  AbsoluteTiming // First
Max[Abs[B - spB]]

0.086657

0.

$\endgroup$
5
$\begingroup$

ReplacePart has a undocumented four variables form that permits this :

oldValuesList = {{1, 0}, {0, 1}}
newValuesList = {13, 14}
modifiedPositionsList = {{1, 1}, {1, 2}}
newValuesPositions = (* gives the origin of the new data *)
 List /@ Range[Length[newValuesList]] (* simply {{1},{2}} *)

ReplacePart[oldValuesList
 , newValuesList
 , modifiedPositionsList
 , newValuesPositions]   

{{13, 14}, {0, 1}}

I have retrieved this information in Michael Trott' s book "Mathematica Guidebook for Programming" page 628.

According to this It was documented until Version 5.2.

EDIT

ReplacePart is known to be often not memory/speed optimal. It may be slow and memory consuming with large data sets.

Henrik Schumacher's comments below confirm this in this particular case.

$\endgroup$
4
  • $\begingroup$ I would have done newValuesPositions = Transpose[{Range[Length[newValuesList]]}] myself. $\endgroup$
    – J. M.'s torpor
    Mar 24 '20 at 14:25
  • $\begingroup$ I would do Partition[Range[Length[newValuesList]], 1]. $\endgroup$ Mar 24 '20 at 14:35
  • 1
    $\begingroup$ Beware, this applies the changes in reverse order! And it's even slower than Thread... $\endgroup$ Mar 24 '20 at 14:37
  • $\begingroup$ It's really awfully slow, like several minutes(!) for 1000000 replacements in a 1000 by 1000 matrix (which can actually be done in 0.05 seconds). So I really do not recommend to use the for-argument version for larger data. This is so unfortunate because I really miss this functionality. One may use SparseArray, but it is not as well-suited for dense matrices. $\endgroup$ Mar 24 '20 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.