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?


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])

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?


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

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.


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)

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)


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.