In-Place-Typkonvertierung eines NumPy-Arrays

127

int32Wie konvertiere ich ein NumPy-Array an float32 Ort und Stelle ? Im Grunde würde ich gerne tun

a = a.astype(numpy.float32)

ohne das Array zu kopieren. Es ist groß.

Der Grund dafür ist, dass ich zwei Algorithmen für die Berechnung von habe a. Einer von ihnen gibt ein Array von zurück int32, der andere ein Array von float32(und dies ist den beiden verschiedenen Algorithmen inhärent). Alle weiteren Berechnungen gehen davon aus, dass aes sich um ein Array von handelt float32.

Derzeit mache ich die Konvertierung in einer C-Funktion namens via ctypes. Gibt es eine Möglichkeit, dies in Python zu tun?

Sven Marnach
quelle
Verwenden ctypesist genauso "in Python" wie Verwenden numpy. :)
Karl Knechtel
3
@ Karl: Nein, weil ich die C-Funktion selbst codieren und kompilieren muss.
Sven Marnach
Oh, ich verstehe. Ich denke, Sie sind wahrscheinlich SOL in diesem Fall.
Karl Knechtel
3
@ Andrew: Es gibt viele Möglichkeiten festzustellen, ob eine Kopie zurückgegeben wird. Eine davon ist das Lesen der Dokumentation .
Sven Marnach
1
In-Place bedeutet einfach "Verwenden des gleichen Speichers wie das ursprüngliche Array". Schauen Sie sich die akzeptierte Antwort an - der letzte Teil zeigt, dass die neuen Werte tatsächlich denselben Speicher überschrieben haben.
Sven Marnach

Antworten:

110

Sie können eine Ansicht mit einem anderen Typ erstellen und dann direkt in die Ansicht kopieren:

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

print(y)

ergibt

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

Beachten Sie, dass das Kopieren von x nach ygeändert wurde , um anzuzeigen, dass die Konvertierung vorhanden war x:

print(x)

druckt

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])
unutbu
quelle
26
Hinweis für diejenigen (wie ich), die eine Konvertierung zwischen dtype unterschiedlicher Bytegröße (z. B. 32 bis 16 Bit) wünschen: Diese Methode schlägt fehl, weil y.size <> x.size. Logisch, wenn Sie einmal darüber nachgedacht haben :-(
Juh_
Funktionierte diese Lösung für eine ältere Version von Numpy? Wenn ich np.arange(10, dtype=np.int32).view(np.float32)auf Numpy 1.8.2 mache, bekomme ich array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels
3
@BasSwinckels: Das wird erwartet. Die Konvertierung erfolgt beim Zuweisen y[:] = x.
Unutbu
Um den Punkt zu verdeutlichen, auf den sich die Artikelgröße (Anzahl der Bits) bezieht, auf die sich die ursprüngliche Antwort und @Juh_ beziehen, z. B.: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Dieser Code benötigt 10 + 10 float32 und ergibt 10 statt 20 float64
dcanelhas
1
Diese direkte Änderung spart möglicherweise Speicherplatz, ist jedoch langsamer als eine einfache x.astype(float)Konvertierung. Ich würde es nicht empfehlen, es sei denn, Ihr Skript grenzt an MemoryError.
Hpaulj
158

Update: Diese Funktion vermeidet das Kopieren nur, wenn dies möglich ist. Daher ist dies nicht die richtige Antwort auf diese Frage. Die Antwort von unutbu ist die richtige.


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

numpy astype hat ein Kopierflag. Warum sollten wir es nicht benutzen?

Vikas
quelle
14
Sobald dieser Parameter in einer NumPy-Version unterstützt wird, können wir ihn natürlich verwenden, aber derzeit ist er nur im Entwicklungszweig verfügbar. Und als ich diese Frage stellte, existierte sie überhaupt nicht.
Sven Marnach
2
@SvenMarnach Es wird jetzt zumindest in meiner Version (1.7.1) unterstützt.
PhilMacKay
Es scheint perfekt in Python3.3 mit der neuesten Numpy-Version zu funktionieren.
CHM
1
Ich finde das ungefähr 700x langsamer als a = a.view ((float, len (a.dtype.names)))
JJ
14
Das Kopierflag besagt nur, dass die Änderung ohne Kopie durchgeführt werden kann, wenn sie ohne Kopie durchgeführt werden kann. Wenn der Typ jedoch anders ist, wird er immer kopiert.
Coderforlife
14

Sie können den Array-Typ ändern, ohne ihn wie folgt zu konvertieren:

a.dtype = numpy.float32

Aber zuerst müssen Sie alle Ganzzahlen in etwas ändern, das als entsprechender Float interpretiert wird. Ein sehr langsamer Weg, dies zu tun, wäre die Verwendung des Python- structModuls wie folgt:

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

... auf jedes Mitglied Ihres Arrays angewendet.

Aber vielleicht wäre ein schnellerer Weg, die ctypeslib-Tools von numpy zu verwenden (mit denen ich nicht vertraut bin).

- bearbeiten -

Da ctypeslib nicht zu funktionieren scheint, würde ich mit der Konvertierung mit der typischen numpy.astypeMethode fortfahren, aber in Blockgrößen vorgehen, die innerhalb Ihrer Speichergrenzen liegen:

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

... dann ändern Sie den dtype, wenn Sie fertig sind.

Hier ist eine Funktion, die die Aufgabe für kompatible dtypes ausführt (funktioniert nur für dtypes mit gleich großen Elementen) und beliebig geformte Arrays mit Benutzersteuerung über die Blockgröße verarbeitet:

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
Paul
quelle
1
Danke für deine Antwort. Ehrlich gesagt denke ich nicht, dass dies für große Arrays sehr nützlich ist - es ist viel zu langsam. Das Neuinterpretieren der Daten des Arrays als einen anderen Typ ist einfach - beispielsweise durch Aufrufen a.view(numpy.float32). Der schwierige Teil ist tatsächlich die Konvertierung der Daten. numpy.ctypeslibHilft nur bei der Neuinterpretation der Daten, nicht bei der tatsächlichen Konvertierung.
Sven Marnach
OK. Ich war mir nicht sicher, was Ihre Speicher- / Prozessorbeschränkungen waren. Siehe meine Bearbeitung.
Paul
Danke für das Update. Blockweise zu arbeiten ist eine gute Idee - wahrscheinlich das Beste, was Sie mit der aktuellen NumPy-Oberfläche erreichen können. Aber in diesem Fall werde ich mich wahrscheinlich an meine aktuelle ctypes-Lösung halten.
Sven Marnach
-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

Verwenden Sie view () und den Parameter 'dtype', um das Array zu ändern.

蒋志强
quelle
Das Ziel der Frage war es , die vorhandenen Daten tatsächlich zu konvertieren . Nach dem Korrigieren des Typs in der letzten Zeile auf intwürde diese Antwort die vorhandenen Daten nur als einen anderen Typ interpretieren, was nicht das ist, wonach ich gefragt habe.
Sven Marnach
Was meinst du? dtype ist nur das Auftreten von Daten im Speicher, es funktioniert wirklich. In np.astype kann der Parameter 'casting' jedoch die Standardeinstellung 'unsicher' für die Konvertierungsmethode steuern.
5.
Ja, ich stimme der ersten akzeptierten Antwort zu. Arr_.astype (new_dtype, copy = False) gibt jedoch weiterhin ein neu zugewiesenes Array zurück. Wie man erfüllt die dtype, orderund subokAnforderungen eine Kopie des Arrays zurück? Ich löse es nicht.
5.
-5

Benutze das:

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
quelle
5
Sind Sie sicher, dass dies keine Kopie ist? Können Sie es überprüfen und ein bisschen mehr erklären?
Michele d'Amico
-5

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

MIO
quelle
1
Während dieses Code-Snippet die Lösung sein kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Sebastialonso
Warum sollte dies eine direkte Konvertierung sein? numpy.subtractgibt eine Kopie zurück, nicht wahr? Nur der Name wird afür einen anderen Datenblock wiederverwendet ... Bitte erklären Sie, wenn ich mich irre.
Koffein
Vielen Dank für den Hinweis, es scheint, dass Sie richtig sind - eine Kopie wird erstellt.
MIO