8

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?

user64494
  • 26,149
  • 4
  • 27
  • 56
sam wolfe
  • 4,663
  • 7
  • 37

4 Answers4

11
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.

Henrik Schumacher
  • 106,770
  • 7
  • 179
  • 309
5

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.

andre314
  • 18,474
  • 1
  • 36
  • 69
  • I would have done newValuesPositions = Transpose[{Range[Length[newValuesList]]}] myself. – J. M.'s missing motivation Mar 24 '20 at 14:25
  • I would do Partition[Range[Length[newValuesList]], 1]. – Henrik Schumacher Mar 24 '20 at 14:35
  • 1
    Beware, this applies the changes in reverse order! And it's even slower than Thread... – Henrik Schumacher Mar 24 '20 at 14:37
  • 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. – Henrik Schumacher Mar 24 '20 at 14:50
3
a = {{1, 0}, {0, 1}};
b = {13, 14};

Using SubsetMap:

SubsetMap[b &, a, First@Position[a, #] & /@ a[[1]]]

({{13, 14}, {0, 1}})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
2
a = {{1, 0}, {0, 1}};

b = {13, 14};

I suggest ReplaceAt (new in 13.1) or MapAt

ReplaceAt[a, _ :> b, 1]

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

MapAt[b &, 1] @ a

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

eldo
  • 67,911
  • 5
  • 60
  • 168