Identificazione di tipi numerici e di array in numpy
Domanda
Esiste una funzione esistente in numpy che mi dirà se un valore è un tipo numerico o un array numpy? Sto scrivendo un codice di elaborazione dati che deve gestire i numeri in diverse rappresentazioni (per & Quot; numero & Quot; intendo qualsiasi rappresentazione di una quantità numerica che può essere manipolata usando gli operatori aritmetici standard, + , -, *, /, **).
Alcuni esempi del comportamento che sto cercando
>>> 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
Se non esiste tale funzione, so che non dovrebbe essere difficile scriverne una, qualcosa del genere
isinstance(n, (int, float, decimal.Decimal, numpy.number, numpy.ndarray))
ma ci sono altri tipi numerici che dovrei includere nell'elenco?
Soluzione
Come altri hanno risposto, potrebbero esserci altri tipi numerici oltre a quelli che hai citato. Un approccio sarebbe quello di verificare esplicitamente le funzionalità desiderate, con qualcosa come
# 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)
Funziona con tutti i tuoi esempi tranne l'ultimo, numpy.array(['1'])
. Questo perché numpy.ndarray
ha i metodi speciali per le operazioni numeriche ma genera TypeError se si tenta di usarli in modo inappropriato con array di stringhe o oggetti. Puoi aggiungere un controllo esplicito per questo come
... and not (isinstance(obj, ndarray) and obj.dtype.kind in 'OSU')
Questo potrebbe essere abbastanza buono.
Ma ... non puoi mai essere 100% sicuro che qualcuno non definirà un altro tipo con lo stesso comportamento, quindi un modo più infallibile è effettivamente provare a fare un calcolo e catturare il eccezione, qualcosa come
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
ma a seconda della frequenza con cui prevedi di usarlo e con quali argomenti, questo potrebbe non essere pratico (può essere potenzialmente lento, ad esempio con array di grandi dimensioni).
Altri suggerimenti
In generale, il modo flessibile, veloce e pitonico per gestire i tipi sconosciuti è semplicemente eseguire alcune operazioni su di essi e catturare un'eccezione sui tipi non validi.
try:
a = 5+'5'
except TypeError:
print "Oops"
Mi sembra che questo approccio sia più facile che definire alcune funzioni per determinare la certezza assoluta del tipo.
Inoltre, numpy ha numpy.isreal
e altre funzioni simili (numpy.is
+ Tab dovrebbe elencarle).
Hanno tutti i loro divertenti astucci d'angolo, ma uno di questi potrebbe essere utile.
Il tuo is_numeric
è mal definito. Vedi i miei commenti alla tua domanda.
Altri tipi numerici potrebbero essere: long
, complex
, fractions.Fraction
, numpy.bool_
, numpy.ubyte
, ...
operator.isNumberType()
restituisce True
per i numeri Python e numpy.array
.
Da Python 2.6 puoi usare isinstance(d, numbers.Number)
invece di deprecato <=>.
Generalmente è meglio verificare le capacità dell'oggetto (ad es. se è possibile aggiungere un numero intero ad esso) e non il suo tipo.
isinstance(numpy.int32(4), numbers.Number)
restituisce False
, quindi non funziona del tutto. operator.isNumberType()
funziona su tutte le varianti di numeri intorpiditi, tuttavia, compreso numpy.array([1])
.