# How do I check if any element in a list is positive?

As a simple example of what I would like to do, suppose I have a list a of all real numbers. I would like to perform a simple check to see if some element of a is positive. Of course, I could do this with a simple loop, but I feel as if Mathematica would have a more efficient way of doing this, in the spirit of functional programming. Is there, or do I just have to do this with a clumsy loop:

test=False; For[counter=1;counter<=Length[a];counter++;If[a[[counter]]>0,test=True;];];

-
A general guidance for how to "map" loop constructions to functional ones is found in this question, see "alternatives to loops" – FredrikD Jul 24 '12 at 5:41
Thanks for the Accept. You've been away some while. – Mr.Wizard Mar 8 '13 at 3:24
@Mr.Wizard Sorry about that. Quite honestly, for some reason I thought I had accepted an answer awhile ago, and didn't realize that was not the case until yesterday when I got a notification that I had received a badge for the question. My bad. – Jonathan Gleason Mar 8 '13 at 18:46
Jonathan, my comment wasn't meant to complain. Frankly I didn't expect to see you on the site again as you had been away for a while. I appreciated you taking the effort to Accept an answer despite the fact that you're apparently not spending a lot of time on the site these days. So thanks again, and welcome back. – Mr.Wizard Mar 8 '13 at 22:29

If I understand you correctly, simply test if the maximum value in the list is Positive:

Positive @ Max @ a


Speed comparison with other methods that were posted:

timeAvg =
Function[func,
Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}],
HoldFirst];

a = RandomInteger[{-1*^7, 2}, 1*^7];

MemberQ[a, _?Positive] // timeAvg

Total@UnitStep[-a] =!= Length@a // timeAvg

Positive@Max@a // timeAvg


0.593

0.0624

0.01148

### Early-exit methods

Although very fast, especially with packed lists, the method above does scan the entire list with no possibility for an early exit when a positive elements occurs near the front of the list. In that case a test that does not scan the entire list may be faster, such as the one that R.M posted. Exploring such methods I propose this:

! VectorQ[a, NonPositive]


Unlike MemberQ, VectorQ does not unpack a packed list.

Timings compared to MemberQ and Max, first with an early positive appearance:

SeedRandom[1]
a = RandomReal[{-1*^7, 1000}, 1*^7];

Positive @ Max @ a        // timeAvg
! VectorQ[a, NonPositive] // timeAvg
MemberQ[a, _?Positive]    // timeAvg

0.008736

0.00013984

0.2528


(Most of the MemberQ time is spent unpacking the list.)

Then no positive appearance (full scan):

a = RandomInteger[{-1*^7, 0}, 1*^7];

Positive @ Max @ a        // timeAvg
! VectorQ[a, NonPositive] // timeAvg
MemberQ[a, _?Positive]    // timeAvg

0.01148

1.544

2.528


Finally a mid-range appearance of a positive value in an unpacked list:

a = RandomReal[{-50, 0}, 1*^7];
a[[5*^6]] = 1;

Positive @ Max @ a        // timeAvg
! VectorQ[a, NonPositive] // timeAvg
MemberQ[a, _?Positive]    // timeAvg

0.212

0.702

1.045

-
You got me, +1 ;) – Rojo Jul 24 '12 at 3:42
@Rojo you'll get me next time. – Mr.Wizard Jul 24 '12 at 3:44
You surprised me, didn't know you were prowling around – Rojo Jul 24 '12 at 3:45
@Rojo prowling now is it? :^) – Mr.Wizard Jul 24 '12 at 3:45
@Mr.Wizard nice timing - fast machine, is it? – Yves Klett Jul 29 '12 at 13:05

I think the canonical way would be to use an "any" function which you can find in this question. Using a variant of my answer, you can use

MemberQ[list, _?Positive]


to check if any element is positive.

-
A variation: MemberQ[Sign[list], 1] – J. M. Jul 24 '12 at 5:19
Very elegant..! – Dr. belisarius Jul 24 '12 at 5:55
Welcome to the 20K club. – Mr.Wizard Jul 24 '12 at 6:33
@J.M. I cannot find a case where that is best. Positive @ Max @ a is always faster. The strength of MemberQ[list, _?Positive] is with an unpacked list where a positive element occurs near the beginning. – Mr.Wizard Jul 29 '12 at 6:53
@JonathanGleason Yes, most *Q functions yield True or False. I don't know for sure if Q stands for "question", but that's kind of how I reasoned it too. Regarding _?, the relevant doc page is PatternTest. In short, it tests to see if the pattern (here Blank) satisfies the True/False test. Reading the related references and tutorials on this page will also be helpful – R. M. Jul 30 '12 at 0:49
l = RandomChoice[Range[-100, 1], 50];


Simplest to understand is

Or @@ Positive[l]


Perhaps faster for long lists is

Total@UnitStep[-l] =!= Length@l

-
Damn, too quick for me – Dr. belisarius Jul 24 '12 at 3:17

The fastest I could come up with, using the undocumented CompileGetElement for indexing (it's the fastest even without):

f = Compile[
{{l, _Integer, 1}},
Module[
{max = -1},
Do[
If[max < CompileGetElement[l, i], max = CompileGetElement[l, i]],
{i, 1, Length@l}];
max > 0
],
CompilationTarget -> "C"
]


Using timeAvg from Mr.Wizard's answer,

MemberQ[a, _?Positive] // timeAvg
Total@UnitStep[-a] =!= Length@a // timeAvg
Positive@Max@a // timeAvg
f[a] // timeAvg
(*
1.38034
0.114101
0.0187749
0.00830531
*)

-
No fair! Blasted v8 compile-to-C... – Mr.Wizard Jul 30 '12 at 19:44
I get error messages for RandomReal input. Should timing include Compileoverhead as well? – Yves Klett Jul 31 '12 at 6:47
@Yves he compiled for _Integer values; you'd need to compile a separate function for _Real values, then craft a function to select between them, as well as intelligently handle lists that are not packed and may contain mixed types. I don't think compilation overhead should be included in timings unless it uses pre-calculation. – Mr.Wizard Jul 31 '12 at 11:03
@YvesKlett As Mr.W says it's compiled for Integers, and would need to be recompiled for reals. I don't know if timing should include compilation overhead; to be honest, I offer this is what I'd do if I needed it to be as fast as possible. It's clearly not leveraging Mathematica's strengths, nor is particularly pretty or clever. – acl Jul 31 '12 at 13:25
How about a short-circuited version of the loop that keeps testing CompileGetElement[l, i] until it fins something positive? (i.e., use Break[]) somewhere. – J. M. Apr 17 '13 at 4:41

This is even faster than acl's code, for data with positive elements appearing early on, because it stops as soon as it finds a positive.

ff = Compile[{{l, _Real, 1}},
Module[{i = 1, n = Length@l},
While[CompileGetElement[l, i] <= 0. && i <= n, i = i + 1];
i <= n], "RuntimeOptions" -> "Speed", CompilationTarget -> "C"];


Since the OP specifies real numbers I've changed acl's function to take reals:

f = Compile[{{l, _Real, 1}},
Module[{max = -1.},
Do[If[max < CompileGetElement[l, i], max = CompileGetElement[l, i]], {i, 1, Length@l}];
max > 0], "RuntimeOptions" -> "Speed", CompilationTarget -> "C"];


Here is some timing data where I've inserted a single positive element into the list at varying positions:

b = RandomReal[{-1*^7, 0}, 1*^7];

timedata = Table[
a = b; a[[10^j]] = 1.0; {10^j,
{MemberQ[a, _?Positive] // timeAvg,
Total@UnitStep[-a] =!= Length@a // timeAvg,
Positive@Max@a // timeAvg,
f[a] // timeAvg,
ff[a] // timeAvg}}
, {j, 1, 7}];

ListLogLogPlot[Transpose[Thread /@ timedata], Joined -> True]


-
This is surely the optimal way to approach this problem (though likely overkill for the OP). Unfortunately I cannot test it in v7, and as a rule I don't vote for v8-only answers. Not because such answers are not good, but because I think it is important to test things, and it would be unfair to vote for some v8 answers and not others. – Mr.Wizard Jul 31 '12 at 12:08
Somehow after @Mr.Wizard's review no one likes my approach. I'll go and have a proper cry ;-) – Yves Klett Jul 31 '12 at 12:14
@Mr.Wizard, I agree that it's overkill :-) Especially as the OP was explicitly trying to avoid clumsy loops. – Simon Woods Jul 31 '12 at 12:34
@YvesKlett, it's not so much that I don't like your answer, I just thought it was essentially the same algorithm Rojo's UnitStep approach. By the way, why Tr and not Total ? – Simon Woods Jul 31 '12 at 12:39
Tr is shorter ;-) although similar in spirit the timing is different for Clip and UnitStep approaches. – Yves Klett Jul 31 '12 at 17:38

To add a bit variety, you could try:

l = RandomChoice[Range[-100, 1], 5000000];

Tr[Clip[l, {0, Infinity}]] > 0


The timing for different methods of input shows that, unsurprisingly, some solutions are very dependent on the average number of positive elements and others not so much. @Mr.Wizard´s Positive@Max@a and @acl´s compiled f[a] seem to win every time.

f = Compile[{{l, _Integer, 1}},
Module[{max = -1},
Do[If[max < CompileGetElement[l, i],
max = CompileGetElement[l, i]], {i, 1, Length@l}];
max > 0], CompilationTarget -> "C"];

timeAvg =
Function[func,
Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0,
15}], HoldFirst];

a = RandomInteger[{-1*^7, 1}, 1*^7];

MemberQ[a, _?Positive] // timeAvg
Or @@ Positive[a] // timeAvg
Total@UnitStep[-a] =!= Length@a // timeAvg
Tr[Clip[a, {0, Infinity}]] > 0 // timeAvg
Positive@Max@a // timeAvg
f[a] // timeAvg

4.072
0.546
0.0716
0.04056
0.01748
0.01048

a = RandomInteger[{-1*^7, 1*^7}, 1*^7];

MemberQ[a, _?Positive] // timeAvg
Or @@ Positive[a] // timeAvg
Total@UnitStep[-a] =!= Length@a // timeAvg
Tr[Clip[a, {0, Infinity}]] > 0 // timeAvg
Positive@Max@a // timeAvg
f[a] // timeAvg

0.561
0.359
0.078
1.748
0.01684
0.01048

-
If you can find a case where this is faster than Positive @ Max @ list you'll get my vote. If not it's just considerable complication, IMHO. – Mr.Wizard Jul 29 '12 at 19:02
@Mr.Wizard It definitely is a complication and I do not hold high hopes for getting that vote. – Yves Klett Jul 29 '12 at 21:37
@YvesKlett, the reason for the much longer timing for your code in the second example is that you are adding up a large number of large integers. If you set the upper bound of Clip to 1 instead of infinity it is quite a bit quicker. – Simon Woods Jul 31 '12 at 14:24
@SimonWoods I thought so too but the timings are not really conclusive. Using Reals for the bounds masssively slows Clip down, though. – Yves Klett Jul 31 '12 at 17:39

Here is a solution that uses LengthWhile:

test[list_] := Length@list != LengthWhile[list, NonPositive]
`

This test works well for lists that contain a positive element near the beginning of the list.

-