Numpy dizileri eklerken taşma önlemek

Datatyp ile uint8 dizisi eklemek istiyorum. Bu dizilerdeki değerlerin bir taşma gerçekleşmesi için yeterince büyük olabileceğini biliyorum. Böylece şöyle bir şey alıyorum:

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
a += b

Şimdi, a [150 250 44] . Ancak, taşma yerine, uint8 için izin verilen maksimum değer olamayacak kadar büyük olan değerler istiyorum. Böylece istediğim sonuç [150 250 255] olacaktır.

Bu sonucu aşağıdaki kod ile alabilirim:

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
c = np.zeros((1,3), dtype=np.uint16)
c += a
c += b
c[c>255] = 255
a = np.array(c, dtype=np.uint8)

Sorun şu ki, dizilerimin gerçekten büyük olması, daha büyük veri tipine sahip üçüncü bir dizi oluşturmak bir bellek sorunu olabilir. Açıklanan sonucu elde etmek için hızlı ve daha fazla bellek verimli bir yolu var mı?

11

6 cevap

Bunu, üçüncü bir dintpe uint8 dizisi ve bir bool dizisi (birlikte bir uint16 dizisinden daha fazla bellek verimli olan) oluşturarak elde edebilirsiniz.

np.putmask is useful for avoiding a temp array.

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
c = 255 - b  # a temp uint8 array here
np.putmask(a, c < a, c)  # a temp bool array here
a += b

Bununla birlikte, @moarningsun doğru bir şekilde gösterdiği gibi, bir bool dizisi uint8 dizisiyle aynı miktarda belleği alır, bu nedenle bu mutlaka yardımcı olmaz. Bunu, herhangi bir zamanda birden fazla geçici diziye sahip olmaktan kaçınarak çözmek mümkündür:

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
b = 255 - b  # old b is gone shortly after new array is created
np.putmask(a, b < a, b)  # a temp bool array here, then it's gone
a += 255 - b  # a temp array here, then it's gone

Bu yaklaşım CPU için bellek tüketimini değiştirir.


Diğer bir yaklaşım, O (1) ekstra bellek (yani, dizilerinizin boyutundan bağımsız) olan olası tüm sonuçları önceden hesaplamaktır :

c = np.clip(np.arange(256) + np.arange(256)[..., np.newaxis], 0, 255).astype(np.uint8)
c
=> array([[  0,   1,   2, ..., 253, 254, 255],
          [  1,   2,   3, ..., 254, 255, 255],
          [  2,   3,   4, ..., 255, 255, 255],
          ..., 
          [253, 254, 255, ..., 255, 255, 255],
          [254, 255, 255, ..., 255, 255, 255],
          [255, 255, 255, ..., 255, 255, 255]], dtype=uint8)

c[a,b]
=> array([150, 250, 255], dtype=uint8)

Dizileriniz çok büyükse, bu yaklaşım en verimli olanıdır. Yine, işlem süresinde pahalıdır, çünkü süper hızlı tamsayı ilavelerinin yavaş 2dim-array indekslemesi ile değiştirilmesi.

NASIL ÇALIŞIYOR

Yukarıdaki c dizisinin oluşturulması, bir dizi yayın hilesinden yararlanır. (N,) şekil dizisini ve (1, N) şekil dizisini her ikisini de (N, N) şeklinde yayınlayın , bu nedenle sonuç, tüm olası toplamların bir NxN dizisidir. Sonra klibi atarız. Tatmin edici 2dim bir dizi elde ediyoruz: c [i, j] = min (i + j, 255) her i, j için.

Sonra kalan şey, doğru değerleri elde etmek için fantezi indekslemesini kullanmaktır. Girdiğiniz girdiyle çalışarak, aşağıdakilere erişiyoruz:

c[( [100, 200, 250] , [50, 50, 50] )]

İlk dizin dizisi 1. kısma, ikincisi 2. kısma karşılık gelir. Böylece, sonuç, [c [100,50], c [200,50] değerlerinden oluşan, dizin dizileriyle aynı şekilde bir dizidir ( (N,) ), c [250,50]] .

7
katma
@moarningsun Sanırım haklısın. Bununla birlikte, kişisel olarak kendimi mükemmel hissetmediğim taşmalara dayanıyor ...
katma yazar shx2, kaynak
@moarningsun neden cevabını sildin? Bence bu iyi bir cevap ve işe yarıyor
katma yazar shx2, kaynak
@Tamas Daha fazla ayrıntı sağlamak için cevabımı düzenlerim
katma yazar shx2, kaynak
putmask hakkında bir şey bilmiyordum, bunun için teşekkürler! Bu işlevi kullanarak bence a + = b ve ardından np.putmask (a, a da çalışmalı mı?
katma yazar user2379410, kaynak
Bu yöntem, putmask 'ın verimli olmasını gerektirir; bu, cevabınızın ana noktalarından biriydi. Bu yüzden buraya bir yorum olarak koyabileceğimi düşündüm.
katma yazar user2379410, kaynak
Üçüncü yöntemini denedim ve gayet iyi çalışıyor. Bununla birlikte, biraz daha fazla açıklayabilir misiniz? Bu üçüncü yöntemin ('önceden hesapla') yürütme zamanını durumla karşılaştırdım, a için bir uint16 dizisi kullandım, a + = b yapın ve [a> 255] = 255. 'Ön hesap' süresi iki katına çıkar. Fakat daha fazla hafıza verimli ise, uint16 yaklaşımıyla hafıza tükettiğim büyük dizilerle hesaplamaları yapabilirim.
katma yazar Thomas, kaynak

İşte bir yol:

>>> a = np.array([100, 200, 250], dtype=np.uint8)
>>> b = np.array([50, 50, 50], dtype=np.uint8)
>>> a+=b; a[a>> a
array([150, 250, 255], dtype=uint8)
1
katma

Numba ile gerçekten yerinde yapabilirsiniz, örneğin:

import numba

@numba.jit('void(u1[:],u1[:])', locals={'temp': numba.uint16})
def add_uint8_inplace_clip(a, b):
    for i in range(a.shape[0]):
        temp = a[i] + b[i]
        a[i] = temp if temp<256 else 255

add_uint8_inplace_clip(a, b)

Veya Numexpr ile, örneğin:

import numexpr

numexpr.evaluate('where((a+b)>255, 255, a+b)', out=a, casting='unsafe')

Numexpr upcasts uint8 int32 dahili olarak, uint8 dizisine geri koymadan önce.

1
katma
def non_overflowing_sum(a, b)
    c = np.uint16(a)+b
    c[np.where(c>255)] = 255
    return np.uint8( c )

hafızayı da takas ediyor ama daha zarif buldum ve dönüşümü takiben geçici uint16 serbest kaldı

1
katma

Peki ya

>>> a + np.minimum(255 - a, b)
array([150, 250, 255], dtype=uint8)

genel olarak veri türünüz için en yüksek değeri elde etmek

np.iinfo(np.uint8).max
0
katma
@ shx2: Sadece iki tane sayıyorum. 255 - a ve np.minimum (255 - a, b) . Üçüncüsü nedir?
katma yazar user2357112, kaynak
@PadraicCunningham, öyle, ancak uint8 türünde, uint16'da değil. Ancak, üç temp uint8 dizisi oluşturur.
katma yazar shx2, kaynak
@ user2357112, a + ... . OP sonucu a dizisinin yerine istiyorsa, bundan kaçınmak mümkündür.
katma yazar shx2, kaynak

Bunun için numpy’de bir işlev var :

numpy.nan_to_num (x) [kaynak]

Nan'ı sıfırla ve inf'i sonlu sayılarla değiştirin.

     

Sayı (NaN) 'ı sıfır, (pozitif) sonsuzluk ile çok büyük bir sayı ve negatif sonsuz ile çok küçük (veya negatif) bir sayı ile değiştiren bir dizi veya skalar döndürür.

     

Yeni öğenin x ve dtype biçimiyle aynı biçimde x cinsinden en yüksek hassasiyetle.

     

x tam değilse, NaN sıfıra değiştirilir ve sonsuzluk (-infinity), çıktı türüne uyan en büyük (en küçük veya en negatif) kayan nokta değeri ile değiştirilir. X tam değilse, x'in bir kopyası döndürülür.

Çıktıdaki kayan noktadan dolayı uint8 ile çalışıp çalışmayacağından emin değilim, ancak diğer okuyucular için yararlı olabilir

0
katma
@ToshinouKyouko Evet, tamsayılar için gerçekten farklı, sadece OP örneğindeki gibi taşıyorlar.
katma yazar luator, kaynak
@Thomas hmm, belki de tamsayı türleri için farklıdır, ancak yüzdürme problemiyle karşılaştığımda, taşmalar +/- sonsuz olarak gözüküyordu.
katma yazar Toshinou Kyouko, kaynak
Bunun soruya nasıl yardımcı olabileceğini anlamıyorum. Eklenecek dizilerin hiçbirinde NaN'ler veya sonsuz değerler yoktur. Öyleyse belki de cevabının noktasını özlüyorumdur?
katma yazar Thomas, kaynak