Convertir dtypes numpy a tipos nativos de python

238

Si tengo un tipo de letra numpy, ¿cómo lo convierto automáticamente al tipo de datos de Python más cercano? Por ejemplo,

numpy.float32 -> "python float"
numpy.float64 -> "python float"
numpy.uint32  -> "python int"
numpy.int16   -> "python int"

Podría intentar llegar a un mapeo de todos estos casos, pero ¿proporciona numpy alguna forma automática de convertir sus tipos en los tipos de python nativos más cercanos posibles? Este mapeo no necesita ser exhaustivo, pero debe convertir los tipos comunes que tienen un análogo de Python cercano. Creo que esto ya sucede en algún lugar numpy.

conradlee
fuente

Respuestas:

325

Use val.item()para convertir la mayoría de los valores de NumPy a un tipo nativo de Python:

import numpy as np

# for example, numpy.float32 -> python float
val = np.float32(0)
pyval = val.item()
print(type(pyval))         # <class 'float'>

# and similar...
type(np.float64(0).item()) # <class 'float'>
type(np.uint32(0).item())  # <class 'long'>
type(np.int16(0).item())   # <class 'int'>
type(np.cfloat(0).item())  # <class 'complex'>
type(np.datetime64(0, 'D').item())  # <class 'datetime.date'>
type(np.datetime64('2001-01-01 00:00:00').item())  # <class 'datetime.datetime'>
type(np.timedelta64(0, 'D').item()) # <class 'datetime.timedelta'>
...

(Otro método es np.asscalar(val), sin embargo, está en desuso desde NumPy 1.16).


Para los curiosos, para construir una tabla de conversiones de escalares de matriz NumPy para su sistema:

for name in dir(np):
    obj = getattr(np, name)
    if hasattr(obj, 'dtype'):
        try:
            if 'time' in name:
                npn = obj(0, 'D')
            else:
                npn = obj(0)
            nat = npn.item()
            print('{0} ({1!r}) -> {2}'.format(name, npn.dtype.char, type(nat)))
        except:
            pass

Hay unos pocos tipos NumPy que no tienen equivalente nativa de Python en algunos sistemas, entre ellos: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoubley longfloat. Estos deben convertirse a su equivalente NumPy más cercano antes de usar .item().

Mike T
fuente
Estoy usando pandas (0.23.0). Al menos para esa versión, np.str no tiene el método .item (), por lo que la única forma que vi fue envolver .item () dentro de un bloque try.
Robert Lugg
3
@RobertLugg np.strno es un tipo Numpy, es decir np.str is str, es solo un alias para un tipo Python estándar. Lo mismo con np.float, np.int, np.bool, np.complex, y np.object. Los tipos Numpy tienen un final _, por ejemplo np.str_.
Mike T
Entiendo. Entonces, el problema es "sería bueno si" pudiera hacer: np.float64(0).item()y también np.float(0).item(). En otras palabras, para los casos en que se sabe qué hacer, respalde el .item()método incluso si simplemente devuelve el mismo valor. De esa manera podría aplicar .item()en escalares mucho más numpy sin carcasa especial. Tal como están las cosas, los conceptos aparentemente paralelos difieren debido a la implementación subyacente. Entiendo totalmente por qué se hizo esto. Pero es una molestia para el usuario de la biblioteca.
Robert Lugg
45

Me encontré con un conjunto mixto de tipos numpy y python estándar. como se derivan todos los tipos numpy numpy.generic, así es como puede convertir todo a tipos estándar de python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
fuente
55
Como señala la respuesta aceptada , NumPy 1.16 desaprobó el np.asscalar()método. ¿Por qué? Probablemente sin una razón apreciable. A pesar de una década de relativa estabilidad, la API NumPy es ahora un objetivo móvil inestable que exige un mantenimiento constante de las aplicaciones posteriores. Al menos nos dejaron el item()método ... por ahora.
Cecil Curry
El método asscalar se ha depreciado desde v1.6 de numpy
Eswar
Puede reemplazar fácilmente la respuesta con if isinstance(o, numpy.generic): return o.item() raise TypeErrory se convierte en una respuesta no obsoleta nuevamente: D
Buggy
19

Si desea convertir (numpy.array O numpy scalar O tipo nativo O numpy.darray) A tipo nativo, simplemente puede hacer:

converted_value = getattr(value, "tolist", lambda: value)()

tolist convertirá su escalar o matriz al tipo nativo de python. La función lambda predeterminada se encarga del caso donde el valor ya es nativo.

v.thorey
fuente
2
Enfoque más limpio para los tipos mixtos (nativos y no nativos), ¡bien hecho! Y para aquellos que se preguntan, sí, tolist solo devuelve un único valor (el escalar) cuando lo está llamando a un solo valor, no a una lista como podría pensar. Vale la pena señalar que la forma más simple de escribir el lambda es lambda: valueque no queremos ninguna entrada.
fgblomqvist
getattr¡+ tolistcombo no solo es universal, sino incluso vectorizado! (unlinke .item ())
mirekphd
11

Qué tal si:

In [51]: dict([(d, type(np.zeros(1,d).tolist()[0])) for d in (np.float32,np.float64,np.uint32, np.int16)])
Out[51]: 
{<type 'numpy.int16'>: <type 'int'>,
 <type 'numpy.uint32'>: <type 'long'>,
 <type 'numpy.float32'>: <type 'float'>,
 <type 'numpy.float64'>: <type 'float'>}
unutbu
fuente
1
Menciono ese tipo de solución como una posibilidad al final de mi pregunta. Pero estoy buscando una solución sistemática en lugar de una codificada que solo cubra algunos de los casos. Por ejemplo, si numpy agrega más tipos de letra en el futuro, su solución se rompería. Entonces no estoy contento con esa solución.
conradlee
El número de tipos posibles es ilimitado. Considere np.dtype('mint8')para cualquier entero positivo m. No puede haber un mapeo exhaustivo. (Tampoco creo que haya una función integrada para hacer esta conversión por usted. Podría estar equivocado, pero no lo creo :))
unutbu
2
Python asigna dtypes numpy a tipos de python, no estoy seguro de cómo, pero me gustaría usar cualquier método que hagan. Creo que esto debe suceder para permitir, por ejemplo, la multiplicación (y otras operaciones) entre dtypes numpy y tipos de python. Supongo que su método no mapea exhaustivamente todos los tipos de numpy posibles, pero al menos los más comunes donde tiene sentido.
conradlee
No funciona de forma coherente: >>> print([numpy.asscalar(x) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.6499999999999999, 0.6, 0.55, 0.5, 0.44999999999999996, 0.3999999999999999, 0.35, 0.29999999999999993, 0.25, 0.19999999999999996, 0.1499999999999999, 0.09999999999999998, 0.04999999999999993, 0.0]como puede ver, no todos los valores se convirtieron correctamente.
Alex F
siguiendo mi comentario anterior, extrañamente este funciona, aunque hubiera pensado que necesitarías poner la ronda en el tipo nativo de Python en lugar del tipo nativo de Numpy: >>> print([numpy.asscalar(round(x,2)) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1, 0.05, 0.0]
Alex F
9

tolist()Es un enfoque más general para lograr esto. Funciona en cualquier dtype primitivo y también en matrices o matrices.

En realidad, no produce una lista si se llama desde tipos primitivos:

numpy == 1.15.2

>>> import numpy as np

>>> np_float = np.float64(1.23)
>>> print(type(np_float), np_float)
<class 'numpy.float64'> 1.23

>>> listed_np_float = np_float.tolist()
>>> print(type(listed_np_float), listed_np_float)
<class 'float'> 1.23

>>> np_array = np.array([[1,2,3.], [4,5,6.]])
>>> print(type(np_array), np_array)
<class 'numpy.ndarray'> [[1. 2. 3.]
 [4. 5. 6.]]

>>> listed_np_array = np_array.tolist()
>>> print(type(listed_np_array), listed_np_array)
<class 'list'> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Carlos Santos
fuente
8

También puede llamar al item()método del objeto que desea convertir:

>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>
Aryeh Leib Taurog
fuente
6

Creo que puedes escribir la función de conversión de tipo general de esta manera:

import numpy as np

def get_type_convert(np_type):
   convert_type = type(np.zeros(1,np_type).tolist()[0])
   return (np_type, convert_type)

print get_type_convert(np.float32)
>> (<type 'numpy.float32'>, <type 'float'>)

print get_type_convert(np.float64)
>> (<type 'numpy.float64'>, <type 'float'>)

Esto significa que no hay listas fijas y su código se escalará con más tipos.

Matt Alcock
fuente
¿Sabe dónde está el código fuente para la parte del método tolist () que asigna tipos numpy a tipos python? Eché un vistazo rápido pero no pude encontrarlo.
conradlee
Esto es un truco, lo que estoy haciendo es generar un numpy.ndarray1 con cero zeros()y usar la ndarrays tolist()función para convertir a tipos nativos. Una vez en los tipos nativos, pido el tipo y lo devuelvo. tolist()es una función delndarray
Matt Alcock
Sí, veo que funciona para lo que quiero, así que acepté tu solución. Pero me pregunto cómo tolist () hace su trabajo para decidir en qué tipo de fundición, y no estoy seguro de cómo encontrar la fuente.
conradlee
numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 es donde se documenta la función. Pensé que inspeccionar podría ayudar a encontrar más información, pero no era una alegría. El siguiente paso intenté clonar github.com/numpy/numpy.git y ejecutar grep -r 'tolist' numpy. (¡todavía en progreso, numpy es enorme!)
Matt Alcock
3

numpy guarda esa información en un mapeo expuesto typeDictpara que pueda hacer algo como lo siguiente ::

>>> import __builtin__
>>> import numpy as np
>>> {v: k for k, v in np.typeDict.items() if k in dir(__builtin__)}
{numpy.object_: 'object',
 numpy.bool_: 'bool',
 numpy.string_: 'str',
 numpy.unicode_: 'unicode',
 numpy.int64: 'int',
 numpy.float64: 'float',
 numpy.complex128: 'complex'}

Si desea los tipos de python reales en lugar de sus nombres, puede hacer ::

>>> {v: getattr(__builtin__, k) for k, v in np.typeDict.items() if k in vars(__builtin__)}
{numpy.object_: object,
 numpy.bool_: bool,
 numpy.string_: str,
 numpy.unicode_: unicode,
 numpy.int64: int,
 numpy.float64: float,
 numpy.complex128: complex}
Meitham
fuente
3

Lamento llegar tarde a la parte, pero estaba viendo un problema de conversión numpy.float64a Python normal floatsolamente. Vi 3 formas de hacerlo:

  1. npValue.item()
  2. npValue.astype(float)
  3. float(npValue)

Aquí están los tiempos relevantes de IPython:

In [1]: import numpy as np

In [2]: aa = np.random.uniform(0, 1, 1000000)

In [3]: %timeit map(float, aa)
10 loops, best of 3: 117 ms per loop

In [4]: %timeit map(lambda x: x.astype(float), aa)
1 loop, best of 3: 780 ms per loop

In [5]: %timeit map(lambda x: x.item(), aa)
1 loop, best of 3: 475 ms per loop

Parece que float(npValue)parece mucho más rápido.

gt6989b
fuente
1

Mi enfoque es un poco contundente, pero parece ser bueno para todos los casos:

def type_np2py(dtype=None, arr=None):
    '''Return the closest python type for a given numpy dtype'''

    if ((dtype is None and arr is None) or
        (dtype is not None and arr is not None)):
        raise ValueError(
            "Provide either keyword argument `dtype` or `arr`: a numpy dtype or a numpy array.")

    if dtype is None:
        dtype = arr.dtype

    #1) Make a single-entry numpy array of the same dtype
    #2) force the array into a python 'object' dtype
    #3) the array entry should now be the closest python type
    single_entry = np.empty([1], dtype=dtype).astype(object)

    return type(single_entry[0])

Uso:

>>> type_np2py(int)
<class 'int'>

>>> type_np2py(np.int)
<class 'int'>

>>> type_np2py(str)
<class 'str'>

>>> type_np2py(arr=np.array(['hello']))
<class 'str'>

>>> type_np2py(arr=np.array([1,2,3]))
<class 'int'>

>>> type_np2py(arr=np.array([1.,2.,3.]))
<class 'float'>
Simon Streicher
fuente
Veo que esto es esencialmente lo mismo que la respuesta de Matt Alcock.
Simon Streicher
1

Una nota al margen sobre los escalares de matriz para aquellos que no necesitan conversión automática y conocen el tipo numpy del valor:

Los escalares de matriz difieren de los escalares de Python, pero en su mayor parte se pueden usar indistintamente (la excepción principal es para versiones de Python anteriores a v2.x, donde los escalares de matriz entera no pueden actuar como índices para listas y tuplas). Hay algunas excepciones, como cuando el código requiere atributos muy específicos de un escalar o cuando verifica específicamente si un valor es un escalar de Python. En general, los problemas se solucionan fácilmente mediante la conversión explícita de escalares de matriz a escalares de Python, utilizando la función de tipo Python correspondiente (por ejemplo, int, float, complex, str, unicode).

Fuente

Por lo tanto, para la mayoría de los casos, la conversión podría no ser necesaria en absoluto, y el escalar de matriz podría usarse directamente. El efecto debe ser idéntico al uso del escalar Python:

>>> np.issubdtype(np.int64, int)
True
>>> np.int64(0) == 0
True
>>> np.issubdtype(np.float64, float)
True
>>> np.float64(1.1) == 1.1
True

Pero si, por alguna razón, se necesita la conversión explícita, usar la función incorporada de Python correspondiente es el camino a seguir. Como se muestra en la otra respuesta, también es más rápido que el item()método escalar de matriz .

wombatonfire
fuente
0

Traduzca el ndarray completo en lugar de un objeto de datos de unidad:

def trans(data):
"""
translate numpy.int/float into python native data type
"""
result = []
for i in data.index:
    # i = data.index[0]
    d0 = data.iloc[i].values
    d = []
    for j in d0:
        if 'int' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        elif 'float' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        else:
            res = j
        d.append(res)
    d = tuple(d)
    result.append(d)
result = tuple(result)
return result

Sin embargo, lleva algunos minutos manejar grandes marcos de datos. También estoy buscando una solución más eficiente. Espero una mejor respuesta.

Qinhong Ma
fuente