Suppose we have a very big very sparse matrix, e.g.:
m=10^5; n=10^5; r=10^6; a =SparseArray[Transpose[{RandomInteger[{1,m},r],RandomInteger[{1,n},r]}]->RandomChoice[{-1,1,2,-3},r],{m,n}]
How can I efficiently transform a SparseArray
(which is in the CRS format) into the list of lists format, i.e. a table, which contains in the i-th place all entries of the i-th column? One solution is:
Table[{#[[1,1]],#[[2]]}& /@ ArrayRules[a[[All,v]]][[;;-2]],{v,Dimensions[a][[2]]}]
This is way too slow. Another solution (which I copied from here and don't understand), is:
Module[{b=Transpose[a],ci,rp,v,l1,l2}, ci=b["ColumnIndices"]; rp=b["RowPointers"]; v=b["NonzeroValues"];
l1 = Internal`PartitionRagged[Flatten[ci], Differences[rp]];
l2 = Internal`PartitionRagged[v, Differences[rp]];
Table[ Transpose[{l1[[j]],l2[[j]]}], {j,Length@l1}]]
This is slow as well. A third attempt:
Module[{l=GatherBy[Sort@Transpose@Join[Reverse@Transpose@a["NonzeroPositions"],{a["NonzeroValues"]}],First]},
l=AssociationThread[Map[First,l,2],Map[Rest,l,{2}]]; Table[Lookup[l,j,{}],{j,Dimensions[a][[2]]}]];
This is very fast, but it constructs an Association
, which eats up a lot of RAM. Is there a better way?
Block[{pos, id, l, nu}, pos = a["NonzeroPositions"]; l = GatherBy[Sort[MapThread[Append, {Reverse[pos, 2], a["NonzeroValues"]}]], First]; nu = Transpose[{Complement[Range[Last[Dimensions[a]]], a["NonzeroPositions"][[All, 2]]]}]; Insert[Drop[#, None, 1] & /@ l, {}, nu]]
. Please test it against your actual application. $\endgroup$