21

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?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
RMMA
  • 2,710
  • 2
  • 18
  • 33

11 Answers11

31

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} *)
Thomas
  • 1,967
  • 1
  • 15
  • 18
25

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

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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 01: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 03: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 09:03
  • Just a side note.I was testing a solution, ListPlot[(Reap@ Do[Sow[Total@Clip[list, {0, 0}, {1, 0}] // timeAvg], {10}])[[2, 1]], PlotRange -> {Automatic, {0, Automatic}}, Joined -> True] and timeAvg displays a large standard deviation – Dr. belisarius Aug 22 '12 at 14:16
  • Looks like this is a very nice approach. However, this doesn't work if one wants to count complex numbers in a list. I also tried the straight away form (where ev is the list of numbers): count[]; For[u = 1, u <= 6, u++, If[Abs[Im[ev[[u]]] > 10^-10], count++]; ]; But no results...Maybe somebody has already done something like that? – Lady InRed Sep 19 '12 at 10:32
  • 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
  • Thanks! I was trying out something similar by putting imaginary parts into a separate list and then checking the length of it... – Lady InRed Oct 04 '12 at 14:29
19

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.

ecoxlinux
  • 979
  • 4
  • 10
7

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 *)
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
6

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"]
s0rce
  • 9,632
  • 4
  • 45
  • 78
4

Another one:

Total@Boole@Negative@data
sakra
  • 5,120
  • 21
  • 33
4

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
bill s
  • 68,936
  • 4
  • 101
  • 191
3

As a short variant

Count[data, x_ /; x < 0]
bmf
  • 15,157
  • 2
  • 26
  • 63
3

Using GroupBy

SeedRandom[0];
list = RandomInteger[{-9, 9}, 30];

Length @ GroupBy[list, Sign][-1]

16

Length @ GroupBy[list, Sign][#] & /@ {-1, 0, 1}

{16, 1, 13}

eldo
  • 67,911
  • 5
  • 60
  • 168
2

Using CountsBy:

list = {-9, 1, 1, -4, -9, -3, 1, -9, 4, 3, 4, -5, 8, -5, -2, -9};

CountsBy[list, Sign][-1]

(9)

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
1

Some more just because. I ma grabbing the list from @E. Chan-López

list = {-9, 1, 1, -4, -9, -3, 1, -9, 4, 3, 4, -5, 8, -5, -2, -9};

to begin with

Select[list, Negative] // Length

also

DeleteCases[list, x_ /; ! MatchQ[x, _?Negative]] // Length

furthermore

Pick[list, Negative[list]] // Length

and finally

Pick[#, 1 - HeavisideTheta@#, 1] &@list // Length
bmf
  • 15,157
  • 2
  • 26
  • 63