Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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;];];
share|improve this question
1  
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
1  
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
add comment

7 Answers

up vote 24 down vote accepted

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
share|improve this answer
1  
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
show 3 more comments

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.

share|improve this answer
add comment

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[Compile`GetElement[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 < Compile`GetElement[l, i], max = Compile`GetElement[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]

enter image description here

share|improve this answer
    
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
show 2 more comments

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 < Compile`GetElement[l, i], 
      max = Compile`GetElement[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
share|improve this answer
    
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
add comment

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

f = Compile[
  {{l, _Integer, 1}},
  Module[
   {max = -1},
   Do[
    If[max < Compile`GetElement[l, i], max = Compile`GetElement[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
*)
share|improve this answer
    
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
2  
@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 Compile`GetElement[l, i] until it fins something positive? (i.e., use Break[]) somewhere. –  J. M. Apr 17 '13 at 4:41
add comment

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.

share|improve this answer
1  
A variation: MemberQ[Sign[list], 1] –  J. M. Jul 24 '12 at 5:19
1  
Very elegant..! –  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
1  
@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 –  rm -rf Jul 30 '12 at 0:49
show 3 more comments
l = RandomChoice[Range[-100, 1], 50];

Simplest to understand is

Or @@ Positive[l]

Perhaps faster for long lists is

Total@UnitStep[-l] =!= Length@l
share|improve this answer
1  
Damn, too quick for me –  belisarius Jul 24 '12 at 3:17
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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