# Indices of nonzero rows in a sparse array

Given $$A\!\in\!\mathbb{Z}^{m\times n}$$ stored as a=SparseArray[{...},{m,n}], how can I obtain the list of all indices $$i\!\in\!\{1,\ldots,m\}$$ for which the $$i$$-th row in $$A$$ is nonzero?

One option is DeleteDuplicates@Flatten@Transpose[a]["ColumnIndices"]. Is there a nicer way, or is this optimal?

RandomPrivatePositionsOf[Unitize[Differences[a["RowPointers"]]], 1]


A timing example:

n = 100000;
m = 6000000;
a[[RandomInteger[{1, n}, 1000], All]] = 0;

r = DeleteDuplicates@Flatten@Transpose[a]["ColumnIndices"]; // AbsoluteTiming // First
r1 = RandomPrivatePositionsOf[Unitize[Differences[a["RowPointers"]]], 1]; // AbsoluteTiming // First
Sort[r] == r1


0.579587

0.002103

True

# Edit

The undocumented function RandomPrivatePositionsOf may be replaced by the following compiled function.

PositionsOfInteger = Compile[{{a, _Integer, 1}, {x, _Integer}},
Block[{b, i = 0},
b = Table[0, {Length[a]}];
Do[If[CompileGetElement[a, j] == x, b[[++i]] = j], {j, 1,
Length[a]}];
If[i > 0, b[[1 ;; i]], Most[{0}]]
],
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
];


Indeed, this turns out to be even a bit faster:

r2 = PositionsOfInteger[Unitize[Differences[a["RowPointers"]]], 1]; //
AbsoluteTiming // First
r1 == r2


0.001647

True

• What does "ColumnIndices" do? In this post, it says "gives the row pointers array from the compressed sparse row data", but I don't understand how it works. – Leon Jan 2 at 17:42
• And what is this RandomPrivatePositionsOf? I can't find these commands in the documentation. – Leon Jan 2 at 18:03
• In a nutshell, one has these relations (both expression evaluate to True): Flatten[a["ColumnIndices"]] == a["NonzeroPositions"][[All, 2]] and Prepend[Accumulate[Total[Unitize[a], {2}]], 0] == a["RowPointers"]. – Henrik Schumacher Jan 2 at 18:03
• There is no documentation on RandomPrivatePositionsOf; it is an undocumented funtion. RandomPrivatePositionsOf[#, 0] & does essentially the same as Flatten[Position[#, 0]] & - but it is ten times faster. – Henrik Schumacher Jan 2 at 18:05
• Well, everything in Mathematica has a nonvanishing risk to be invalidated by future versions. However, for symbols in the "System" context, this risk is rather small. While there are several prominent examples of undocumented symbols that appear to have been ''stable'' for several major realeases, the risk of future changes of these symbols is certainly much higher. This is why I added a compiled function PositionsOfInteger as substitute for RandomPrivatePositionsOf. – Henrik Schumacher Jan 27 at 11:50