Conversione del tipo sul posto di un array NumPy


127

Dato un array NumPy di int32, come posso convertirlo sul float32 posto ? Quindi, sostanzialmente, vorrei fare

a = a.astype(numpy.float32)

senza copiare l'array. È grande.

Il motivo per questo è che ho due algoritmi per il calcolo di a. Uno di essi restituisce un array di int32, l'altro restituisce un array di float32(e questo è inerente ai due diversi algoritmi). Tutti gli ulteriori calcoli presuppongono che asia un array di float32.

Attualmente eseguo la conversione in una funzione C chiamata via ctypes. C'è un modo per farlo in Python?


Usare ctypesè tanto "in Python" quanto usare numpy. :)
Karl Knechtel l'

3
@Karl: No, perché devo codificare e compilare la funzione C da solo.
Sven Marnach,

Oh, capisco. Penso che tu sia probabilmente SOL su questo.
Karl Knechtel,

3
@Andrew: ci sono molti modi per sapere se restituisce una copia. Uno di questi è leggere la documentazione .
Sven Marnach,

1
Sul posto significa semplicemente "usare la stessa memoria dell'array originale". Dai un'occhiata alla risposta accettata: l'ultima parte mostra che i nuovi valori hanno effettivamente sovrascritto la stessa memoria.
Sven Marnach,

Risposte:


110

È possibile creare una vista con un tipo diverso, quindi copiarla sul posto nella vista:

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

print(y)

i rendimenti

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

Per mostrare la conversione era sul posto, si noti che la copia da x a yalterato x:

print(x)

stampe

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])

26
Nota per quelli (come me) che desiderano la conversione tra tipi di byte di dimensioni diverse (es. Da 32 a 16 bit): questo metodo non riesce perché y.size <> x.size. Logico una volta che ci pensi :-(
Ju_12

Questa soluzione funzionava con alcune versioni precedenti di Numpy? Quando faccio np.arange(10, dtype=np.int32).view(np.float32)su Numpy 1.8.2, ottengo array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels,

3
@BasSwinckels: è previsto. La conversione si verifica quando si assegna y[:] = x.
unutbu,

per chiarire il punto sollevato in merito alla dimensione degli elementi (numero di bit) a cui fa riferimento la risposta originale e @Juh_ ad esempio: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')questo codice accetta 10 + 10 float32 e risulta in 10, anziché in 20 float64
dcanelhas

1
Questa modifica sul posto può risparmiare sull'utilizzo della memoria, ma è più lenta di una semplice x.astype(float)conversione. Non lo consiglierei a meno che il tuo script non sia al limite di MemoryError.
hpaulj,

158

Aggiornamento: questa funzione evita la copia solo se possibile, quindi questa non è la risposta corretta a questa domanda. la risposta di unutbu è quella giusta.


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

il tipo numpy ha un flag di copia. Perché non dovremmo usarlo?


14
Una volta che questo parametro è supportato in una versione di NumPy, possiamo ovviamente usarlo, ma attualmente è disponibile solo nel ramo di sviluppo. E quando ho fatto questa domanda, non esisteva affatto.
Sven Marnach,

2
@SvenMarnach Ora è supportato, almeno nella mia versione (1.7.1).
PhilMacKay,

Sembra funzionare perfettamente in python3.3 con l'ultima versione numpy.
CHM,

1
Trovo che questo sia circa 700x più lento di a = a.view ((float, len (a.dtype.names)))
JJ

14
Il flag di copia indica solo che se la modifica può essere eseguita senza una copia, verrà eseguita senza una copia. Tuttavia, se il tipo è diverso, verrà comunque sempre copiato.
coderforlife,

14

È possibile modificare il tipo di array senza convertire in questo modo:

a.dtype = numpy.float32

ma prima devi cambiare tutti i numeri interi in qualcosa che verrà interpretato come il float corrispondente. Un modo molto lento per farlo sarebbe usare il structmodulo di Python in questo modo:

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

... applicato a ciascun membro dell'array.

Ma forse un modo più veloce sarebbe quello di utilizzare gli strumenti ctypeslib di numpy (di cui non ho familiarità)

- modificare -

Dal momento che ctypeslib sembra non funzionare, quindi procederei con la conversione con il numpy.astypemetodo tipico , ma procederò in blocchi di dimensioni che rientrano nei limiti di memoria:

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

... quindi, quando hai finito, cambia il tipo.

Ecco una funzione che compie l'attività per tutti i tipi compatibili (funziona solo per i tipi con elementi della stessa dimensione) e gestisce array di forma arbitraria con controllo dell'utente sulla dimensione del blocco:

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

1
Grazie per la tua risposta. Onestamente, non penso che questo sia molto utile per le grandi matrici - è troppo lento. Reinterpretare i dati dell'array in un tipo diverso è facile, ad esempio chiamando a.view(numpy.float32). La parte difficile è in realtà la conversione dei dati. numpy.ctypeslibaiuta solo a reinterpretare i dati, non a convertirli effettivamente.
Sven Marnach,

ok. Non ero sicuro di quali fossero i tuoi limiti di memoria / processore. Vedi la mia modifica.
Paul,

Grazie per l'aggiornamento. Farlo in senso antiorario è una buona idea - probabilmente il meglio che puoi ottenere con l'attuale interfaccia NumPy. Ma in questo caso, probabilmente mi atterrò alla mia attuale soluzione di tipo.
Sven Marnach,

-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

usa view () e il parametro 'dtype' per cambiare l'array in posizione.


L'obiettivo della domanda era effettivamente convertire i dati in atto. Dopo aver corretto il tipo nell'ultima riga int, questa risposta reinterpreterebbe i dati esistenti solo come un tipo diverso, che non è quello che stavo chiedendo.
Sven Marnach,

cosa intendi? dtype è solo l'aspetto dei dati in memoria, funziona davvero. Tuttavia, in np.astype, il parametro 'casting' può controllare il metodo di conversione predefinito 'non sicuro'.
蒋志强

Sì, sono d'accordo con la prima risposta accettata. Tuttavia arr_.astype (new_dtype, copy = False) restituisce comunque un array appena allocato. Come soddisfatto i dtype, ordere subokrequisiti per restituire una copia di array? Non lo risolvo.
蒋志强

-5

Usa questo:

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)

5
Sei sicuro che non sia una copia? Puoi controllarlo e spiegare un po 'di più?
Michele d'Amico,

-5

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


1
Sebbene questo frammento di codice possa essere la soluzione, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Sebastialonso,

Perché questa dovrebbe essere una conversione sul posto ? numpy.subtractsta restituendo una copia, no? Solo il nome ariutilizzato per un altro blocco di dati ... Spiegare, se sbaglio su questo.
Koffein,

Grazie per averlo sottolineato, sembra che tu abbia ragione: ne viene prodotta una copia.
MIO
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.