2013-02-08 35 views
6

Tôi có một cái nhìn lên bảng (LUT) mà các cửa hàng 65536 uint8 giá trị:Loại đúc với numpy.take

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

Tôi muốn sử dụng LUT này để chuyển đổi các giá trị trong một mảng của uint16 s:

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

và tôi muốn thực hiện chuyển đổi tại chỗ, vì mảng cuối cùng này có thể khá lớn. Khi tôi thử, điều sau xảy ra:

Và tôi không hiểu điều gì đang diễn ra. Tôi biết rằng, không có đối số out, lợi tức là của cùng một loại như lut, vì vậy uint8. Nhưng tại sao không thể uint8 được truyền tới một số uint16? Nếu bạn hỏi NumPy:

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

Rõ ràng các công việc sau:

>>> 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) 

Nhưng điều này cũng làm việc:

>>> 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) 

Điều này thực sự làm cho không có ý nghĩa, kể từ bây giờ int32 s đang được đúc để uint16 s, chắc chắn đó không phải là điều an toàn để làm:

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

Mã của tôi hoạt động nếu tôi đặt dtype các 's lut để bất cứ điều gì trong uint16, uint32, uint64, int32 hoặc int64, nhưng không cho uint8, int8int16.

Tôi có thiếu thứ gì đó không, hoặc điều này đơn giản là bị hỏng trong không?

Cách giải quyết cũng được hoan nghênh ... Vì LUT không lớn, tôi đoán không tệ khi có loại khớp với mảng, ngay cả khi mất gấp đôi không gian, nhưng nó không cảm thấy đúng để làm điều đó ...

Có cách nào để bảo đảm không lo lắng về an toàn khi truyền?

+0

Trường hợp sử dụng khác là float -> int index, 'y.take (xfloat.astype (int), mode =" clip ")': không an toàn mà không có 'astype' nhưng phổ biến (tốt, trong c) và hữu ích . – denis

Trả lời

2

Sự cố thú vị. numpy.take(lut, ...) được chuyển thành lut.take(...) có nguồn có thể được xem xét ở đây:

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

tôi tin rằng các ngoại lệ được ném at line 105:

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

nơi trong trường hợp của bạn outarr nhưng dtype là một trong những lut, tức là uint8. Vì vậy, nó cố gắng để đúc arr đến uint8, lỗi. Tôi phải nói rằng tôi không chắc chắn lý do tại sao nó cần phải làm điều đó, chỉ cần chỉ ra nó không ... Vì lý do nào đó take dường như giả sử bạn muốn là mảng đầu ra có cùng một dtypelut.

Nhân tiện, trong nhiều trường hợp, cuộc gọi tới PyArray_FromArray sẽ thực sự tạo một mảng mới và thay thế sẽ không được đặt ở vị trí. Đây là trường hợp ví dụ: if you call take with mode='raise' (mặc định và điều gì xảy ra trong ví dụ của bạn) hoặc bất cứ khi nào lut.dtype != arr.dtype. Vâng, ít nhất là nó nên, và tôi không thể giải thích tại sao, khi bạn đúc lut đến int32 mảng đầu ra vẫn còn uint16! Đây là một bí ẩn đối với tôi - có thể nó có liên quan đến cờ NPY_ARRAY_UPDATEIFCOPY (xem thêm here).

Bottom line:

  1. hành vi của NumPy là thực sự khó hiểu ... Có lẽ người khác sẽ cung cấp một số cái nhìn sâu sắc tại sao nó làm những gì nó làm
  2. tôi sẽ không cố gắng để xử lý arr trong nơi - có vẻ như một mảng mới được tạo ra dưới mui xe trong hầu hết các trường hợp anyway. Tôi chỉ đơn giản là đi với arr = lut.take(arr) - mà theo cách này cuối cùng sẽ miễn phí một nửa bộ nhớ trước đây được sử dụng bởi arr.
+0

@seberg - đã biết, bạn đang nói về cảnh báo nào? Điểm chính của tôi thực sự là OP chỉ nên nắm lấy một mảng mới. – lum

+1

như thường lệ, nên đọc kỹ hơn :), vâng. Không có cách nào để tránh một bản sao ở đây trừ khi loại, v.v. – seberg

+0

Tôi cũng đã đi xuống con đường đó tối qua ... Nếu việc đúc không an toàn đang diễn ra là những gì được mong đợi, thì tôi nghĩ rằng lệnh gọi 'PyArray_FromArray' nên được thực hiện với cờ' NPY_ARRAY_FORCECAST', để nó không an toàn đúc tất cả các thời gian. Hành vi hiện tại rõ ràng là bị hỏng. Tôi sẽ xem về việc thiết lập bản thân mình và gửi một bản vá cho nó. Nó có thể là không thể, nhưng điều đúng đắn để làm có vẻ như đã tạo ra 'self' thành 'dtype' của' out ', không phải là cách khác. – Jaime

Các vấn đề liên quan