TL;DR
numpyの符号無しのndarrayをシフト演算を使って符号有りに変換する方法の紹介です.
numpyのシフト演算
numpyは要素毎にシフト演算することができる.
シフト演算も例外できる.
>>> import numpy as np
>>> val = np.arange(5)
>>> val
array([0, 1, 2, 3, 4])
>>> val << 4
array([ 0, 16, 32, 48, 64], dtype=int32)
>>> val >> 1
array([0, 0, 1, 1, 2], dtype=int32)
符号無し => 符号有りに変換
astype
astype関数を使って符号無しから符号有りに変換することはできる.
8bitであれば,uint8からint8に変換すれば問題ない(16bit/32bit/64bitも同様).
>>> import numpy as np
>>> val = np.asarray([255, 0, 127, 128], dtype=np.uint8)
>>> val
array([255, 0, 127, 128], dtype=uint8)
>>> val.astype(np.int8)
array([ -1, 0, 127, -128], dtype=int8)
しかし,4bitや12bitといったデータを扱う場合こうはいかない.
(4bitや12bitといった型は存在しないため)
要素数が少ない場合は2進変換->10進変換をfor文でループしたり,frompyfuncを使えば良いが,要素数が多いと時間がかかる(numpyの意味がない).
np.whereを使うとしても要素数が多いと時間はかかってくる(インデックスアクセスが遅くなる).
そこでシフト演算を使うことで解決を図る.
シフト演算
numpyでは型の上限のbitまで左シフトしてから右シフトすると符号有りに変換される.
最上位bit=符号bitに情報が残るようである.
符号有り8bitで詳しく説明する.
1を2進で表すと以下
1 → 00000001
これを7bit左シフト
000000001 << 7
> 10000000
10進で表すと
10000000 → -128
7bit左シフトした分を右シフト
10000000 >> 7
> 00000001 (理論的にはこっち)
> 10000001 (実際はこっち)
10進で表すと
00000001 → 1
10000001 → -1
つまり,4bitだと8bit,12bitだと16bitで情報を持つことになるので,4bit左シフトして右シフトすると符号有りに変換できる.
(24bitだと32bitなので8bitシフトになる)
>>> import numpy as np
>>> val = np.arange(pow(2, 4), dtype=np.int8)
>>> val
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
dtype=int8)
>>> val << 4 >> 4
array([ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1],
dtype=int8)
まとめ
この特性を活かすことで,シフト演算を使って高速に4bitや12bitを符号無しから符号有りに変換できます.
(4bitや12bitといっていますが,何bitでもOKです)
ぜひ試してみてください.