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.