python: come identificare se una variabile è un array o uno scalare


283

Ho una funzione che accetta l'argomento NBins. Voglio fare una chiamata a questa funzione con uno scalare 50o un array [0, 10, 20, 30]. Come posso identificare all'interno della funzione qual è la lunghezza di NBins? o detto diversamente, se è uno scalare o un vettore?

Ho provato questo:

>>> N=[2,3,5]
>>> P = 5
>>> len(N)
3
>>> len(P)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>> 

Come vedete, non posso applicare lena P, dato che non è una matrice .... C'è qualcosa di simile isarrayo isscalarin Python?

Grazie


3
Hai provato a testarlo type?
Sukrit Kalra,

Risposte:


390
>>> isinstance([0, 10, 20, 30], list)
True
>>> isinstance(50, list)
False

Per supportare qualsiasi tipo di sequenza, selezionare collections.Sequenceinvece di list.

nota : isinstancesupporta anche una tupla di classi, il controllo type(x) in (..., ...)dovrebbe essere evitato e non è necessario.

Puoi anche voler controllare not isinstance(x, (str, unicode))


3
grazie, non immaginavo che l'inversione list
diventasse

3
Sebbene questa sia un'ottima risposta, collections.Sequenceè anche un ABC per stringa, quindi dovrebbe essere preso in considerazione. Sto usando qualcosa del genere if type(x) is not str and isinstance(x, collections.Sequence):. Questo non è eccezionale, ma è affidabile.
bbenne10

2
@ bbenne10 certo, ma evita type, e controlla anche not isinstance(x, (str, unicode))su Python 2
jamylak,

Perché hai detto "controlla il tipo (x) in (..., ...) dovrebbe essere evitato ed è inutile"? Se lo dici tu, sarebbe molto gentile spiegare perché, forse non sono l'unico a chiedermi perché dovrebbe essere evitato.
Olivier Pons


119

Le risposte precedenti presuppongono che l'array sia un elenco standard di Python. Come qualcuno che usa spesso intorpidimento, consiglierei un test molto pitonico di:

if hasattr(N, "__len__")

12
le stringhe hanno un __len__attributo (quindi immagino, non tecnicamente un tipo scalare)
xofer

20
if hasattr(N, '__len__') and (not isinstance(N, str))spiegherebbe correttamente le stringhe.
Tucidide411,

1
Spiega anche il dict su Python 3
Bruno Henrique,

44

Combinando insieme le risposte di @jamylak e @ jpaddison3, se devi essere robusto contro gli array intorpiditi come input e gestirli allo stesso modo degli elenchi, dovresti usare

import numpy as np
isinstance(P, (list, tuple, np.ndarray))

Ciò è efficace contro le sottoclassi di matrici di elenchi, tuple e intorpidimento.

E se vuoi essere robusto anche con tutte le altre sottoclassi di sequenza (non solo elenco e tupla), usa

import collections
import numpy as np
isinstance(P, (collections.Sequence, np.ndarray))

Perché dovresti fare le cose in questo modo isinstancee non confrontarle type(P)con un valore target? Ecco un esempio, in cui creiamo e studiamo il comportamento di NewListuna banale sottoclasse di elenco.

>>> class NewList(list):
...     isThisAList = '???'
... 
>>> x = NewList([0,1])
>>> y = list([0,1])
>>> print x
[0, 1]
>>> print y
[0, 1]
>>> x==y
True
>>> type(x)
<class '__main__.NewList'>
>>> type(x) is list
False
>>> type(y) is list
True
>>> type(x).__name__
'NewList'
>>> isinstance(x, list)
True

Nonostante xe yconfrontando come uguali, gestirli con il typerisultato comporterebbe comportamenti diversi. Tuttavia, poiché xè un'istanza di una sottoclasse di list, using isinstance(x,list)fornisce il comportamento e gli ossequi desiderati xe yallo stesso modo.


Questa è la risposta più adatta alle mie esigenze. Ho appena aggiunto anche set. Perché non voglio essere robusto contro i dadi. isinstance(P, (list, tuple, set, np.ndarray))
Santiago,

32

Esiste un equivalente a isscalar () in numpy? Sì.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True

6
Sarebbe meglio e un esempio: >>> np.isscalar('abcd')ritorna True.
Syrtis Major,

Grazie! questo è un esempio molto più generale di quanto sopra e dovrebbe essere preferito. È anche una risposta diretta alla domanda del PO.
Cristóbal Sifón,

1
Bello. Anche se un gotcha è che isscalar (None) restituisce False. Numpy lo implementa comereturn (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))
Shital Shah

5
No, purtroppo. La numpy.isscalar()funzione presenta una serie di difetti di progettazione inconciliabili e sarà probabilmente deprecata in una futura revisione. Per parafrasare la documentazione ufficiale : "In quasi tutti i casi np.ndim(x) == 0dovrebbe essere usato invece di np.isscaler(x), poiché il primo restituirà correttamente anche true per array 0d." Una valida alternativa compatibile con il futuro numpy.isscalar()sarebbe quindi quella di avvolgere banalmente numpy.ndim(): ad es.def is_scalar(obj): return np.ndim(obj) == 0
Cecil Curry,

In realtà questo non dovrebbe essere votato perché np.isscalarè confuso. Doc ufficiale ha suggerito di utilizzare np.array.ndimovunque, ovvero np.isscalar(np.array(12))False mentre dovrebbe essere considerato scalare poiché np.array(12).ndimè 0.
knh190

17

Mentre l'approccio di @ jamylak è il migliore, ecco un approccio alternativo

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True

2
Sarebbe stato bello se anche la persona che aveva votato per il voto inferiore avesse dato una ragione.
Sukrit Kalra,

ho effettivamente effettuato l'upgrade, ma poi mi sono reso conto che non funziona deos in 2.7: >>> p = [] >>> digita (p) in (elenco) Traceback (ultima chiamata più recente): File "<stdin>" , linea 1, in <modulo>
Oleg Gryb,

@OlegGryb: provare type(p) in (list, ).
Sukrit Kalra,

ah, è una tupla a destra, non un elenco, capito, grazie e ora funziona. Mi dispiace, non posso votare 2 volte - la migliore soluzione finora :)
Oleg Gryb

3

Un altro approccio alternativo (uso della proprietà del nome classe ):

N = [2,3,5]
P = 5

type(N).__name__ == 'list'
True

type(P).__name__ == 'int'
True

type(N).__name__ in ('list', 'tuple')
True

Non è necessario importare nulla.


3

Ecco l'approccio migliore che ho trovato: Verifica l'esistenza di __len__e __getitem__.

Puoi chiedere perché? I motivi includono:

  1. Il metodo popolare isinstance(obj, abc.Sequence)fallisce su alcuni oggetti incluso il Tensore di PyTorch perché non implementano __contains__.
  2. Sfortunatamente, nelle collezioni di Python non c'è nulla che controlli solo __len__e __getitem__che ritengo siano metodi minimi per oggetti simili a array.
  3. Funziona su elenco, tupla, ndarray, tensore ecc.

Quindi senza ulteriori indugi:

def is_array_like(obj, string_is_array=False, tuple_is_array=True):
    result = hasattr(obj, "__len__") and hasattr(obj, '__getitem__') 
    if result and not string_is_array and isinstance(obj, (str, abc.ByteString)):
        result = False
    if result and not tuple_is_array and isinstance(obj, tuple):
        result = False
    return result

Nota che ho aggiunto i parametri predefiniti perché la maggior parte delle volte potresti voler considerare le stringhe come valori, non come array. Allo stesso modo per le tuple.


2
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False

2

È possibile controllare il tipo di dati della variabile.

N = [2,3,5]
P = 5
type(P)

Ti darà messo come tipo di dati di P.

<type 'int'>

In modo che tu possa differenziare che è un numero intero o un array.


2

Sono sorpreso che una domanda così basilare non sembri avere una risposta immediata in Python. Mi sembra che quasi tutte le risposte proposte utilizzino una sorta di controllo del tipo, che di solito non è consigliato in Python e sembrano limitate a un caso specifico (falliscono con tipi numerici diversi o oggetti iterabili generici che non sono tuple o liste).

Per me, ciò che funziona meglio è importare numpy e usare array.size, ad esempio:

>>> a=1
>>> np.array(a)
Out[1]: array(1)

>>> np.array(a).size
Out[2]: 1

>>> np.array([1,2]).size
Out[3]: 2

>>> np.array('125')
Out[4]: 1

Nota anche:

>>> len(np.array([1,2]))

Out[5]: 2

ma:

>>> len(np.array(a))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-40-f5055b93f729> in <module>()
----> 1 len(np.array(a))

TypeError: len() of unsized object

Sono anche sorpreso che nessuno di loro sembri avere a che fare con i generatori.
RhysC,

2

Basta usare sizeinvece di len!

>>> from numpy import size
>>> N = [2, 3, 5]
>>> size(N)
3
>>> N = array([2, 3, 5])
>>> size(N)
3
>>> P = 5
>>> size(P)
1

2
NameError: nome di 'dimensioni' non è definito
thang

1
È vero. Stavo usando le dimensioni intorpidite senza accorgermene. Hai bisogno di: dalla dimensione dell'importazione numpy
Mathieu Villion

2
np.size(5)e np.size([5])sono entrambi == 1, quindi questo non distingue correttamente il tipo (cioè identifica uno scalare), che credo sia l'obiettivo.
michael

Questa è un'osservazione interessante. La domanda originale si riferisce a isscalar, che è una funzione Matlab. In Matlab non c'è assolutamente alcuna differenza tra uno scalare e un array di dimensioni 1, sia esso un vettore o un array N-dim. IMHO, questo è un vantaggio per Matlab.
Mathieu Villion,

0

preds_test [0] is of shape (128.128,1) Consente di verificare il tipo di dati utilizzando la funzione isinstance () che isinstance accetta 2 argomenti. Il primo argomento è dato Il secondo argomento è il tipo di dati isinstance (preds_test [0], np.ndarray) restituisce Output come True. Significa che preds_test [0] è un array.


0

Per rispondere alla domanda nel titolo, un modo diretto per dire se una variabile è uno scalare è provare a convertirla in un float. Se ottieni TypeError, non lo è.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.