Идентификация числовых и типов массивов в numpy

Существует ли существующая функция в numpy, которая сообщит мне, является ли значение числовым или массив numpy? Я пишу код обработки данных, который должен обрабатывать числа в нескольких разных представлениях (под "числом" я подразумеваю любое представление числовой величины, которое можно манипулировать с помощью стандартных арифметических операторов +, -, *,/, * *).

Некоторые примеры поведения, которое я ищу

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

Если такой функции нет, я знаю, что ее не должно быть сложно написать, например,

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

но есть ли другие числовые типы, которые я должен включить в список?

Ответы

Ответ 1

Как ответили другие, кроме тех, которые вы упомянули, могут быть другие числовые типы. Один из подходов заключался бы в том, чтобы явно проверить возможности, которые вы хотите, с чем-то вроде

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

Это работает для всех ваших примеров, кроме последнего, numpy.array(['1']). Это потому, что numpy.ndarray имеет специальные методы для числовых операций, но вызывает TypeError, если вы попытаетесь использовать их ненадлежащим образом со строками или массивами объектов. Вы можете добавить явную проверку для этого, например

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

Это может быть достаточно хорошо.

Но... вы никогда не сможете быть на 100% уверены, что кто-то не будет определять другой тип с таким же поведением, поэтому более надежным способом является попытка сделать расчет и поймать исключение, что-то вроде

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

но в зависимости от того, как часто вы планируете использовать его и с какими аргументами, это может быть непрактичным (это может быть потенциально медленным, например, с большими массивами).

Ответ 2

В общем, гибкий, быстрый и путинский способ обработки неизвестных типов состоит в том, чтобы просто выполнить некоторую операцию над ними и получить исключение из недопустимых типов.

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

Мне кажется, что этот подход проще, чем специальная защита от некоторой функции, чтобы определить абсолютную определенность типа.

Ответ 3

Кроме того, numpy имеет numpy.isreal и другие подобные функции (numpy.is + Tab должен их перечислять).

У всех есть свои забавные угловые случаи, но один из них может быть полезен.

Ответ 4

Ваш is_numeric не определен. См. Мои комментарии к вашему вопросу.

Другие числовые типы могут быть: long, complex, fractions.Fraction, numpy.bool_, numpy.ubyte,...

operator.isNumberType() возвращает True для чисел Python и numpy.array.

С Python 2.6 вы можете использовать isinstance(d, numbers.Number) вместо устаревшего operator.isNumberType().

Как правило, лучше проверить возможности объекта (например, можно ли добавить к нему целое число), а не его тип.

Ответ 5

isinstance(numpy.int32(4), numbers.Number) возвращает False, так что это не совсем работает. operator.isNumberType() работает только со всеми вариантами числовых чисел, включая numpy.array([1]).