NumPy dizisinin yerinde tür dönüşümü


127

Bir NumPy dizisi Verilen int32, ben nasıl dönüştürebilirim float32 yerde ? Yani temelde yapmak isterim

a = a.astype(numpy.float32)

diziyi kopyalamadan. Bu büyük.

Bunu yapmamın nedeni, hesaplaması için iki algoritmaya sahip olmam a. Bunlardan biri bir dizi döndürür int32, diğeri bir dizi döndürür float32(ve bu iki farklı algoritmanın doğasında vardır). Diğer tüm hesaplamalar bunun abir dizi olduğunu varsayar float32.

Şu anda dönüşümü via adlı bir C fonksiyonunda yapıyorum ctypes. Bunu Python'da yapmanın bir yolu var mı?


ctypes"Python'da" kullanmak , kullanmak kadar fazladır numpy. :)
Karl Knechtel

3
@Karl: Hayır, çünkü C işlevini kendim kodlamalı ve derlemeliyim.
Sven Marnach

Ah anlıyorum. Sanırım bu sefer muhtemelen SOL'sunuz.
Karl Knechtel

3
@Andrew: Bir kopya döndürüp döndürmediğini anlamanın birçok yolu var. Bunlardan biri belgeleri okumaktır .
Sven Marnach

1
Yerinde, basitçe "orijinal diziyle aynı belleği kullanmak" anlamına gelir. Kabul edilen cevaba bir bakın - son kısım, yeni değerlerin gerçekten aynı hafızanın üzerine yazdığını gösteriyor.
Sven Marnach

Yanıtlar:


110

Farklı bir dtype ile bir görünüm oluşturabilir ve ardından görünüme yerinde kopyalayabilirsiniz:

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

print(y)

verim

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

Dönüşümün yerinde olduğunu göstermek için, ' x den ydeğiştirilene kopyalamanın x:

print(x)

baskılar

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

26
Farklı bayt boyutundaki dtype (örn. 32 ila 16 bit) arasında dönüşüm isteyenler (benim gibi) için not: Bu yöntem başarısız olur çünkü y.size <> x.size. Düşündüğünüzde
mantıklı

Bu çözüm, Numpy'nin eski bir sürümü için çalışıyor muydu? np.arange(10, dtype=np.int32).view(np.float32)Numpy 1.8.2'de yaptığımda , anlıyorum array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels

3
@BasSwinckels: Bu bekleniyor. Dönüşüm, atadığınızda gerçekleşir y[:] = x.
unutbu

orijinal yanıt ve @Juh_ tarafından belirtilen öğe boyutu (bit sayısı) hakkında yapılan noktayı açıklığa kavuşturmak için: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Bu kod 10 + 10 float32 alır ve sonuçta 20 float64 yerine 10 olur
dcanelhas

1
Bu yerinde değişiklik bellek kullanımında tasarruf sağlayabilir, ancak basit bir x.astype(float)dönüştürmeden daha yavaştır . Komut dosyanız MemoryError sınırında değilse bunu tavsiye etmem.
hpaulj

158

Güncelleme: Bu işlev yalnızca yapabiliyorsa kopyalamayı önler, dolayısıyla bu soru için doğru cevap bu değildir. unutbu'nun cevabı doğru olanıdır.


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

numpy astype'ın bir kopya bayrağı vardır. Neden kullanmayalım?


14
Bu parametre bir NumPy sürümünde desteklendikten sonra elbette kullanabiliriz, ancak şu anda yalnızca geliştirme dalında mevcuttur. Ve ben bu soruyu sorduğumda, hiç yoktu.
Sven Marnach

2
@SvenMarnach Artık en azından benim sürümümde (1.7.1) destekleniyor.
PhilMacKay

En son numpy sürümü ile python3.3'te mükemmel çalışıyor gibi görünüyor.
CHM

1
Bunun a = a.view'den ((float, len (a.dtype.names)))
JJ

14
Kopya bayrağı yalnızca değişiklik kopyasız yapılabiliyorsa, kopyasız yapılacağını belirtir. Ancak tür farklıdır, yine de her zaman kopyalayacaktır.
coderforlife

14

Dizi türünü şu şekilde dönüştürmeden değiştirebilirsiniz:

a.dtype = numpy.float32

ama önce tüm tam sayıları karşılık gelen kayan nokta olarak yorumlanacak bir şeye değiştirmelisiniz. Bunu yapmanın çok yavaş bir yolu, python'un structmodülünü şu şekilde kullanmak olacaktır :

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

... dizinizin her bir üyesine uygulanır.

Ama belki de daha hızlı bir yol, numpy'nin ctypeslib araçlarını kullanmak olabilir (benim aşina olmadığım)

- Düzenle -

Ctypeslib işe yaramadığından, o zaman dönüştürmeye tipik numpy.astypeyöntemle devam ederdim , ancak bellek sınırlarınız dahilinde olan blok boyutlarında devam ederdim :

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

... sonra tamamlandığında dtype'ı değiştirin.

Herhangi bir uyumlu dtype için görevi gerçekleştiren (yalnızca aynı boyuttaki öğelere sahip dtype'lar için çalışır) ve blok boyutu üzerinde kullanıcı denetimiyle rastgele şekilli dizileri işleyen bir işlev:

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
Cevabınız için teşekkürler. Açıkçası, bunun büyük diziler için çok yararlı olduğunu düşünmüyorum - çok yavaş. Dizinin verilerini farklı bir tür olarak yeniden yorumlamak kolaydır - örneğin arayarak a.view(numpy.float32). İşin zor kısmı aslında verileri dönüştürmektir. numpy.ctypeslibgerçekten dönüştürmeye değil, yalnızca verilerin yeniden yorumlanmasına yardımcı olur.
Sven Marnach

tamam. Bellek / işlemci sınırlamalarının ne olduğundan emin değildim. Düzenlememe bakın.
Paul

Güncelleme için teşekkürler. Blok halinde yapmak iyi bir fikirdir - muhtemelen mevcut NumPy arayüzüyle elde edebileceğinizin en iyisidir. Ancak bu durumda, muhtemelen mevcut ctypes çözümüme bağlı kalacağım.
Sven Marnach

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

diziyi yerinde değiştirmek için view () ve 'dtype' parametresini kullanın.


Sorunun amacı , verileri yerinde dönüştürmekti . Son satırdaki türü olarak düzelttikten sonra int, bu yanıt yalnızca mevcut verileri farklı bir tür olarak yeniden yorumlayacaktı, ki bu istediğim şey bu değildi.
Sven Marnach

ne demek istiyorsun? dtype yalnızca bellekteki verilerin görünümüdür, gerçekten çalışır. Ancak np.astype parametresinde, 'çevrim' parametresi, dönüştürme yöntemi varsayılanı 'güvensiz'i kontrol edebilir.
蒋志强

Evet, kabul edilen ilk cevaba katılıyorum. Ancak arr_.astype (new_dtype, copy = False) yine de yeni ayrılmış bir dizi döndürür. Nasıl tatmin etmek dtype, orderve subokdizinin bir kopyasını döndürmek için gereksinimleri? Ben çözmüyorum.
蒋志强

-5

Bunu kullan:

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
Bunun bir kopya olmadığına emin misin? Kontrol edip biraz daha açıklayabilir misin?
Michele d'Amico

-5

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


1
Bu kod pasajı çözüm olabilir, ancak bir açıklama da dahil olmak üzere yayınınızın kalitesini artırmaya gerçekten yardımcı olur. Gelecekte okuyucular için soruyu yanıtladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceklerini unutmayın.
Sebastialonso

Bu neden yerinde bir dönüşüm olmalı ? numpy.subtractbir kopyasını iade ediyor, değil mi? Yalnızca ad abaşka bir veri yığını için yeniden kullanıldı ... Bu konuda yanılıyorsam lütfen açıklayın.
koffein

Bunu belirttiğiniz için teşekkür ederiz, öyle görünüyor ki haklısınız - bir kopya üretiliyor.
MIO
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.