6

uint865536の値を格納するルックアップテーブル(LUT)があります。

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

uint16このLUTを使用して、 sの配列の値を変換したいと思います。

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

この最後の配列はかなり大きくなる可能性があるため、変換を適切に実行したいと思います。試してみると、次のようになります。

>>> np.take(lut, arr, out=arr)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\numpy\core\fromnumeric.py", line 103, in take
    return take(indices, axis, out, mode)
TypeError: array cannot be safely cast to required type

そして、私は何が起こっているのか理解していません。引数がないout場合、戻り値はと同じdtypeであることがわかっているlutので、uint8。しかし、なぜuint8キャストできないのuint16ですか?あなたがnumpyに尋ねるなら:

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

明らかに次の作品:

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

しかし、これも機能します。

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

int32現在、 sがsにキャストされているため、これは実際には意味がありuint16ません。これは、安全な方法ではありません。

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

lutのdtypeを、、、、uint16またはuint32のいずれuint64かに設定するとコードは機能しますが、、およびの場合は失敗します。int32int64uint8int8int16

私は何かが足りないのですか、それともこれは単にnumpyで壊れていますか?

回避策も歓迎します...LUTはそれほど大きくないので、2倍のスペースが必要な場合でも、その型を配列の型と一致させることはそれほど悪くないと思いますが、それを行うのは正しくないと感じています。 ..

キャストの安全性を気にしないようにnumpyに指示する方法はありますか?

4

1 に答える 1

2

興味深い問題です。ここでソースを見ることができるようnumpy.take(lut, ...)に変換されます:lut.take(...)

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

105 行目で例外がスローされると思います。

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

あなたの場合outはどこにありますarrdtype、の1つlutですuint8。そのため、にキャストしようとしarrますuint8が、失敗します。takeなぜそれを行う必要があるのか​​ わからないと言わざるを得ません.それを指摘するだけですdtype..lut

ところで、多くの場合、 の呼び出しPyArray_FromArrayは実際には新しい配列を作成し、置換は行われません。これは、たとえばwith(デフォルト、および例で何が起こるか)、または whenを呼び出す場合ですtakemode='raise'lut.dtype != arr.dtype。まあ、少なくともそうすべきであり、出力配列にキャストlutすると、理由は説明できませんが残っています! これは私にとって謎です - おそらくそれはNPY_ARRAY_UPDATEIFCOPYフラグと関係があります (こちらも参照)。int32uint16

結論:

  1. numpy の動作は確かに理解するのが難しいです...おそらく他の誰かがなぜそれが何をするのかについての洞察を提供するでしょう
  2. その場で処理しようとはしませんarr- とにかく、ほとんどの場合、内部で新しい配列が作成されるようです。私は単に行きますarr = lut.take(arr)- ちなみに、これは以前に によって使用されていたメモリの半分を最終的に解放しarrます。
于 2013-02-09T14:26:12.513 に答える