Accumulate matrix elements

m = Import["matrix.dat"];


Let's define 'area accumulation' for matrices as follows:

m1=Accumulate[Accumulate /@ m];


How to add a condition so that the cumulative is only for elements other than zero? (so that the zero elements remain zeros)? It would be good if the code was fast because I will do it for many matrices :)

• Q: something like picking the non-zero entries and then Accumulate over those? Something like Accumulate[SparseArray[m]["NonzeroValues"]] or did I misunderstand?
– user49048
Commented Mar 8, 2022 at 19:46
• Please provide a programmatic way to access your data. People should not need to fill out forms or sign up for services to reproduce your code. See this Q&A about how to Upload large amounts of data more easily in Mathematica Meta. Commented Aug 9, 2023 at 12:22

If the input matrix sa is numeric, you can multiply the accumulated versions with Unitize[sa]. For example,

SeedRandom[1]
sa = SparseArray @ RandomChoice[{3, 1, 1, 1} -> {0, 1, 2, 3}, {7, 7}]


MatrixForm[sa]


sa0 = Unitize[sa] (Accumulate /@ sa);

MatrixForm[sa0]


sa1 = Unitize[sa] Accumulate[sa0];

MatrixForm[sa1]


If the input matrix may have non-numeric elements use saUnitize instead of Unitize where

ClearAll[saUnitize]
saUnitize = SparseArray[#["NonzeroPositions"] -> 1, Dimensions @ #] &;


Example:

SeedRandom[1]

sb = SparseArray @ RandomChoice[{3, 1, 1, 1} -> {0, a, b, 3}, {7, 7}];

MatrixForm[sb]


sb0 = saUnitize[sb] (Accumulate /@ sb);

MatrixForm[sb0]


sb1 = saUnitize[sb] Accumulate[sb0];

MatrixForm[sb1]


Update:

Combining the steps into a single function:

ClearAll[accumulateNonzeroValues]

accumulateNonzeroValues = (saUnitize[sa] # &) @* Accumulate @*
(saUnitize[sa] # &) @* Map[Accumulate];

accumulateNonzeroValues @ sa == sa1

True

accumulateNonzeroValues @ sb == sb1

True


Alternatively, you can also use a combination of SubsetMap + Fold + Accumulate as follows:

foldAccumulate[s_SparseArray] :=
Module[{pos = Join @@ (GatherBy[s["NonzeroPositions"], #] & /@ {First, Last})},
Fold[SubsetMap[Accumulate, #, #2] &, s,  pos]];

foldAccumulate @ sa == sa1

 True

foldAccumulate @ sb == sb1

 True


If we want to skip String, Symbols and missing values:

SeedRandom[1];
dt = SparseArray @ RandomChoice[{3, 1, 1, 1} -> {1, "a", Missing[_], 3}, {7, 7}] // Normal;

skip[v_?VectorQ] :=
Module[{as, st, nm},
as = AssociationThread[Range @ Length[v] -> v];
st = Select[as, MatchQ[_String | _Symbol | _Missing]];
nm = Select[as, NumberQ];
<|Thread[Keys[nm] -> Accumulate @ Values[nm]], st|> // KeySort // Values]


Strangely, Accumulate doesn't know Associations (other functions like MovingAverage do).

Block[{set = Dataset[#, ItemSize -> 4] &},
Row[{dt // set, " \[DoubleLongRightArrow] ", skip /@ dt // set}]]