2013-02-08 7 views
6

Mam look-up table (LUT), która przechowuje 65536 uint8 wartości:odlew z numpy.take

lut = np.random.randint(256, size=(65536,)).astype('uint8') 

Chcę użyć tego LUT do konwersji wartości w tablicy uint16 s:

arr = np.random.randint(65536, size=(1000, 1000)).astype('uint16') 

i chcę przeprowadzić konwersję w miejscu, ponieważ ta ostatnia tablica może być dość duża. Kiedy go wypróbuję, następuje:

I nie rozumiem, co się dzieje. Wiem, że bez argumentu out, zwrot jest tego samego typu co lut, czyli uint8. Ale dlaczego nie można wykonać rzutowania na uint8 na uint16? Jeśli zapytać numpy:

>>> np.can_cast('uint8', 'uint16') 
True 

Oczywiście następujące prace:

>>> lut = lut.astype('uint16') 
>>> np.take(lut, arr, out=arr) 
array([[173, 251, 218, ..., 110, 98, 235], 
     [200, 231, 91, ..., 158, 100, 88], 
     [ 13, 227, 223, ..., 94, 56, 36], 
     ..., 
     [ 28, 198, 80, ..., 60, 87, 118], 
     [156, 46, 118, ..., 212, 198, 218], 
     [203, 97, 245, ..., 3, 191, 173]], dtype=uint16) 

Ale to działa również:

>>> lut = lut.astype('int32') 
>>> np.take(lut, arr, out=arr) 
array([[ 78, 249, 148, ..., 77, 12, 167], 
     [138, 5, 206, ..., 31, 43, 244], 
     [ 29, 134, 131, ..., 100, 107, 1], 
     ..., 
     [109, 166, 14, ..., 64, 95, 102], 
     [152, 169, 102, ..., 240, 166, 148], 
     [ 47, 14, 129, ..., 237, 11, 78]], dtype=uint16) 

To naprawdę nie ma sensu, ponieważ teraz int32 s są oddane do uint16 s, co zdecydowanie nie jest bezpieczne:

>>> np.can_cast('int32', 'uint16') 
False 

Mój kod działa, jeśli mogę ustawić dtype lut jest do niczego w uint16, uint32, uint64, int32 lub int64, ale nie dla uint8, int8 i int16.

Czy czegoś brakuje, czy jest to po prostu łamane w numpy?

Obejścia są również mile widziane ... Ponieważ LUT nie jest tak duże, to chyba nie jest tak źle, że jego typ pasuje do tablicy, nawet jeśli zajmuje to dwa razy więcej miejsca, ale po prostu nie czuje się dobrze aby to zrobić ...

Czy istnieje sposób, aby powiedzieć numpy, aby nie przejmował się rzucaniem bezpieczeństwa?

+0

Innym przypadkiem użycia jest float -> int indices, 'y.take (xfloat.astype (int), mode =" clip ")': unsafe without 'astype' ale common (well, in c) i useful . – denis

Odpowiedz

2

Interesujący problem. numpy.take(lut, ...) zostaje przekształcona lut.take(...) którego źródłem może być postrzegana tutaj:

https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/item_selection.c#L28

Uważam, że jest wyjątek at line 105:

obj = (PyArrayObject *)PyArray_FromArray(out, dtype, flags); 
if (obj == NULL) { 
    goto fail; 
} 

gdzie w przypadku out jest arr ale dtype jest jednym z lut, czyli uint8. Próbuje więc odrzucić arr do uint8, co nie powiedzie się. Muszę powiedzieć, że nie jestem pewien, dlaczego musi to zrobić, po prostu wskazując na to ... Z jakiegoś powodu take wydaje się zakładać, że chcesz jako macierz wyjściowa mieć ten sam dtype jako lut.

Przy okazji, w wielu przypadkach połączenie z numerem PyArray_FromArray spowoduje utworzenie nowej tablicy, a zamiennik nie będzie dostępny na miejscu. Dotyczy to na przykład if you call take with mode='raise' (ustawienie domyślne i to, co dzieje się w przykładach) lub kiedykolwiek lut.dtype != arr.dtype. Cóż, przynajmniej powinien, i nie mogę wytłumaczyć dlaczego, kiedy odrzucisz lut do int32, tablica wyjściowa pozostaje uint16! To dla mnie tajemnica - może ma to coś wspólnego z flagą NPY_ARRAY_UPDATEIFCOPY (patrz także here).

Konkluzja:

  1. zachowanie numpy jest rzeczywiście trudne do zrozumienia ... Może ktoś zapewni pewien wgląd dlaczego robi to, co robi
  2. nie będę próbować przetwarzać arr w miejsce - wydaje się, że nowa tablica jest tworzona pod maską w większości przypadków. Po prostu pójdę z arr = lut.take(arr) - który przy okazji uwolni połowę pamięci poprzednio używanej przez arr.
+0

@seberg - już to, o którym ostrzeżeniu mówisz? Moim głównym punktem było to, że PO powinien po prostu obejmować nowy zestaw. – lum

+1

jak zwykle powinien był przeczytać uważniej :), tak. Tak, nie ma sposobu na uniknięcie kopii tutaj, chyba że typy itp. Pasują. – seberg

+0

Zejście z tej drogi także zeszłej nocy ... Jeśli niebezpieczne rzucanie, które już trwa, jest zgodne z oczekiwaniami, to myślę, że wywołanie 'PyArray_FromArray' powinno zostać wykonane z zestawem flag" NPY_ARRAY_FORCECAST ", aby przez cały czas wykonuje niebezpieczne rzucanie. Obecne zachowanie jest oczywiście zepsute. Zobaczę, jak się przygotować i przesłać poprawkę. To może nie być możliwe, ale właściwe wydaje się, aby rzucić 'self' na' dtype' of 'out', a nie na odwrót. – Jaime

Powiązane problemy