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.

I would like to count the negative values of a list.

My approach was Count[data, -_] which doesn't work.

How can I tell Mathematica to count all numbers with a negative sign?

share|improve this question

6 Answers 6

up vote 22 down vote accepted

I assume that you have numeric values. A much more efficient way would be

-Total[UnitStep[data] - 1]]

or

Total[1-UnitStep[data]]

Note: While the second notation is certainly a bit more compact, it is about 35% slower than the double-minus notation. I have no idea why. On my system, it takes on average 0.22 sec vs 0.30 sec.

Compare timings between the faster UnitStep version and the pattern matching approach:

data = RandomReal[{-10, 10}, 10^7];

Timing[-Total[UnitStep[data] - 1]]
(* ==> {0.222, 5001715} *)

Timing[Count[data, _?Negative]]
(* ==> {6.734, 5001715} *)
share|improve this answer
1  
Total[1 - UnitStep[data]] is more compact, no? –  J. M. Aug 20 '12 at 13:28
    
@J.M. Clever!!! I will edit according to your comment. However, it seams to make the code slightly slower. Any idea why? –  Thomas Aug 20 '12 at 13:29
    
I get the same timings on my machine. Did you notice that it turns around for shorter data? (Try 100 or so.) –  sebhofer Aug 20 '12 at 18:02
6  
The faster version does two negations but both applied to numbers, while the slow version negates a whole list –  Rojo Aug 21 '12 at 3:51

I've found the fastest way to do this sort of thing is to use a very simple procedural Do loop compiled to C.

f2 = Compile[{{numbers, _Real, 1}}, 
  Block[{count = 0}, Do[If[number < 0., count++], {number, numbers}]; 
   count], CompilationTarget -> "C", "RuntimeOptions" -> "Speed"]
share|improve this answer

Lots of nice answers. Here's another, but I'm afraid it's quite slow.

Total[Select[data, # < 0 &]]

Though this totals all the negative numbers. To count them you could do:

-Total[Sign[Select[data, # < 0 &]]]

Then I got to thinking, "how would I program this in Matlab?"

 Total[1 - Sign[data]]/2
share|improve this answer
    
For the last one, this would be significantly faster: (Length[data] - Total[Sign[data]])/2, for the same reasons Rojo stated in Thomas' answer. –  ecoxlinux Aug 22 '12 at 17:27
    
good point -- thanks –  bill s Aug 23 '12 at 3:55

This seems competitive in terms of speed:

Total@Clip[list, {0, 0}, {1, 0}]

Testing:

SetAttributes[timeAvg, HoldFirst]
$HistoryLength = 0;
timeAvg[func_] := 
 Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}]
neg = Length@# - Tr@UnitStep@# &;
list = RandomReal[1*^7 {-1, 1}, 1*^7];

Total@Clip[list, {0, 0}, {1, 0}] // timeAvg
-Total[UnitStep[list] - 1] // timeAvg
Length[list] - Total[UnitStep[list]] // timeAvg
neg@list // timeAvg

(* 0.1594, 0.375, 0.1812, 0.197 *)
share|improve this answer
1  
Interesting, although in my machine this new one is still significantly slower than the last two. –  ecoxlinux Aug 22 '12 at 17:37

For numeric values, the following:

Length[data] - Total[UnitStep[data]] 

is 50% faster than Thomas' solution.

Update: An even faster approach will be to compile to C, as shown in the following function f:

ClearAll[f];
f = Compile[{{vector, _Real, 1}, {bound, _Real}},
      Module[{t = 0, i = 1, len = Length[vector]}, 
        For[i = 1, i <= len, i++, t += Boole[vector[[i]] < bound]]; t], 
      CompilationTarget -> "C", "RuntimeOptions" -> "Speed"]

Using the timeAvg function, as defined in Mr. Wizard's answer, we have:

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

data = RandomReal[{-10, 10}, 10^7];

{-Total[UnitStep[data] - 1] // timeAvg,
 Length[data] - Total[UnitStep[data]] // timeAvg,
 Length[data] - Tr[UnitStep[data]] // timeAvg,
 f[data, 0.] // timeAvg}

(* ==> {0.105993, 0.0431972, 0.0422639, 0.0141324} *)

Note that the function f defined above will work for any upper bound, and not just 0. And would be significantly faster (at least in my machine) than the corresponding Length[data]- Tr[UnitStep[data - bound]] approach when bound is not zero.

For the dangers with the use of "RuntimeOptions"->"Speed", see the Mathematica help: RuntimeOptions.

share|improve this answer

Use _?Negative:

list = RandomInteger[{-9, 9}, 30]

Count[list, _?Negative]

Your pattern will match an object with an explicit negative sign:

Count[{a, -b, c, a, -a, a, -b}, -_]

3

You could combine the patterns to match either:

Count[{-1, -2, 3, 4, -a, b, -c}, -_ | _?Negative]

4


Since this has become a speed competition (which is fine by me), rather than the beginner's question I took it to be, here is my own variation, using Tr for fast packed array summing:

neg = Length@# - Tr@UnitStep@# &;

Timings:

SetAttributes[timeAvg, HoldFirst]

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

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

-Total[UnitStep[list] - 1] // timeAvg

Length[list] - Total[UnitStep[list]] // timeAvg

neg @ list // timeAvg

0.592

0.234

0.1278

share|improve this answer
    
Perhaps you should mention that the latter solution only works for variables with an added minus, not for values. –  Sjoerd C. de Vries Aug 20 '12 at 11:49
    
Okay, so expanding on this question, are the easiest ways to find all values in the list less than, say, 5 Length[list] - Total[UnitStep[list - 5]] using the UnitStep method? Or is there another pattern match to use? –  kale Aug 21 '12 at 1:47
    
@kalewallace pattern matching methods are easier, more declarative and more general, but when everything is numeric, the other methods are way faster. Count[list, i_/;i<5] –  Rojo Aug 21 '12 at 3:54
1  
@kalewallace you may find this answer of interest. But, as Rojo states there is a trade-off, with patterns being more general and often conceptually simpler, while numeric approaches are usually faster given the right data (packed arrays of machine size reals or integers). –  Mr.Wizard Aug 21 '12 at 9:03
1  
@Anastasiia your comment: "this doesn't work if one wants to count complex numbers" doesn't make much sense as complex numbers are not simply positive or negative. Perhaps you just want Count[dat, _Complex] to count all complex numbers in dat, or Count[cr, _?(Im@# > 0 &)] to count all numbers with an imaginary part greater than zero? –  Mr.Wizard Sep 19 '12 at 15:24

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.