Konversi tipe in-place dari array NumPy

127

Diberikan array NumPy int32, bagaimana cara mengubahnya float32 di tempat ? Jadi pada dasarnya, saya ingin melakukannya

a = a.astype(numpy.float32)

tanpa menyalin array. Itu besar.

Alasan untuk melakukan ini adalah bahwa saya memiliki dua algoritma untuk perhitungan a. Salah satunya mengembalikan array int32, yang lain mengembalikan array float32(dan ini melekat pada dua algoritma yang berbeda). Semua perhitungan lebih lanjut menganggap itu aadalah array dari float32.

Saat ini saya melakukan konversi dalam fungsi C yang dipanggil via ctypes. Apakah ada cara untuk melakukan ini dengan Python?

Sven Marnach
sumber
Menggunakan ctypessebanyak "dengan Python" seperti menggunakan numpy. :)
Karl Knechtel
3
@ Kararl: Tidak, karena saya harus kode dan mengkompilasi fungsi C sendiri.
Sven Marnach
Oh begitu. Saya pikir Anda mungkin SOL yang satu ini.
Karl Knechtel
3
@Andrew: Ada banyak cara untuk mengetahui apakah ia mengembalikan salinan. Salah satunya adalah dengan membaca dokumentasinya .
Sven Marnach
1
Di tempat berarti "menggunakan memori yang sama dengan array asli". Lihatlah jawaban yang diterima - bagian terakhir menunjukkan bahwa nilai-nilai baru memang telah ditimpa memori yang sama.
Sven Marnach

Jawaban:

110

Anda dapat membuat tampilan dengan tipe yang berbeda, dan kemudian menyalin di tempat ke tampilan:

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

print(y)

hasil panen

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

Untuk menunjukkan bahwa konversi sudah dilakukan, perhatikan bahwa penyalinan dari x ke ydiubah x:

print(x)

cetakan

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])
unutbu
sumber
26
Catatan untuk mereka (seperti saya) yang menginginkan konversi antara dtype dengan ukuran byte yang berbeda (misalnya 32 hingga 16 bit): Metode ini gagal karena y.ukuran <> x.ukuran. Masuk akal begitu Anda memikirkannya :-(
Juh_
Apakah solusi ini berfungsi untuk beberapa versi Numpy yang lebih lama? Ketika saya melakukannya np.arange(10, dtype=np.int32).view(np.float32)pada Numpy 1.8.2, saya mengerti array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels
3
@BasSwinckels: Itu sudah diharapkan. Konversi terjadi ketika Anda menetapkan y[:] = x.
unutbu
untuk memperjelas poin yang dibuat tentang itemsize (jumlah bit) yang dirujuk oleh jawaban asli dan @ Juh_ misalnya: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Kode ini membutuhkan 10 + 10 float32 dan menghasilkan 10, bukan 20 float64
dcanelhas
1
Perubahan di tempat ini dapat menghemat penggunaan memori, tetapi lebih lambat daripada x.astype(float)konversi sederhana . Saya tidak akan merekomendasikan hal ini kecuali skrip Anda berbatasan dengan MemoryError.
hpaulj
158

Pembaruan: Fungsi ini hanya menghindari salinan jika bisa, maka ini bukan jawaban yang benar untuk pertanyaan ini. jawaban unutbu adalah yang benar.


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

astype numpy memiliki bendera salinan. Kenapa kita tidak menggunakannya?

Vara
sumber
14
Setelah parameter ini didukung dalam rilis NumPy, tentu saja kami dapat menggunakannya, tetapi saat ini hanya tersedia di cabang pengembangan. Dan pada saat saya menanyakan pertanyaan ini, tidak ada sama sekali.
Sven Marnach
2
@SvenMarnach Sekarang didukung, setidaknya dalam versi saya (1.7.1).
PhilMacKay
Tampaknya berfungsi dengan baik di python3.3 dengan versi numpy terbaru.
CHM
1
Saya menemukan ini sekitar 700x lebih lambat dari a = view ((float, len (a.dtype.names)))
JJ
14
Bendera salinan hanya mengatakan bahwa jika perubahan dapat dilakukan tanpa salinan, itu akan dilakukan tanpa salinan. Namun jenisnya berbeda tetap akan selalu menyalin.
coderforlife
14

Anda dapat mengubah jenis array tanpa mengonversi seperti ini:

a.dtype = numpy.float32

tetapi pertama-tama Anda harus mengubah semua bilangan bulat menjadi sesuatu yang akan ditafsirkan sebagai float yang sesuai. Cara yang sangat lambat untuk melakukan ini adalah dengan menggunakan structmodul python seperti ini:

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

... diterapkan pada setiap anggota array Anda.

Tapi mungkin cara yang lebih cepat adalah dengan menggunakan alat ctypeslib numpy (yang saya tidak kenal)

- edit -

Karena ctypeslib tampaknya tidak berfungsi, maka saya akan melanjutkan konversi dengan numpy.astypemetode biasa , tetapi melanjutkan dalam ukuran blok yang berada dalam batas memori Anda:

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

... lalu ubah dtype setelah selesai.

Berikut adalah fungsi yang menyelesaikan tugas untuk setiap dtypes yang kompatibel (hanya berfungsi untuk dtypes dengan item berukuran sama) dan menangani array berbentuk sewenang-wenang dengan kontrol pengguna atas ukuran blok:

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
sumber
1
Terima kasih atas jawaban anda. Jujur, saya tidak berpikir ini sangat berguna untuk array besar - itu terlalu lambat. Menafsirkan kembali data array sebagai tipe yang berbeda itu mudah - misalnya dengan menelepon a.view(numpy.float32). Bagian yang sulit sebenarnya adalah mengubah data. numpy.ctypeslibhanya membantu menafsirkan ulang data, bukan dengan benar-benar mengubahnya.
Sven Marnach
baik. Saya tidak yakin apa keterbatasan memori / prosesor Anda. Lihat hasil edit saya.
Paul
Terima kasih atas pembaruannya. Melakukannya secara blockwise adalah ide yang bagus - mungkin yang terbaik yang bisa Anda dapatkan dengan antarmuka NumPy saat ini. Tetapi dalam kasus ini, saya mungkin akan tetap menggunakan solusi ctypes saya saat ini.
Sven Marnach
-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

gunakan view () dan parameter 'dtype' untuk mengubah larik di tempat.

蒋志强
sumber
Tujuan dari pertanyaan adalah untuk benar-benar mengubah data yang ada. Setelah mengoreksi jenis di baris terakhir int, jawaban ini hanya akan menafsirkan kembali data yang ada sebagai jenis yang berbeda, yang bukan yang saya minta.
Sven Marnach
maksud kamu apa? dtype hanya penampilan data dalam memori, itu benar-benar berfungsi. Namun dalam np.astype, parameter 'casting' dapat mengontrol metode konversi default 'tidak aman'.
蒋志强
Ya, saya setuju dengan jawaban yang diterima pertama. Namun arr_.astype (new_dtype, copy = False) masih mengembalikan array yang baru dialokasikan. Cara puas dtype, orderdan subokpersyaratan untuk kembali salinan dari array? Saya tidak menyelesaikannya.
蒋志强
-5

Gunakan ini:

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
sumber
5
Apakah Anda yakin itu bukan salinan? Bisakah Anda memeriksanya dan menjelaskan sedikit lebih banyak?
Michele d'Amico
-5

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

MIO
sumber
1
Sementara cuplikan kode ini mungkin solusinya, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
Sebastialonso
Mengapa ini harus menjadi konversi di tempat ? numpy.subtractmengembalikan salinan, bukan? Hanya nama yang adigunakan kembali untuk sepotong data ... Tolong jelaskan, jika saya salah tentang hal ini.
koffein
Terima kasih telah menunjukkan ini, sepertinya Anda benar - salinan dihasilkan.
MIO