Conversión de tipo in situ de una matriz NumPy

127

Dada una matriz NumPy de int32, ¿cómo la convierto float32 en su lugar ? Básicamente, me gustaría hacer

a = a.astype(numpy.float32)

sin copiar la matriz. Es grande.

La razón para hacer esto es que tengo dos algoritmos para el cálculo de a. Uno de ellos devuelve una matriz de int32, el otro devuelve una matriz de float32(y esto es inherente a los dos algoritmos diferentes). Todos los cálculos adicionales suponen que aes una matriz de float32.

Actualmente hago la conversión en una función C llamada vía ctypes. ¿Hay alguna manera de hacer esto en Python?

Sven Marnach
fuente
Usar ctypeses tanto "en Python" como usar numpy. :)
Karl Knechtel
3
@Karl: No, porque tengo que codificar y compilar la función C yo mismo.
Sven Marnach
Oh ya veo. Creo que probablemente eres SOL en este caso.
Karl Knechtel
3
@ Andrew: Hay muchas formas de saber si devuelve una copia. Uno de ellos es leer la documentación .
Sven Marnach
1
In situ simplemente significa "usar la misma memoria que la matriz original". Eche un vistazo a la respuesta aceptada: la última parte muestra que los nuevos valores han sobrescrito la misma memoria.
Sven Marnach

Respuestas:

110

Puede crear una vista con un tipo diferente y luego copiar in situ en la vista:

import numpy as np
x = np.arange(10, dtype='int32')
y = x.view('float32')
y[:] = x

print(y)

rendimientos

array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.], dtype=float32)

Para mostrar la conversión estaba en el lugar, nota que la copia de x al yalterada x:

print(x)

huellas dactilares

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])
unutbu
fuente
26
Nota para aquellos (como yo) que desean la conversión entre dtype de diferente tamaño de byte (por ejemplo, 32 a 16 bits): este método falla porque y.size <> x.size. Lógico una vez que lo piensas :-(
Juh_
¿Funcionaba esta solución para alguna versión anterior de Numpy? Cuando lo hago np.arange(10, dtype=np.int32).view(np.float32)en Numpy 1.8.2, me sale array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels
3
@BasSwinckels: Eso se espera. La conversión se produce cuando asigna y[:] = x.
unutbu
para aclarar el punto hecho sobre el tamaño del elemento (número de bits) al que se refiere la respuesta original y @Juh_ por ejemplo: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')este código toma 10 + 10 float32 y da como resultado 10, en lugar de 20 float64
dcanelhas
1
Este cambio en el lugar puede ahorrar en el uso de memoria, pero es más lento que una simple x.astype(float)conversión. No lo recomendaría a menos que su script esté bordeando MemoryError.
hpaulj
158

Actualización: esta función solo evita la copia si puede, por lo tanto, esta no es la respuesta correcta para esta pregunta. La respuesta de unutbu es la correcta.


a = a.astype(numpy.float32, copy=False)

Astype numpy tiene una bandera de copia. ¿Por qué no deberíamos usarlo?

Vikas
fuente
14
Una vez que este parámetro es compatible con una versión de NumPy, podríamos usarlo, pero actualmente solo está disponible en la rama de desarrollo. Y cuando hice esta pregunta, no existía en absoluto.
Sven Marnach
2
@SvenMarnach Ahora es compatible, al menos en mi versión (1.7.1).
PhilMacKay
Parece funcionar perfectamente en python3.3 con la última versión numpy.
CHM
1
Creo que esto es aproximadamente 700 veces más lento que a = a.view ((float, len (a.dtype.names)))
JJ
14
El indicador de copia solo dice que si el cambio se puede hacer sin una copia, se hará sin una copia. Sin embargo, si el tipo es diferente, siempre se copiará.
coderforlife
14

Puede cambiar el tipo de matriz sin convertir así:

a.dtype = numpy.float32

pero primero debe cambiar todos los enteros a algo que se interpretará como el flotante correspondiente. Una forma muy lenta de hacer esto sería usar el structmódulo de Python como este:

def toi(i):
    return struct.unpack('i',struct.pack('f',float(i)))[0]

... aplicado a cada miembro de su matriz.

Pero quizás una forma más rápida sería utilizar las herramientas ctypeslib de numpy (con las que no estoy familiarizado)

- editar -

Como ctypeslib no parece funcionar, entonces procedería con la conversión con el numpy.astypemétodo típico , pero procedería en tamaños de bloque que estén dentro de los límites de su memoria:

a[0:10000] = a[0:10000].astype('float32').view('int32')

... luego cambie el dtype cuando haya terminado.

Aquí hay una función que realiza la tarea para cualquier tipo de dt compatible (solo funciona para dtypes con elementos del mismo tamaño) y maneja matrices de forma arbitraria con control del usuario sobre el tamaño del bloque:

import numpy

def astype_inplace(a, dtype, blocksize=10000):
    oldtype = a.dtype
    newtype = numpy.dtype(dtype)
    assert oldtype.itemsize is newtype.itemsize
    for idx in xrange(0, a.size, blocksize):
        a.flat[idx:idx + blocksize] = \
            a.flat[idx:idx + blocksize].astype(newtype).view(oldtype)
    a.dtype = newtype

a = numpy.random.randint(100,size=100).reshape((10,10))
print a
astype_inplace(a, 'float32')
print a
Pablo
fuente
1
Gracias por tu respuesta. Honestamente, no creo que esto sea muy útil para grandes matrices, es demasiado lento. Reinterpretar los datos de la matriz como un tipo diferente es fácil, por ejemplo, llamando a.view(numpy.float32). La parte difícil es realmente convertir los datos. numpy.ctypeslibsolo ayuda a reinterpretar los datos, no a convertirlos realmente.
Sven Marnach
Okay. No estaba seguro de cuáles eran sus limitaciones de memoria / procesador. Mira mi edición.
Paul
Gracias por la actualización. Hacerlo en bloque es una buena idea, probablemente lo mejor que puede obtener con la interfaz NumPy actual. Pero en este caso, probablemente me atendré a mi solución actual de ctypes.
Sven Marnach
-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

use view () y el parámetro 'dtype' para cambiar la matriz en su lugar.

蒋志强
fuente
El objetivo de la pregunta era convertir los datos en su lugar. Después de corregir el tipo en la última línea int, esta respuesta solo reinterpretaría los datos existentes como un tipo diferente, que no es lo que estaba pidiendo.
Sven Marnach
¿Qué quieres decir? dtype es solo la apariencia de los datos en la memoria, realmente funciona. Sin embargo, en np.astype, el parámetro 'casting' puede controlar el método de conversión predeterminado 'inseguro'.
蒋志强
Sí, estoy de acuerdo con la primera respuesta aceptada. Sin embargo, arr_.astype (new_dtype, copy = False) aún devuelve una matriz recién asignada. Cómo satisfecho las dtype, ordery suboklos requisitos para devolver una copia de la matriz? No lo resuelvo
蒋志强
-5

Utilizar este:

In [105]: a
Out[105]: 
array([[15, 30, 88, 31, 33],
       [53, 38, 54, 47, 56],
       [67,  2, 74, 10, 16],
       [86, 33, 15, 51, 32],
       [32, 47, 76, 15, 81]], dtype=int32)

In [106]: float32(a)
Out[106]: 
array([[ 15.,  30.,  88.,  31.,  33.],
       [ 53.,  38.,  54.,  47.,  56.],
       [ 67.,   2.,  74.,  10.,  16.],
       [ 86.,  33.,  15.,  51.,  32.],
       [ 32.,  47.,  76.,  15.,  81.]], dtype=float32)
Ankit Barik
fuente
55
¿Estás seguro de que no es una copia? ¿Puedes comprobarlo y explicar un poco más?
Michele d'Amico
-5

a = np.subtract(a, 0., dtype=np.float32)

Mio
fuente
1
Si bien este fragmento de código puede ser la solución, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
Sebastialonso el
¿Por qué debería ser una conversión en el lugar ? numpy.subtractestá devolviendo una copia, ¿no? Solo el nombre areutilizado para otra porción de datos ... Por favor explique, si estoy equivocado sobre esto.
koffein
Gracias por señalar esto, parece que está en lo correcto: se produce una copia.
MIO