9
$\begingroup$

I want to check if a data set of size $10^{10}$ contains any non-positive elements. Positive[Name of dataset] returns a list of True and False of length $10^{10}$. I want only a single True if all terms of that dataset are positive and False otherwise.

$\endgroup$
3
  • 3
    $\begingroup$ VectorQ[list, Positive]? $\endgroup$ Commented Mar 27, 2019 at 14:26
  • 1
    $\begingroup$ Use Apply as in And @@ Positive[list] $\endgroup$
    – Bob Hanlon
    Commented Mar 27, 2019 at 14:52
  • $\begingroup$ How do you want to deal with terms that are exactly zero? Do you need all terms to be positive (use Positive), or do you need all terms to be zero or positive (use NonNegative)? $\endgroup$
    – Roman
    Commented Mar 27, 2019 at 15:01

4 Answers 4

14
$\begingroup$

Alternate solution:

list = RandomReal[1, 10^6];
Min[list] >= 0
$\endgroup$
4
  • 3
    $\begingroup$ ...i.e. NonNegative[Min[list]]. $\endgroup$ Commented Mar 27, 2019 at 16:08
  • 3
    $\begingroup$ Very good solution! This avoids lists of Booleans and hence allows for vectorization. (Boolean arrays cannot be packed.) $\endgroup$ Commented Mar 27, 2019 at 18:01
  • 2
    $\begingroup$ This is about 100 times faster than any of the other solutions. Impressive! $\endgroup$
    – Roman
    Commented Mar 27, 2019 at 20:03
  • $\begingroup$ Thank you very much. $\endgroup$
    – a b
    Commented Mar 28, 2019 at 8:40
10
$\begingroup$

Since you have a very large list, you should look at the timing

list = RandomReal[1, 10^6];

(And @@ Positive[list]) // AbsoluteTiming (* Hanlon *)

(* {0.050573, True} *)

VectorQ[list, Positive] // AbsoluteTiming (* J.M. *)

(* {0.261642, True} *)

(AnyTrue[list, Negative] // Not) // AbsoluteTiming (* Morbo *)

(* {0.324062, True} *)

And @@ (list /. {x_?Negative -> False, 
 x_?Positive -> True}) // AbsoluteTiming (* Alrubaie *)

(* {1.00664, True} *)

EDIT: As suggested by mjw, encountering a nonpositive value early in the list significantly alters the results.

list2 = ReplacePart[list, 1000 -> -1];

(And @@ Positive[list2]) // AbsoluteTiming (*Hanlon*)

(* {0.277642, False} *)

VectorQ[list2, Positive] // AbsoluteTiming (*J.M.*)

(* {0.000223, False} *)

(AnyTrue[list2, Negative] // Not) // AbsoluteTiming (*Morbo*)

(* {0.000262, False} *)

And @@ (list2 /. {x_?Negative -> False, 
     x_?Positive -> True}) // AbsoluteTiming (*Alrubaie*)

(* {1.43026, False} *)
$\endgroup$
8
  • $\begingroup$ Looks like your method is five times faster than the next best! Can you give some insight into why this is? Thanks! $\endgroup$
    – mjw
    Commented Mar 27, 2019 at 15:34
  • 1
    $\begingroup$ Also, and I guess this depends on the probability of any entry being negative, it may make sense for the algorithm to stop as soon as it finds a negative (or non-positive) element in the list. $\endgroup$
    – mjw
    Commented Mar 27, 2019 at 15:37
  • 1
    $\begingroup$ @mjw, And[] does short-circuit evaluation. $\endgroup$ Commented Mar 27, 2019 at 15:41
  • 1
    $\begingroup$ @Bob, Thank you for your edit. Why, though, does it take longer for your method to work when there is a negative entry? I would have thought that in each case, it would take the same amount of time to go through the whole list. $\endgroup$
    – mjw
    Commented Mar 27, 2019 at 16:03
  • 1
    $\begingroup$ @Roman, I do not believe that any lazy evaluation is being done. My comment was more to point out that an evaluation like And[True, True, False, True, True, ... True] will finish at once (and similar remarks apply for Or[]). Perhaps one can judiciously use Catch[]/Throw[]if an early-return test for long lists is desired. $\endgroup$ Commented Mar 27, 2019 at 16:06
5
$\begingroup$

Ah, maybe this is too simple, but works for exactly what you're doing:

data = Table[RandomReal[{-1,1}],{i,1,1000}];
AnyTrue[data,Negative] // Not
(*False*)

data2 = Table[RandomReal[], {i, 1, 10^2}];
AnyTrue[data2, Negative] // Not
(*True*)
$\endgroup$
4
  • $\begingroup$ AllTrue[data, Positive] to get the sign right. Or use Not on your solution. $\endgroup$
    – Roman
    Commented Mar 27, 2019 at 14:55
  • $\begingroup$ @Roman - the poster is using AnyTrue not AllTrue $\endgroup$
    – Bob Hanlon
    Commented Mar 27, 2019 at 15:00
  • 1
    $\begingroup$ Yes @BobHanlon . In order to invert his solution to what the OP wants you have to either Not@AnyTrue[data,Negative] or (simpler) AllTrue[data,Positive] or AllTrue[data,NonNegative]. $\endgroup$
    – Roman
    Commented Mar 27, 2019 at 15:04
  • $\begingroup$ ah, signs are reversed, missed that part. I updated the code to reflect questioners exact question. $\endgroup$ Commented Mar 27, 2019 at 15:04
3
$\begingroup$
list = {1, 2, 3, 4, -5, -6, -7};

list /. {x_?Negative -> True, x_?Positive -> False}
$\endgroup$
0

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.