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.

| improve this answer | |
$\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.

| improve this answer | |
$\endgroup$
  • $\begingroup$ I would have done newValuesPositions = Transpose[{Range[Length[newValuesList]]}] myself. $\endgroup$ – J. M.'s technical difficulties Mar 24 at 14:25
  • $\begingroup$ I would do Partition[Range[Length[newValuesList]], 1]. $\endgroup$ – Henrik Schumacher Mar 24 at 14:35
  • 1
    $\begingroup$ Beware, this applies the changes in reverse order! And it's even slower than Thread... $\endgroup$ – Henrik Schumacher Mar 24 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$ – Henrik Schumacher Mar 24 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.