Question

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?

Was it helpful?

Solution

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).

OTHER TIPS

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.

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.

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.

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]).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top