¿Cómo identificar tipos numpy en Python?

100

¿Cómo se puede determinar de manera confiable si un objeto tiene un tipo numérico?

Me doy cuenta de que esta pregunta va en contra de la filosofía de la escritura de pato, pero la idea es asegurarse de que una función (que usa scipy y numpy) nunca devuelva un tipo numpy a menos que se llame con un tipo numpy. Esto surge en la solución a otra pregunta, pero creo que el problema general de determinar si un objeto tiene un tipo numpy está lo suficientemente lejos de la pregunta original como para separarlos.

Douglas B. Staple
fuente
Una pregunta: si usted (o, digamos, scipy) define un tipo que subclasifica un tipo numpy, ¿eso debería contar o no? (Creo que no puede subclasificar tipos numpy en Python, pero puede hacerlo en un módulo C, y creo que también puede subclasificar tipos numpypy en PyPy ... así que probablemente no importe, pero no es inconcebible que pueda hacerlo).
abarnert
No había pensado en eso; Básicamente, su comentario señala que la pregunta es más difícil de lo esperado. Honestamente, ese tipo de consideración de alto nivel es demasiado para mi situación. Para la respuesta general y portátil, diría que siempre que se defina el comportamiento, está bien.
Douglas B. Staple

Respuestas:

116

Use la typefunción incorporada para obtener el tipo, luego puede usar la __module__propiedad para averiguar dónde se definió:

>>> import numpy as np
a = np.array([1, 2, 3])
>>> type(a)
<type 'numpy.ndarray'>
>>> type(a).__module__
'numpy'
>>> type(a).__module__ == np.__name__
True
abarnert
fuente
¿Por ejemplo, numpy.ma.MaskedArray no es un tipo suficientemente numpy?
panda-34
Si quieres algo en numpy. * Simplemente recorre el paquete principal del módulo. (En ese punto, obviamente desea envolverlo en una función). Y si desea que Pandas DataFrames cuente como numpyish, agregue un o para probar eso. Y así. El punto es que tienes que saber qué estás pidiendo realmente cuando quieres hacer algo tan inusual como el cambio de tipo manual suelto, pero una vez que lo sabes, es fácil de implementar.
abarnert
1
Esta solución parece muy poco pitónica y se basa en atributos ocultos. ¿Pero tal vez sea solo cuestión de gustos?
j08lue
2
@ j08lue No son atributos ocultos, son atributos especiales documentados. Sin embargo, no es pitónico, pero creo que eso es inherente al problema. (Y creo que es una de las fortalezas de Python que cuando quieres hacer algo que el lenguaje desalienta, la mejor solución suele ser lo suficientemente fea como para decir que estás haciendo algo que normalmente es una mala idea)
abarnert
69

La solución que se me ocurrió es:

isinstance(y, (np.ndarray, np.generic) )

Sin embargo, no está 100% claro si se garantiza que todos los tipos de numpy sean np.ndarrayo np.generic, y esta probablemente no sea una versión sólida.

Douglas B. Staple
fuente
1
Supongo que podría filtrar dir(numpy)por tipos y funciones integradas (y clases, pero no creo que tenga ninguna) y usar eso para generar una tupla isinstancecontra, lo que sería robusto. (Creo que se puede pasar incorporado funciones de isinstance si están realmente constructores de tipo o no, pero que tendría que comprobar que.)
abarnert
Sí, todos deberían ser subclases de esos dos AFAIK.
Seberg
@seberg Gracias. Ciertamente, parece ser el caso por ahora, pero la documentación de Python no es muy clara al respecto y posiblemente podría cambiar en el futuro.
Douglas B. Staple
19

Pregunta antigua, pero se me ocurrió una respuesta definitiva con un ejemplo. No está de más mantener las preguntas frescas ya que tuve el mismo problema y no encontré una respuesta clara. La clave es asegurarse de haber numpyimportado y luego ejecutar el isinstancebool. Si bien esto puede parecer simple, si está haciendo algunos cálculos en diferentes tipos de datos, esta pequeña verificación puede servir como una prueba rápida antes de comenzar alguna operación vectorizada numpy.

##################
# important part!
##################

import numpy as np

####################
# toy array for demo
####################

arr = np.asarray(range(1,100,2))

########################
# The instance check
######################## 

isinstance(arr,np.ndarray)
Linwoodc3
fuente
9

De hecho, eso depende de lo que estés buscando.

  • Si desea probar si una secuencia es realmente a ndarray, isinstance(..., np.ndarray)probablemente a sea la más fácil. Asegúrese de no volver a cargar numpy en segundo plano, ya que el módulo puede ser diferente, pero de lo contrario, debería estar bien. MaskedArrays, matrix, recarraySon todas las subclases de ndarray, por lo que debe ser fijado.
  • Si desea probar si un escalar es un escalar numpy, las cosas se complican un poco. Puede comprobar si tiene shapeun dtypeatributo y un atributo. Puede compararlo dtypecon los dtypes básicos, cuya lista puede encontrar en np.core.numerictypes.genericTypeRank. Tenga en cuenta que los elementos de esta lista son cadenas, por lo que tendría que hacer un tested.dtype is np.dtype(an_element_of_the_list)...
Pierre GM
fuente
+1. Si realmente está buscando algo además de "es un numpytipo" y puede definir qué es ese algo, esto es mejor que las otras respuestas. Y en la mayoría de los casos, debería buscar algo específico que pueda definir.
abarnert
8

Para obtener el tipo, use la typefunción incorporada . Con el inoperador, puede probar si el tipo es un tipo numérico comprobando si contiene la cadena numpy;

In [1]: import numpy as np

In [2]: a = np.array([1, 2, 3])

In [3]: type(a)
Out[3]: <type 'numpy.ndarray'>

In [4]: 'numpy' in str(type(a))
Out[4]: True

(Este ejemplo se ejecutó en IPython , por cierto. Muy útil para uso interactivo y pruebas rápidas).

Roland Smith
fuente
2
Esto funciona, pero si define un tipo llamado, digamos, "numpygroup", obtendrá falsos positivos. Además, depender de la representación de cadena de los tipos es una mala idea si puede evitarlo, y en este caso, puede hacerlo. En su lugar, mire su módulo.
abarnert
Usar el módulo es de hecho una mejor solución.
Roland Smith
Regex podría usarse
omkaartg
@ Omkaar.K Regex podría usarse para qué? ¿Hacer exactamente la misma verificación de una manera un poco más complicada?
abarnert
@abamert "Podría" es lo que dije, también regex podría parecer complicado para tareas simples como estas, pero es extremadamente útil para tareas de procesamiento de cadenas grandes, por lo que no es una mala idea aprenderlo. Supongo que ya lo sabe, ya que su cartera lo retrata como un programador senior.
omkaartg
3

Tenga en cuenta que type(numpy.ndarray)es un typesí mismo y tenga cuidado con los tipos booleanos y escalares. No se desanime demasiado si no es intuitivo o fácil, es un dolor al principio.

Véase también: - https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.dtypes.html - https://github.com/machinalis/mypy-data/tree/master/numpy- mypy

>>> import numpy as np
>>> np.ndarray
<class 'numpy.ndarray'>
>>> type(np.ndarray)
<class 'type'>
>>> a = np.linspace(1,25)
>>> type(a)
<class 'numpy.ndarray'>
>>> type(a) == type(np.ndarray)
False
>>> type(a) == np.ndarray
True
>>> isinstance(a, np.ndarray)
True

Diversión con booleanos:

>>> b = a.astype('int32') == 11
>>> b[0]
False
>>> isinstance(b[0], bool)
False
>>> isinstance(b[0], np.bool)
False
>>> isinstance(b[0], np.bool_)
True
>>> isinstance(b[0], np.bool8)
True
>>> b[0].dtype == np.bool
True
>>> b[0].dtype == bool  # python equivalent
True

Más diversión con tipos escalares, consulte: - https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.scalars.html#arrays-scalars-built-in

>>> x = np.array([1,], dtype=np.uint64)
>>> x[0].dtype
dtype('uint64')
>>> isinstance(x[0], np.uint64)
True
>>> isinstance(x[0], np.integer)
True  # generic integer
>>> isinstance(x[0], int)
False  # but not a python int in this case

# Try matching the `kind` strings, e.g.
>>> np.dtype('bool').kind                                                                                           
'b'
>>> np.dtype('int64').kind                                                                                          
'i'
>>> np.dtype('float').kind                                                                                          
'f'
>>> np.dtype('half').kind                                                                                           
'f'

# But be weary of matching dtypes
>>> np.integer
<class 'numpy.integer'>
>>> np.dtype(np.integer)
dtype('int64')
>>> x[0].dtype == np.dtype(np.integer)
False

# Down these paths there be dragons:

# the .dtype attribute returns a kind of dtype, not a specific dtype
>>> isinstance(x[0].dtype, np.dtype)
True
>>> isinstance(x[0].dtype, np.uint64)
False  
>>> isinstance(x[0].dtype, np.dtype(np.uint64))
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types
# yea, don't go there
>>> isinstance(x[0].dtype, np.int_)
False  # again, confusing the .dtype with a specific dtype


# Inequalities can be tricky, although they might
# work sometimes, try to avoid these idioms:

>>> x[0].dtype <= np.dtype(np.uint64)
True
>>> x[0].dtype <= np.dtype(np.float)
True
>>> x[0].dtype <= np.dtype(np.half)
False  # just when things were going well
>>> x[0].dtype <= np.dtype(np.float16)
False  # oh boy
>>> x[0].dtype == np.int
False  # ya, no luck here either
>>> x[0].dtype == np.int_
False  # or here
>>> x[0].dtype == np.uint64
True  # have to end on a good note!
Darren Weber
fuente