7
$\begingroup$

I have a long list (apprx. 10000 elements) of numbers. I want to count the number of elements between two determined vallues a and b.

For example, consider the list:

0.38591
0.05216
0.3482
0.88671
0.16251
0.46188
0.91803
0.40279
0.31246
0.80954
0.40326
1.291
0.55321

and a=0.3 and b=0.6

$\endgroup$
10
$\begingroup$
n = 10000;
list = RandomReal[{0, 1}, n];
a = 0.3;
b = 0.6;
UnitStep[list - a].UnitStep[b - list]
$\endgroup$
6
$\begingroup$

This isn't as fast as the UnitStep method given by Henrik, but for many applications having the most efficient method isn't necessary.

Length @ Select[list, Between[{a, b}]]

or

Count[list, _?(Between[{a, b}])]
$\endgroup$
4
$\begingroup$
Total@Boole[a <= # <= b & /@ list]
$\endgroup$
4
$\begingroup$

If you are trying to do this operation repeatedly for the same list but different endpoints, then a faster alternative is to use Nearest. The basic idea is the following (I increased the size of the random list so that the speed difference is more readily apparent):

SeedRandom[0]
list=RandomReal[{0,1}, 10^5];
nf = Nearest[Sort @ list -> "Index"]; //AbsoluteTiming
nf[.6] - nf[.3] //RepeatedTiming

{0.015579, Null}

{4.97*10^-6, {29950}}

as compared to Henrik's solution:

UnitStep[list-a].UnitStep[b-list] //RepeatedTiming

{0.00025, 29950}

The time to create the nearest function is rather large, but it only needs to be done once. The application of the nearest functions is extremely fast.

However, although for this example the two approaches agree, the Nearest approach doesn't quite work because the nearest point may be lower or greater than the endpoint selected. So, a more refined approach that accounts for this fuzziness is the following:

InbetweenCount[list_] := With[{slist = Sort @ list},
    InbetweenCountFunction[slist, Nearest[slist -> "Index"]]
]

InbetweenCountFunction[list_, nf_][{left_, right_}] := With[
    {
    l = First @ nf[left],
    r = First @ nf[right]
    },
    r - l + Switch[Sign[list[[{l, r}]] - {left, right}],
        {1,-1}, 1,
        {-1,1}, -1,
        _, 0
    ]
]

This is how the above code would be used:

icf = InbetweenCount[list]; //AbsoluteTiming
icf[{.3, .6}] //RepeatedTiming

{0.014189, Null}

{0.0000114, 29950}

Notice that the creation of the InbetweenCountFunction takes nontrivial time. However, it's application is very fast.

It is possible to vectorize the InbetweenCountFunction so that it works on a list of {left, right} pairs to obtain a further speed improvement.

$\endgroup$
2
$\begingroup$

(Not as fast as that by Henrik)

Count[Clip[list, {a, b}, {0., 0.}], Except[0.]]
$\endgroup$
2
$\begingroup$

You can also use BinCounts using {{a,b}} as bin specification:

BinCounts[list, {{a, b}}] 

or a combination of Clip, Unitize and Total:

Total @ Unitize @ Clip[list, {a, b}, {0, 0}]

Note: This is slower than Henrik's approach using Dot+ UnitStep combination.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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