tags:

views:

1102

answers:

6

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?

A: 

You can use type():

>>> a=scipy.array([1,2,3,4])
>>> b=array.array('L')
>>> type(a)
<type 'numpy.ndarray'>
>>> type(b)
<type 'array.array'>
>>> c=10.5
>>> type(c) in scipy.ScalarType
True
>>>
gimel
A: 

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.

J.F. Sebastian
Yeah, but if I had an exact definition in mind I could have written the function ;-) I edited in some more information.
David Zaslavsky
+3  A: 

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.

Triptych
It is not an answer to the question but I agree completely.
J.F. Sebastian
The problem is that '5'*5 does work.
J.F. Sebastian
@JF, that's true, but I still like this answer to this question. I think the str*int bug will be easier to catch than the is_numeric function will be to write. Also, consider that, IIRC, no other mathematical operation is defined where the two operands are str and int.
Triptych
I like it too... if only I could accept more than one ;-)
David Zaslavsky
+3  A: 

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

def is_numeric(obj):
    attrs = ['__add__', '__sub__', '__mul__', '__div__', '__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).

dF
A: 

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

Cyrus Omar
A: 

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.

Cyrus Omar