5

I am interested in updating the values of the sparse matrix. I have a list of matrix element which I am interested in updating and a list of new values. Currently, I do it in the following manner:

s = SparseArray[{{i_, i_} -> i}, {100, 100}];
newP = RandomInteger[{1, 100}, {5, 2}];
newV = RandomReal[1, {5}];
MapThread[(s[[#1[[1]], #1[[2]]]] = #2) &, {newP, newV}]

Any suggestions on how to do it more efficiently?

kglr
  • 394,356
  • 18
  • 477
  • 896
Kiril Danilchenko
  • 2,019
  • 1
  • 9
  • 18

2 Answers2

7
s = SparseArray[{{i_, i_} -> i}, {10000, 10000}];

newP = RandomInteger[{1, 10000}, {4000, 2}];
newV = RandomReal[1, {4000}];

s1 = s;
MapThread[(s1[[#1[[1]], #1[[2]]]] = #2) &, {newP, newV}] // 
  RepeatedTiming // First

1.93

(s2 = With[{nzp = Join[newP, s["NonzeroPositions"]],
      nzv = Join[newV, s["NonzeroValues"]]}, 
     SparseArray[nzp -> nzv, Dimensions[s]]]) // 
  RepeatedTiming  // First

0.0019

(s4 = With[{mask = SparseArray[newP -> 1, Dimensions[s]], 
      ns = SparseArray[newP -> newV, Dimensions[s]]}, (1 - mask) s + 
      mask ns]) // RepeatedTiming // First

0.0012

s1 == s2 == s4

True

Versus Mr.Wizard's method:

RepeatedTiming[
    s3 = s +  SparseArray[newP -> newV - Extract[s, newP], Dimensions[s]];
  ] // First

0.0016

kglr
  • 394,356
  • 18
  • 477
  • 896
  • Interesting; s4 is significantly slower than the others on my system, timing 0.00270. A variation of that was the first thing I tried. – Mr.Wizard Aug 01 '18 at 11:17
  • @Mr.Wizard, please note that i had changed 5000 to 4000 to avoid memory limits of free cloud plan. The timings i posted are obtained in Wolfram Cloud (v11.3). – kglr Aug 01 '18 at 11:35
  • @Mr.Wizard, in version 9 s4 is slower than both s2 and s3. – kglr Aug 01 '18 at 11:42
2

Compared to kglr's answer this is marginally faster on my system (version 10.1), and a little simpler. It may be acceptable in many cases, however it will not work if you are trying to update Real to Integer values for example, because by way of numeric operations the Integers will be cast to Real.

s + SparseArray[newP -> newV - Extract[s, newP], Dimensions[s]]

Timings:

(* example code from kglr's answer *)

s = SparseArray[{{i_, i_} -> i}, {10000, 10000}];
newP = RandomInteger[{1, 10000}, {5000, 2}];
newV = RandomReal[1, {5000}];

(* his method again for comparative timing *)

(s2 = 
    Module[{nzp = Join[newP, s["NonzeroPositions"]], 
      nzv = Join[newV, s["NonzeroValues"]]}, 
     SparseArray[nzp -> nzv, Dimensions[s]]]) // RepeatedTiming // First

(* my method *)

RepeatedTiming[
  s3 = s + SparseArray[newP -> newV - Extract[s, newP], Dimensions[s]];
] // First

(* confirm equivalence *)

s2 == s3
0.00162

0.00145

True

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371