Python: cómo identificar si una variable es una matriz o un escalar

283

Tengo una función que toma el argumento NBins. Quiero hacer una llamada a esta función con un escalar 50o una matriz [0, 10, 20, 30]. ¿Cómo puedo identificar dentro de la función, cuál es la longitud de NBins? o dicho de otra manera, si es un escalar o un vector?

Intenté esto:

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

Como se puede ver, no puedo aplicar lena P, ya que no es una matriz .... ¿Hay algo así como isarrayo isscalaren Python?

Gracias

otmezger
fuente
3
¿Has intentado probarlo type?
Sukrit Kalra

Respuestas:

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

Para admitir cualquier tipo de secuencia, marque en collections.Sequencelugar de list.

nota : isinstancetambién admite una tupla de clases, se type(x) in (..., ...)debe evitar la comprobación y es innecesario.

También es posible que desee comprobar not isinstance(x, (str, unicode))

jamylak
fuente
3
gracias, no imaginé invertir listpara obtener falsos para escalares ... gracias
otmezger
3
Si bien esta es una gran respuesta, también collections.Sequencees un ABC para cadena, por lo que debe tenerse en cuenta. Estoy usando algo así if type(x) is not str and isinstance(x, collections.Sequence):. Esto no es genial, pero es confiable.
bbenne10
2
@ bbenne10 seguro, pero evite type, y también verifique not isinstance(x, (str, unicode))Python 2
jamylak
¿Por qué dijiste "el tipo de verificación (x) en (..., ...) debe evitarse y es innecesario"? Si lo dice, sería muy amable explicar por qué, tal vez no soy el único en preguntarse por qué debería evitarse.
Olivier Pons
118

Las respuestas anteriores suponen que la matriz es una lista estándar de Python. Como alguien que usa numpy a menudo, recomendaría una prueba muy pitónica de:

if hasattr(N, "__len__")
jpaddison3
fuente
12
las cadenas tienen un __len__atributo (así que supongo que técnicamente no es un tipo escalar)
xofer
20
if hasattr(N, '__len__') and (not isinstance(N, str))explicaría adecuadamente las cadenas.
Tucídides411
1
También tenga en cuenta dict en Python 3
Bruno Henrique
44

Combinando las respuestas de @jamylak y @ jpaddison3 juntas, si necesita ser robusto frente a matrices numpy como entrada y manejarlas de la misma manera que las listas, debe usar

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

Esto es robusto frente a subclases de listas, tuplas y matrices numpy.

Y si también quiere ser robusto frente a todas las demás subclases de secuencia (no solo lista y tupla), use

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

¿Por qué debería hacer las cosas de esta manera isinstancey no compararlas type(P)con un valor objetivo? Aquí hay un ejemplo, donde hacemos y estudiamos el comportamiento de NewListuna subclase trivial de la lista.

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

A pesar de xy ycomparándolos como iguales, manejarlos con ellos typeresultaría en un comportamiento diferente. Sin embargo, ya que xes una instancia de una subclase de list, utilizando isinstance(x,list)da el comportamiento y trata deseado xy yde la misma manera.

scottclowe
fuente
Esta es la respuesta que mejor se adapta a mis necesidades. Acabo de agregar set, también. Porque no quiero ser robusto contra los dictados. isinstance(P, (list, tuple, set, np.ndarray))
Santiago
32

¿Hay un equivalente a isscalar () en numpy? Si.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
jmhl
fuente
66
Sería mejor y un ejemplo: >>> np.isscalar('abcd')devoluciones True.
Syrtis Major
¡Gracias! Este es un ejemplo mucho más general que cualquiera de los anteriores y debería preferirse. También es una respuesta directa a la pregunta del OP.
Cristóbal Sifón
1
Agradable. Aunque un problema es que isscalar (None) devuelve False. Numpy implementa esto comoreturn (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))
Shital Shah
55
No, tristemente La numpy.isscalar()función sufre una serie de defectos de diseño irreconciliables y probablemente quedará obsoleta en alguna revisión futura. Parafraseando la documentación oficial : "En casi todos los casos se np.ndim(x) == 0debe usar en lugar de np.isscaler(x), ya que el primero también devolverá correctamente verdadero para las matrices 0d". Una sólida alternativa compatible con interés numpy.isscalar()lo tanto, sería trivial para envolver numpy.ndim(): por ejemplo,def is_scalar(obj): return np.ndim(obj) == 0
Cecil Curry
En realidad, esto no debería ser votado porque np.isscalares confuso. El documento oficial sugirió usarlo en np.array.ndimtodas partes, np.isscalar(np.array(12))es decir, es falso, aunque debería considerarse escalar, ya que np.array(12).ndimes 0.
knh190
17

Mientras que el enfoque de @ jamylak es el mejor, aquí hay un enfoque alternativo

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Sukrit Kalra
fuente
2
Hubiera sido genial si la persona que rechazó la respuesta hubiera dado una razón también.
Sukrit Kalra
En realidad he votado, pero luego me di cuenta de que no funciona en 2.7: >>> p = [] >>> escriba (p) en (list) Traceback (última llamada): Archivo "<stdin>" , línea 1, en <módulo>
Oleg Gryb
@OlegGryb: Prueba type(p) in (list, ).
Sukrit Kalra
ah, es una tupla a la derecha, no una lista, lo tengo, gracias y funciona ahora. Lamento, no puedo votar 2 veces, la mejor solución hasta ahora :)
Oleg Gryb
3

Otro enfoque alternativo (uso de la propiedad de nombre de clase ):

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

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

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

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

No es necesario importar nada.

Marek
fuente
3

Aquí está el mejor enfoque que he encontrado: Verifique la existencia de __len__y __getitem__.

Usted puede preguntar por qué? Los motivos incluyen:

  1. El método popular isinstance(obj, abc.Sequence)falla en algunos objetos, incluido el Tensor de PyTorch porque no se implementan __contains__.
  2. Desafortunadamente, no hay nada en las colecciones.abc de Python que verifique solo __len__y __getitem__que considero que son métodos mínimos para objetos tipo matriz.
  3. Funciona en lista, tupla, ndarray, tensor, etc.

Así que sin más preámbulos:

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

Tenga en cuenta que he agregado parámetros predeterminados porque la mayoría de las veces es posible que desee considerar las cadenas como valores, no como matrices. Del mismo modo para las tuplas.

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

Puede verificar el tipo de datos de la variable.

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

Le dará salida como tipo de datos de P.

<type 'int'>

Para que pueda diferenciar que es un entero o una matriz.

unnati patil
fuente
2

Me sorprende que una pregunta tan básica no parezca tener una respuesta inmediata en Python. Me parece que casi todas las respuestas propuestas usan algún tipo de verificación de tipo, que generalmente no se recomienda en python y parecen restringidas a un caso específico (fallan con diferentes tipos numéricos u objetos genéricos iterables que no son tuplas o listas).

Para mí, lo que funciona mejor es importar numpy y usar array.size, por ejemplo:

>>> 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 también:

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

Out[5]: 2

pero:

>>> 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
Vincenzooo
fuente
También me sorprende que ninguno de ellos parezca tratar con generadores tampoco.
RhysC
2

Simplemente use en sizelugar de 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
Mathieu Villion
fuente
2
NameError: nombre de 'tamaño' no está definido
Thang
1
Es verdad. Estaba usando un tamaño numpy sin notarlo. Necesitas: desde tamaño de importación numpy
Mathieu Villion
2
np.size(5)y np.size([5])son ambos == 1, por lo que esto no distingue correctamente el tipo (es decir, identifica un escalar), que creo que es el objetivo.
michael
Este es un comentario interesante. La pregunta original se refiere a isscalar, que es una función de Matlab. En Matlab, no hay absolutamente ninguna diferencia entre un escalar y una matriz de tamaño 1, ya sea un vector o una matriz N-dim. En mi humilde opinión, esta es una ventaja para Matlab.
Mathieu Villion
0

preds_test [0] tiene forma (128,128,1) Permite verificar su tipo de datos usando la función isinstance () La función isinstance toma 2 argumentos. El primer argumento es datos. El segundo argumento es tipo de datos isinstance (preds_test [0], np.ndarray) da la salida como Verdadero. Significa que preds_test [0] es una matriz.

Sumanth Meenan
fuente
0

Para responder la pregunta en el título, una forma directa de saber si una variable es escalar es intentar convertirla en flotante. Si lo consigues TypeError, no lo es.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
Disco
fuente