20
$\begingroup$

I want to know is there any one line code to find position of first nonzero number without calculating position of all non zero numbers. For example,

A = {0, 0, 0, 1, -2, -1, 0};
First[Flatten[Position[A, _?(# != 0 &)], 1]]

will give me position of first non zero number. But first it calculate position of all non zero number. Is there any way to stop position function when it found first non zero number?

$\endgroup$
1
  • 6
    $\begingroup$ FirstPosition[A, _?(# != 0 &)] $\endgroup$ May 2, 2019 at 19:21

9 Answers 9

19
$\begingroup$

You can specify number of object to return in Position. For example,

list = RandomInteger[{-5, 5}, 100000];
Position[list, _?(# != 0 &), 1, 1] // AbsoluteTiming

{0.003050, {{1}}}

Position[list, _?(# != 0 &)] // First // AbsoluteTiming

{0.138709, {1}}

$\endgroup$
0
9
$\begingroup$
SeedRandom[1];
LengthWhile[RandomInteger[{-5, 5}, 100000], # == 0 &] + 1
(* 1 *)
$\endgroup$
2
  • $\begingroup$ you mean by this? LengthWhile[list, # == 0 &] + 1 $\endgroup$
    – halmir
    Oct 4, 2013 at 19:03
  • $\begingroup$ @halmir yes, I corrected it while you were writing the comment :) $\endgroup$ Oct 4, 2013 at 19:04
9
$\begingroup$

We can use SparseArray. It computes all the positions, but it's a bit faster than Position, esp. on packed arrays, since Position unpacks a packed array.

First @ SparseArray[packedarray]["NonzeroPositions"]

or

First @ SparseArray[Developer`ToPackedArray @ unpackedlist]["NonzeroPositions"]

Timings on ten random lists:

SeedRandom[1];
n = 10;
list = RandomChoice[{0.9, 0.1} -> {0, 1}, {n, 100000}];
packedlist = Developer`ToPackedArray @ list;

Table[
  First @ SparseArray[Developer`ToPackedArray @ list[[i]]]["NonzeroPositions"],
  {i, n}] // AbsoluteTiming
Table[
  Position[list[[i]], _?(# != 0 &), 1, 1],
  {i, n}] // AbsoluteTiming

Table[
  First @ SparseArray[packedlist[[i]]]["NonzeroPositions"],
  {i, n}] // AbsoluteTiming
Table[
  Position[packedlist[[i]], _?(# != 0 &), 1, 1],
  {i, n}] // AbsoluteTiming

{0.007372, {{15}, {28}, {4}, {20}, {13}, {2}, {8}, {7}, {42}, {9}}}
{0.009183, {{{15}}, {{28}}, {{4}}, {{20}}, {{13}}, {{2}}, {{8}}, {{7}}, {{42}}, {{9}}}}

{0.005098, {{15}, {28}, {4}, {20}, {13}, {2}, {8}, {7}, {42}, {9}}}
{0.024295, {{{15}}, {{28}}, {{4}}, {{20}}, {{13}}, {{2}}, {{8}}, {{7}}, {{42}}, {{9}}}}


Compiled

It's rather straightforward in an imperative paradigm:

firstnzp = Compile[{{list, _Integer, 1}},
  Do[If[list[[i]] != 0, Return[i]], {i, Length@list}],
  RuntimeOptions -> "Speed"
  ]

And fast:

Table[firstnzp @ list[[i]], {i, n}] // AbsoluteTiming

{0.004164, {15, 28, 4, 20, 13, 2, 8, 7, 42, 9}}

$\endgroup$
8
$\begingroup$

While this certainly isn't a contender for speed, patterns are fun:

A /. {z : Longest[(0 | 0`) ...], ___} :> Length@{z} + 1
$\endgroup$
6
$\begingroup$

Just for fun:

ArrayRules[list][[1, 1, 1]]
$\endgroup$
4
$\begingroup$

Yet another compiled function:

firstnzp2 = Compile[{{list, _Integer, 1}}, 
  Module[{n = 0}, While[list[[++n]] == 0]; n], 
  CompilationTarget -> "C", RuntimeOptions -> "Speed"];

The speed is the same as Michael's compiled code after addition CompilationTarget -> "C".

Or one-liner:

1 + Total@UnitStep[-Accumulate@Unitize[list]]
$\endgroup$
3
$\begingroup$

Comparing:

Position[s, e_ /; e != 0] // Trace

Position[s, e_ /; e != 0, 1, 1] // Trace

The number of matched positions to be found at certain levels can be specified, then Position, searching left to right, stops as soon as it finds that many.

$\endgroup$
2
$\begingroup$

If you are interested in manual solution, this might be helpful as well.

    f[x_] := (If[x > 0, Return[Position[A, x][[1, 1]]]]; x)

sparr = SparseArray[{{1, 400} -> 2, {1, 90000} -> 2}]
A = Normal[sparr][[1]];
  Catch[Table[If[f[A[[i]]] > 0, Throw[f[A[[i]]]], f[A[[i]]]], {i, Length[A]}]]

400 (Timing 0.06 Sec)

sparr = SparseArray[{{1, 90000} -> 2}]
A = Normal[sparr][[1]];
  Catch[Table[If[f[A[[i]]] > 0, Throw[f[A[[i]]]], f[A[[i]]]], {i, Length[A]}]]

90000 (1.11 Sec)

$\endgroup$
1
$\begingroup$

Here is a simpler method to get the position of the first nonzero element:

Position[A, DeleteCases[A, 0][[1]]][[1, 1]]  (* Single Line *)

Here is how it works:

firstNonZeroElement =  DeleteCases[A, 0][[1]] (* Returns value of first non-zero element *)
Position[A, firstNonZeroElement] (* Finds positions of all occurances of firstNonZeroElement *)
First[Position[A, firstNonZeroElement]][[1]] (* Selects the first one *)
$\endgroup$
3
  • $\begingroup$ I don't think that this is what OP asked for: DeleteCases[A, 0][[1]] messes around with the whole list A and this leads to a timing that is consideraly higher than halmir's. $\endgroup$ May 21, 2019 at 1:10
  • $\begingroup$ You are right about timing, this might not be the best. A remains unchanged. $\endgroup$ May 21, 2019 at 10:19
  • $\begingroup$ The benefit of this method is that it may be understandable for a new Mathematica user $\endgroup$ Aug 12, 2019 at 2:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.