Konwersja typu w miejscu tablicy NumPy


129

Biorąc pod uwagę tablicę NumPy int32, jak przekonwertować ją na float32 miejsce ? Więc zasadniczo chciałbym to zrobić

a = a.astype(numpy.float32)

bez kopiowania tablicy. To jest duże.

Powodem tego jest to, że mam dwa algorytmy do obliczania a. Jeden z nich zwraca tablicę int32, a drugi tablicę float32(i jest to nieodłączne dla dwóch różnych algorytmów). Wszystkie dalsze obliczenia zakładają, że ajest to tablica float32.

Obecnie wykonuję konwersję w funkcji C o nazwie via ctypes. Czy można to zrobić w Pythonie?


Używanie ctypesjest tak samo „w Pythonie”, jak używanie numpy. :)
Karl Knechtel

3
@Karl: Nie, ponieważ sam muszę kodować i kompilować funkcję C.
Sven Marnach

Rozumiem. Myślę, że prawdopodobnie jesteś SOL w tej sprawie.
Karl Knechtel

4
@Andrew: Istnieje wiele sposobów, aby sprawdzić, czy zwraca kopię. Jednym z nich jest przeczytanie dokumentacji .
Sven Marnach

1
Lokalne oznacza po prostu „używanie tej samej pamięci, co oryginalna tablica”. Spójrz na zaakceptowaną odpowiedź - ostatnia część pokazuje, że nowe wartości rzeczywiście nadpisały tę samą pamięć.
Sven Marnach

Odpowiedzi:


113

Możesz utworzyć widok z innym typem, a następnie skopiować lokalnie do widoku:

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

print(y)

plony

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

Aby pokazać, że konwersja była na miejscu, pamiętaj, że kopiowanie z x do zostało yzmienione x:

print(x)

wydruki

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

28
Uwaga dla tych (takich jak ja), którzy chcą konwersji między dtype o różnym rozmiarze bajtu (np. 32 do 16 bitów): Ta metoda zawodzi, ponieważ y.size <> x.size. Logiczne, kiedy się nad tym zastanowić :-(
Juh_

Czy to rozwiązanie działało na starszej wersji Numpy? Kiedy robię np.arange(10, dtype=np.int32).view(np.float32)na Numpy 1.8.2, dostaję array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels

3
@BasSwinckels: Tego się oczekuje. Konwersja następuje po przypisaniu y[:] = x.
unutbu

aby wyjaśnić kwestię dotyczącą rozmiaru pozycji (liczby bitów), do której odnosi się oryginalna odpowiedź i @Juh_ np .: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Ten kod zajmuje 10 + 10 float32 i daje w wyniku 10 zamiast 20 float64
dcanelhas

1
Ta lokalna zmiana może zaoszczędzić na zużyciu pamięci, ale jest wolniejsza niż prosta x.astype(float)konwersja. Nie polecałbym tego, chyba że twój skrypt graniczy z MemoryError.
hpaulj

159

Aktualizacja: Ta funkcja unika kopiowania tylko wtedy, gdy jest to możliwe, dlatego nie jest to poprawna odpowiedź na to pytanie. Odpowiedź unutbu jest właściwa.


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

numpy astype ma flagę kopiowania. Dlaczego nie powinniśmy tego używać?


14
Gdy już ten parametr jest obsługiwany w wydaniu NumPy, możemy go oczywiście używać, ale obecnie jest on dostępny tylko w gałęzi programistycznej. A kiedy zadawałem to pytanie, w ogóle nie istniało.
Sven Marnach

2
@SvenMarnach Jest teraz obsługiwany, przynajmniej w mojej wersji (1.7.1).
PhilMacKay

Wygląda na to, że działa doskonale w python3.3 z najnowszą wersją numpy.
CHM

1
Uważam, że jest to około 700 razy wolniejsze niż a = a.view ((float, len (a.dtype.names)))
JJ

14
Flaga kopiowania mówi tylko, że jeśli zmiana może być wykonana bez kopii, zostanie wykonana bez kopii. Jednak jeśli rodzaj jest inny, zawsze będzie kopiowany.
coderforlife

14

Możesz zmienić typ tablicy bez konwersji w ten sposób:

a.dtype = numpy.float32

ale najpierw musisz zmienić wszystkie liczby całkowite na coś, co zostanie zinterpretowane jako odpowiadająca im liczba zmiennoprzecinkowa. Bardzo powolnym sposobem na zrobienie tego byłoby użycie structmodułu Pythona w następujący sposób:

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

... zastosowana do każdego elementu tablicy.

Ale być może szybszym sposobem byłoby wykorzystanie narzędzi ctypeslib firmy Numpy (których nie znam)

- edytować -

Ponieważ ctypeslib wydaje się nie działać, kontynuowałbym konwersję typową numpy.astypemetodą, ale kontynuowałbym w blokach o rozmiarach mieszczących się w granicach pamięci:

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

... a następnie zmień typ po zakończeniu.

Oto funkcja, która wykonuje zadanie dla dowolnych zgodnych dtypów (działa tylko dla dtypów z elementami o tej samej wielkości) i obsługuje tablice o dowolnym kształcie z kontrolą użytkownika nad rozmiarem bloku:

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
Dzięki za odpowiedź. Szczerze mówiąc, nie sądzę, aby było to zbyt przydatne w przypadku dużych tablic - jest o wiele za wolne. Ponowna interpretacja danych tablicy jako innego typu jest łatwa - na przykład przez wywołanie a.view(numpy.float32). Najtrudniejszą częścią jest konwersja danych. numpy.ctypeslibpomaga tylko przy ponownej interpretacji danych, a nie przy ich rzeczywistej konwersji.
Sven Marnach

ok. Nie byłem pewien, jakie są ograniczenia pamięci / procesora. Zobacz moją edycję.
Paul

Dziękuję za aktualizację. Robienie tego blokowo jest dobrym pomysłem - prawdopodobnie najlepszym, jaki możesz uzyskać dzięki obecnemu interfejsowi NumPy. Ale w tym przypadku prawdopodobnie pozostanę przy moim obecnym rozwiązaniu ctypes.
Sven Marnach

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

użyj view () i parametru 'dtype', aby zmienić tablicę w miejscu.


Celem pytania była rzeczywista konwersja danych na miejscu. Po poprawieniu typu w ostatnim wierszu na int, ta odpowiedź tylko zinterpretowałaby istniejące dane jako inny typ, o co nie pytałem.
Sven Marnach

co masz na myśli? dtype to tylko wygląd danych w pamięci, to naprawdę działa, jednak w np.astype parametr 'casting' może sterować domyślną metodą konwersji 'unsafe'.
蒋志强

Tak, zgadzam się z pierwszą zaakceptowaną odpowiedzią. Jednak arr_.astype (new_dtype, copy = False) nadal zwraca nowo przydzieloną tablicę. Jak spełnia dtype, orderoraz subokwymagania, aby powrócić kopię tablicy? Ja tego nie rozwiązuję.
蒋志强

-5

Użyj tego:

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
Czy na pewno to nie jest kopia? Czy możesz to sprawdzić i wyjaśnić trochę więcej?
Michele d'Amico

-5

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


1
Chociaż ten fragment kodu może być rozwiązaniem, dołączenie wyjaśnienia naprawdę pomaga poprawić jakość Twojego posta. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a osoby te mogą nie znać powodów, dla których zaproponowałeś kod.
Sebastialonso

Dlaczego ma to być konwersja na miejscu ? numpy.subtractzwraca kopię, prawda? Tylko nazwa aponownie wykorzystana dla kolejnej porcji danych ... Proszę wyjaśnić, jeśli się mylę.
koffein

Dziękuję za zwrócenie uwagi, wygląda na to, że masz rację - powstała kopia.
MIO
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.