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?
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} *)
Total[1 - UnitStep[data]]
is more compact, no?
$\endgroup$
Commented
Aug 20, 2012 at 13:28
data
? (Try 100 or so.)
$\endgroup$
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
Length[list] - Total[UnitStep[list - 5]]
using the UnitStep
method? Or is there another pattern match to use?
$\endgroup$
Count[list, i_/;i<5]
$\endgroup$
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?
$\endgroup$
Commented
Sep 19, 2012 at 15:24
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.
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 *)
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"]
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
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}
Using CountsBy
:
list = {-9, 1, 1, -4, -9, -3, 1, -9, 4, 3, 4, -5, 8, -5, -2, -9};
CountsBy[list, Sign][-1]
(*9*)
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