12

I need to check if a given float is close, within a given tolerance, to any float in an array of floats.

import numpy as np

# My float
a = 0.27
# The tolerance
t = 0.01
# Array of floats
arr_f = np.arange(0.05, 0.75, 0.008)

Is there a simple way to do this? Something like if a in arr_f: but allowing for some tolerance in the difference?


Add

By "allow tolerance" I mean it in the following sense:

for i in arr_f:
    if abs(a - i) <= t:
        print 'float a is in arr_f within tolerance t'
        break
Yoel
  • 9,144
  • 7
  • 42
  • 57
Gabriel
  • 40,504
  • 73
  • 230
  • 404
  • do you look for a solution simple to write and maintain or you need a sophisticated approach to achieve better performance? – Aprillion Sep 21 '14 at 19:30
  • i think `numpy.isclose(a,arr_f, atol=t)` is best answer as you see below! – Mazdak Sep 21 '14 at 19:35
  • @Kasra, why is that better than `(abs(arr_f - a) < t).any()`? – Yoel Sep 21 '14 at 19:59
  • @Yoel your answer is good too , but other useed `numpy` method ! and its advantage if this ! – Mazdak Sep 21 '14 at 20:07
  • @Kasra, I don't think that is relevant, especially since this approach is faster, but I guess to each his own, lol :-) – Yoel Sep 21 '14 at 20:14
  • 1
    Often (but not necessarily in your case), you want a relative error, along the lines of `abs(arr_f - a) / max(abs(arr_f), abs(a))` and a value for the tolerance of perhaps `1E-5` or `1E-6`. I expect numpy provides a mechanism for that, too; the [`isclose()`](http://docs.scipy.org/doc/numpy/reference/generated/numpy.isclose.html?highlight=isclose#numpy.isclose) method might provide it — indeed, instead of the `atol=t` suggested, you could use `rtol=t` to specify a relative tolerance. – Jonathan Leffler Sep 21 '14 at 20:33

3 Answers3

22

How about using np.isclose?

>>> np.isclose(arr_f, a, atol=0.01).any()
True

np.isclose compares two objects element-wise to see if the values are within a given tolerance (here specified by the keyword argument atol which is the absolute difference between two elements). The function returns a boolean array.

user929404
  • 2,153
  • 1
  • 22
  • 27
Alex Riley
  • 169,130
  • 45
  • 262
  • 238
  • The problem of this answer is that it makes all the comparisons before performing the `any` operation, while ordinary `any` stops when encountering the first `False`. So using instead `any(np.isclose(ar, a, atol=0.01) for ar in arr_f)` seems more memory-efficient. – Dr_Zaszuś Dec 22 '20 at 09:12
  • @Dr_Zaszuś if working on machines with _very_ limited RAM, that could _possibly_ be an issue. Otherwise, building additional boolean arrays to reduce is almost never a problem, even when working with very large arrays. In cases where the array is massive enough to threaten the limits of the programmer's available memory, then `any(np.isclose(ar, a, atol=0.01) for ar in arr_f)` is likely to be unacceptably slow. How long does that code take when `arr_f` holds 1 million values and `a` is not close to any of them? It's **27s** for me (and only 4ms with `isclose`/`any`)! – Alex Riley Dec 22 '20 at 16:43
8

If you're only interested in a True/False result, then this should work:

In [1]: (abs(arr_f - a) < t).any()
Out[1]: True

Explanation: abs(arr_f - a) < t returns a boolean array on which any() is invoked in order to find out whether any of its values is True.

EDIT - Comparing this approach and the one suggested in the other answer reveals that this one is slightly faster:

In [37]: arr_f = np.arange(0.05, 0.75, 0.008)

In [38]: timeit (abs(arr_f - a) < t).any()
100000 loops, best of 3: 11.5 µs per loop

In [39]: timeit np.isclose(arr_f, a, atol=t).any()
10000 loops, best of 3: 44.7 µs per loop

In [40]: arr_f = np.arange(0.05, 1000000, 0.008)

In [41]: timeit (abs(arr_f - a) < t).any()
1 loops, best of 3: 646 ms per loop

In [42]: timeit np.isclose(arr_f, a, atol=t).any()
1 loops, best of 3: 802 ms per loop

An alternative solution that also returns the relevant indices is as follows:

In [5]: np.where(abs(arr_f - a) < t)[0]
Out[5]: array([27, 28])

This means that the values residing in indices 27 and 28 of arr_f are within the desired range, and indeed:

In [9]: arr_f[27]
Out[9]: 0.26600000000000001

In [10]: arr_f[28]
Out[10]: 0.27400000000000002

Using this approach can also generate a True/False result:

In [11]: np.where(abs(arr_f - a) < t)[0].any()
Out[11]: True
Community
  • 1
  • 1
Yoel
  • 9,144
  • 7
  • 42
  • 57
  • Very good answer too. I was looking for something more "out of the box" already packed into python or numpy, but I'll definitely keep in mins the better performance of doing it this way. Cheers. – Gabriel Sep 24 '14 at 16:20
-1
[temp] = np.where(np.int32((sliceArray - aimFloat) > 0) == 1)

temp[0] is answer. sliceArray is sorted!

Axisnix
  • 2,822
  • 5
  • 19
  • 41
anseldu
  • 11
  • 3