Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Identifying numeric and array types in numpy

Tags:

python

numpy

Is there an existing function in numpy that will tell me if a value is either a numeric type or a numpy array? I'm writing some data-processing code which needs to handle numbers in several different representations (by "number" I mean any representation of a numeric quantity which can be manipulated using the standard arithmetic operators, +, -, *, /, **).

Some examples of the behavior I'm looking for

>>> is_numeric(5)
True
>>> is_numeric(123.345)
True
>>> is_numeric('123.345')
False
>>> is_numeric(decimal.Decimal('123.345'))
True
>>> is_numeric(True)
False
>>> is_numeric([1, 2, 3])
False
>>> is_numeric([1, '2', 3])
False
>>> a = numpy.array([1, 2.3, 4.5, 6.7, 8.9])
>>> is_numeric(a)
True
>>> is_numeric(a[0])
True
>>> is_numeric(a[1])
True
>>> is_numeric(numpy.array([numpy.array([1]), numpy.array([2])])
True
>>> is_numeric(numpy.array(['1'])
False

If no such function exists, I know it shouldn't be hard to write one, something like

isinstance(n, (int, float, decimal.Decimal, numpy.number, numpy.ndarray))

but are there other numeric types I should include in the list?

like image 755
David Z Avatar asked Feb 01 '09 06:02

David Z


5 Answers

As others have answered, there could be other numeric types besides the ones you mention. One approach would be to check explicitly for the capabilities you want, with something like

# Python 2
def is_numeric(obj):
    attrs = ['__add__', '__sub__', '__mul__', '__div__', '__pow__']
    return all(hasattr(obj, attr) for attr in attrs)

# Python 3
def is_numeric(obj):
    attrs = ['__add__', '__sub__', '__mul__', '__truediv__', '__pow__']
    return all(hasattr(obj, attr) for attr in attrs)

This works for all your examples except the last one, numpy.array(['1']). That's because numpy.ndarray has the special methods for numeric operations but raises TypeError if you try to use them inappropriately with string or object arrays. You could add an explicit check for this like

 ... and not (isinstance(obj, ndarray) and obj.dtype.kind in 'OSU')

This may be good enough.

But... you can never be 100% sure that somebody won't define another type with the same behavior, so a more foolproof way is to actually try to do a calculation and catch the exception, something like

def is_numeric_paranoid(obj):
    try:
        obj+obj, obj-obj, obj*obj, obj**obj, obj/obj
    except ZeroDivisionError:
        return True
    except Exception:
        return False
    else:
        return True

but depending on how often you plan to call use it and with what arguments, this may not be practical (it can be potentially slow, e.g. with large arrays).

like image 50
dF. Avatar answered Nov 18 '22 14:11

dF.


Also, numpy has numpy.isreal and other similar functions (numpy.is + Tab should list them).

They all have their fun corner cases but one of those could be useful.

like image 7
Cyrus Omar Avatar answered Nov 18 '22 14:11

Cyrus Omar


In general, the flexible, fast, and pythonic way to handle unknown types is to just perform some operation on them and catch an exception on invalid types.

try:
    a = 5+'5'
except TypeError:
    print "Oops"

Seems to me that this approach is easier than special-casing out some function to determine absolute type certainty.

like image 6
Kenan Banks Avatar answered Nov 18 '22 16:11

Kenan Banks


Your is_numeric is ill-defined. See my comments to your question.

Other numerical types could be: long, complex, fractions.Fraction, numpy.bool_, numpy.ubyte, ...

operator.isNumberType() returns True for Python numbers and numpy.array.

Since Python 2.6 you can use isinstance(d, numbers.Number) instead of deprecated operator.isNumberType().

Generally it is better to check the capabilities of the object (e.g., whether you can add an integer to it) and not its type.

like image 1
jfs Avatar answered Nov 18 '22 14:11

jfs


isinstance(numpy.int32(4), numbers.Number) returns False, so that doesn't quite work. operator.isNumberType() does work on all the variants of numpy numbers, however, including numpy.array([1]).

like image 1
Cyrus Omar Avatar answered Nov 18 '22 15:11

Cyrus Omar