2015-04-13 11 views
10

Chcę dodać numpy tablice z datatyp uint8. Wiem, że wartości w tych tablicach mogą być wystarczająco duże, aby mogło nastąpić przepełnienie. Dostaję coś takiego:Unikaj przepełnienia podczas dodawania numpy tablic

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

Teraz jest [150 250 44]. Jednak zamiast przepełnienia chcę wartości, które są zbyt duże, aby uint8 było maksymalną dozwoloną dla uint8. Tak więc moim pożądanym wynikiem będzie [150 250 255].

mogę dostać ten wynik z następującego kodu:

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) 

Problemem jest to, że moje tablice są naprawdę duże, więc tworząc trzecią tablicę z większego typu danych może być problem pamięci. Czy istnieje szybki i bardziej efektywny sposób na pamięć, aby osiągnąć opisany rezultat?

Odpowiedz

6

Można to osiągnąć poprzez tworzenie trzecią tablicę dtype uint8 plus tablica bool (które razem są bardziej wydajne pod względem pamięci, niż jedna tablica UINT16).

np.putmask jest przydatne do uniknięcia tablicy temp.

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 

Jednakże, @moarningsun prawidłowo wskazuje tablicę BOOL wykonuje się tę samą ilość pamięci w postaci tablicy uint8, więc nie musi być pomocny. Jest możliwe, aby rozwiązać ten problem przez uniknięcie konieczności tablicy więcej niż jeden temp w danym momencie:

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 

Podejście transakcji zużycia pamięci do jednostki centralnej.


Innym podejściem jest precalculate wszystkie możliwe wyniki, czyli O (1) Pamięć dodatkowa (czyli niezależnie od wielkości swoich tablic):

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) 

Takie podejście jest najbardziej pamięć - jeśli twoje tablice są bardzo duże. Ponownie, jest to kosztowne w czasie przetwarzania, ponieważ zastępuje superszybkie liczby całkowite z wolniejszym indeksowaniem 2dim-array.

WYJAŚNIENIE jak to działa

budowa tablicy c powyżej korzysta z numpy nadawczej sztuczki. Dodanie tablicy kształtu (N,) i tablicy kształtu (1,N) nadaje się zarówno jako (N,N)-like, w ten sposób wynik jest tablicą NxN wszystkich możliwych sum. Następnie go przypinamy. Otrzymujemy tablicę 2dim spełniającą: c[i,j]=min(i+j,255) dla każdego i, j.

To, co pozostało, to wymyślne indeksowanie, aby pobrać prawidłowe wartości.Praca z wejściem podałeś, mamy dostęp do:

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

Pierwszy indeksu tablicy odnosi się do 1 słabe, a druga do 2 Dim. Zatem wynikiem jest tablica o tym samym kształcie, co tablice indeksów ((N,)), składające się z wartości [ c[100,50] , c[200,50] , c[250,50] ].

+1

Nie wiedziałem o "putmask", dzięki za to! Korzystając z tej funkcji, myślę, że również "a + = b", po której następuje 'np.putmask (a, a

+0

@moarningsun Myślę, że masz rację. Jednak opiera się na przepełnieniu, z którym osobiście nie czuję się doskonale z ... – shx2

+0

@moarningsun, dlaczego usunąłeś swoją odpowiedź? Myślę, że jest to przyzwoita odpowiedź i działa – shx2

0

Jak o zrobieniu

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

w ogóle coraz max wartość dla typu danych z

np.iinfo(np.uint8).max 
+0

@PadraicCunningham, to robi, ale dtype uint8, nie uint16. Jednakże tworzy * trzy * temp * tablice uint8 .. – shx2

+0

@ shx2: Liczę tylko dwa. '255 - a', i' np.minimum (255 - a, b) '. Jaka jest trzecia? – user2357112

+0

@ user2357112, 'a + ...'. Jeśli OP chce uzyskać wynik zamiast tablicy 'a', można tego uniknąć. – shx2

1

Oto sposób:

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

Można to zrobić naprawdę inplace z Numba, na przykład:

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) 

Albo z numexpr, na przykład:

import numexpr 

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

numexpr upcastsuint8 do int32 wewnętrznie, przed odłożeniem go w uint8 tablica.

0

a function in numpy Jest za to:

numpy.nan_to_num(x)[source]

Wymień nan zera i INF z liczbami skończonymi.

Powoduje zwrócenie tablicy lub skalaru zastępującego Brak liczby (NaN) z zerową, (dodatnią) nieskończonością z bardzo dużą liczbą i ujemną nieskończonością z bardzo małą (lub ujemną) liczbą.

Nowa tablica o tym samym kształcie co x i typ dtype w x z największą precyzją.

Jeśli x jest niedokładne, to NaN jest zastępowane przez zero, a nieskończoność (-infinity) jest zastępowana przez największą (najmniejszą lub najbardziej ujemną) wartość zmiennoprzecinkową, która pasuje do typu danych wyjściowych. Jeśli x nie jest niedokładne, zwracana jest kopia x.

Nie jestem pewien, czy to będzie działać z uint8, ze względu na wzmianki o zmiennoprzecinkowych na wyjściu, ale dla innych czytelników, może być przydatna

+1

Nie widzę, jak to mogłoby pomóc w odpowiedzi na pytanie. W żadnej z tablic, które zostaną dodane, nie ma żadnych NaN lub nieskończonych wartości. Więc może brakuje mi punktu twojej odpowiedzi? – Thomas

+0

@Thomas hmm, być może jest inny dla typów całkowitych, ale kiedy napotkałem problem z float, przepełnienia pojawiły się jako +/- nieskończoności –

+0

@ToshinouKyouko Tak, to jest rzeczywiście różne dla liczb całkowitych, po prostu przelewają się, jak w przykładzie OP. – luator

Powiązane problemy