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.