2016-08-27 18 views
2

符号が詰まった配列(つまり、エントリが1.または-1.のnumpy配列)を整数に変換してバイナリ表現に戻そうとしています。私は動作するものがありますが、それはPythonicではありません、そして、私はそれが遅くなることを期待します。Python:符号のnumpy配列をintとbackに変換する

def sign2int(s): 
    s[s==-1.] = 0. 
    bstr = '' 
    for i in range(len(s)): 
     bstr = bstr + str(int(s[i])) 
    return int(bstr, 2) 

def int2sign(i, m): 
    bstr = bin(i)[2:].zfill(m) 
    s = [] 
    for d in bstr: 
     s.append(float(d)) 
    s = np.array(s) 
    s[s==0.] = -1. 
    return s 

そして

>>> m = 4 
>>> s0 = np.array([1., -1., 1., 1.]) 
>>> i = sign2int(s0) 
>>> print i 
11 
>>> s = int2sign(i, m) 
>>> print s 
[ 1. -1. 1. 1.] 

Iは、それぞれのループのために(1)心配だと(2)文字列として中間表現を構築するために有します。

最終的に、私は例えば、ここで

>>> s = np.array([[1., -1., 1.], [1., 1., 1.]]) 
>>> print sign2int(s) 
[5, 7] 
+0

* real *データセットで試しましたか?どれくらいの大きさですか? – wwii

+0

私が見ると予想される最大のデータセットは、〜1000要素の符号配列を持ちますが、符号配列の数は数十億にものぼります---非常に高い行列です。 @wwii – user1416125

+1

ここで言及したように、私は、符号配列に最大で64要素がある場合にのみ動作すると信じています。 @wwii – user1416125

答えて

0

があなたの機能のいくつかのベクトル化バージョンです---あまりにも、2-D numpyの配列で動作する何かをしたいと思うでしょう。

def sign2int(s): 
    return int(''.join(np.where(s == -1., 0, s).astype(int).astype(str)), 2) 

def int2sign(i, m): 
    tmp = np.array(list(bin(i)[2:].zfill(m))) 
    return np.where(tmp == "0", "-1", tmp).astype(int) 

s0 = np.array([1., -1., 1., 1.]) 

sign2int(s0) 
# 11 

int2sign(11, 5) 
# array([-1, 1, -1, 1, 1]) 

へ2次元配列で関数を使用する場合は、関数を使用できます。

s = np.array([[1., -1., 1.], [1., 1., 1.]]) 

map(sign2int, s) 
# [5, 7] 

map(lambda x: int2sign(x, 4), [5, 7]) 
# [array([-1, 1, -1, 1]), array([-1, 1, 1, 1])] 
1

あなたがnp.packbitsを使用して、この1つのリニアNumpythonicアプローチを使用することができ1D配列:

>>> np.packbits(np.pad((s0+1).astype(bool).astype(int), (8-s0.size, 0), 'constant')) 
array([11], dtype=uint8) 

そして逆転させるため:

>>> unpack = (np.unpackbits(np.array([11], dtype=np.uint8))[-4:]).astype(float) 
>>> unpack[unpack==0] = -1 
>>> unpack 
array([ 1., -1., 1., 1.]) 

および2Dアレイ用:

>>> x, y = s.shape 
>>> np.packbits(np.pad((s+1).astype(bool).astype(int), (8-y, 0), 'constant')[-2:]) 
array([5, 7], dtype=uint8) 

そして逆転させるため:

>>> unpack = (np.unpackbits(np.array([5, 7], dtype='uint8'))).astype(float).reshape(x, 8)[:,-y:] 
>>> unpack[unpack==0] = -1 
>>> unpack 
array([[ 1., -1., 1.], 
     [ 1., 1., 1.]]) 
+0

Thanks @Kasramvd!私はpackbitsとunpackbitsを認識していませんでした。非常に便利ですね。 – user1416125

+0

re:[あなたの削除された回答は別の質問で(リストスワップのベンチマークで)](http://stackoverflow.com/a/39168029/224132)。削除を元に戻し、ベンチマークに関する回答にする必要があります。興味深いことに、あなたのアイデアは非常に遅く走っていて、そのうちの1つがずっと速く走っていました。 IDKには多くのPythonがありますが、他の類似したリスト操作の問題には同様の相対的なパフォーマンスを持つ類似のソリューションが選択される可能性があるので、潜在的には指摘することは有益なことです。 –

+0

@PeterCordes実際、その時までに私はかなり疲れていました(〜20時間目覚まし)。そして、私はそのひどい間違いを乗り切りたいと思っていましたが、私の解決策はまだ必要ではないと思います。残りの部分よりも長い間、私は2つの 'for'ループ(これはより多くのアンパック、複雑さ、呼び出し、スタックジョブなどを意味する)をなぜ使用したのかわかりません。複数の反復、スライシングなどがありますが、今は興味深いのはこれらの3つの回答の間のベンチマークであるかもしれないと思います。 – Kasramvd

1

私は、その後、あなたは、単にバイナリとの和を掛け、2のべき乗の配列を作成することができ、符号表現からバイナリ

>>> a 
array([ 1., -1., 1., -1.]) 
>>> (a + 1)/2 
array([ 1., 0., 1., 0.]) 
>>> 

に変換.. sig2intから始めましょう。

>>> powers = np.arange(a.shape[-1])[::-1] 
>>> np.power(2, powers) 
array([8, 4, 2, 1]) 
>>> a = (a + 1)/2 
>>> powers = np.power(2, powers) 
>>> a * powers 
array([ 8., 0., 2., 0.]) 
>>> np.sum(a * powers) 
10.0 
>>> 

次に、軸情報を追加してブロードキャストに頼って行を操作します。

def sign2int(a): 
    # powers of two 
    powers = np.arange(a.shape[-1])[::-1] 
    np.power(2, powers, powers) 
    # sign to "binary" - add one and divide by two 
    np.add(a, 1, a) 
    np.divide(a, 2, a) 
    # scale by powers of two and sum 
    np.multiply(a, powers, a) 
    return np.sum(a, axis = -1) 
>>> b = np.array([a, a, a, a, a]) 
>>> sign2int(b) 
array([ 11., 11., 11., 11., 11.]) 
>>> 

私は4 100によってビット列でそれを試してみましたが、私がそれを把握することができれば、私は逆を追加します

>>> a = a.repeat(100) 
>>> b = np.array([a, a, a, a, a]) 
>>> b 
array([[ 1., 1., 1., ..., 1., 1., 1.], 
     [ 1., 1., 1., ..., 1., 1., 1.], 
     [ 1., 1., 1., ..., 1., 1., 1.], 
     [ 1., 1., 1., ..., 1., 1., 1.], 
     [ 1., 1., 1., ..., 1., 1., 1.]]) 
>>> sign2int(b) 
array([ 2.58224988e+120, 2.58224988e+120, 2.58224988e+120, 
     2.58224988e+120, 2.58224988e+120]) 
>>> 

速いように見えました。 - 私ができる最良の方法は、単純なPythonにnumpyベクトルマジックを使わずに依存しています。私はそれを反復して一度に1つずつ変換する以外に、一連のintで動作させる方法を考えていませんでした。容認できるようです。1324年に

def foo(n): 
    '''yields bits in increasing powers of two 

    bit sequence from lsb --> msb 
    ''' 
    while n > 0: 
     n, r = divmod(n, 2) 
     yield r 

def int2sign(n): 
    n = int(n) 
    a = np.fromiter(foo(n), dtype = np.int8, count = n.bit_length()) 
    np.multiply(a, 2, a) 
    np.subtract(a, 1, a) 
    return a[::-1] 

作品:

>>> bin(1324) 
'0b10100101100' 
>>> a = int2sign(1324) 
>>> a 
array([ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1], dtype=int8) 

が1.2e305で動作するようです:テストのビットの後

>>> n = int(1.2e305) 
>>> n.bit_length() 
1014 
>>> a = int2sign(n) 
>>> a.shape 
(1014,) 

>>> s = bin(n) 
>>> s = s[2:] 
>>> all(2 * int(x) -1 == y for x, y in zip(s, a)) 
True 
>>> 
+0

あなたの 'sign2int'で注意すべき点は' 0 ** 0 = 1'です。 (少なくともそれは私のマシン上にあります) – user1416125

+0

@ user1416125 - 優秀な、良いキャッチ - 私はそれをリファクタリングしました。 – wwii

0

、文字列を使用していない@wwiiのNumpythonicアプローチ私が最も必要とするものに合うようです。 int2signについては、変換のための標準アルゴリズムを使用して指数にforループを使用しました。これは64ビット整数に対して最大64回の反復を持ちます。 Numpyの放送は各整数を非常に効率的に起こします。

packbitsおよびunpackbitsは8ビット整数に制限されています。そうでなければ、私は最高だと思う(私は試していないが)。

def _sign2int_str(s): 
    return int(''.join(np.where(s == -1., 0, s).astype(int).astype(str)), 2) 

def sign2int_str(s): 
    return np.array(map(_sign2int_str, s)) 

def _int2sign_str(i, m): 
    tmp = np.array(list(bin(i)[2:])).astype(int) 
    return np.pad(np.where(tmp == 0, -1, tmp), (m - len(tmp), 0), "constant", constant_values = -1) 

def int2sign_str(i,m): 
    return np.array(map(lambda x: _int2sign_str(x, m), i.astype(int).tolist())).transpose() 

def sign2int_np(s): 
    p = np.arange(s.shape[-1])[::-1] 
    s = s + 1 
    return np.sum(np.power(s, p), axis = -1).astype(int) 

def int2sign_np(i,m): 
    N = i.shape[-1] 
    S = np.zeros((m, N)) 
    for k in range(m): 
     b = np.power(2, m - 1 - k).astype(int) 
     S[k,:] = np.divide(i.astype(int), b).astype(float) 
     i = np.mod(i, b)   
    S[S==0.] = -1. 
    return S 

そして、ここでは私のテストである:ここで

は、私はそれが他の回答(!みんなのおかげで)での提案に従ってテストした具体的な実装である

X = np.sign(np.random.normal(size=(5000, 20))) 
N = 100 

t = time.time() 
for i in range(N): 
    S = sign2int_np(X) 
print 'sign2int_np: \t{:10.8f} sec'.format((time.time() - t)/N) 

t = time.time() 
for i in range(N): 
    S = sign2int_str(X) 
print 'sign2int_str: \t{:10.8f} sec'.format((time.time() - t)/N) 

m = 20 
S = np.random.randint(0, high=np.power(2,m), size=(5000,)) 

t = time.time() 
for i in range(N): 
    X = int2sign_np(S, m) 
print 'int2sign_np: \t{:10.8f} sec'.format((time.time() - t)/N) 

t = time.time() 
for i in range(N): 
    X = int2sign_str(S, m) 
print 'int2sign_str: \t{:10.8f} sec'.format((time.time() - t)/N) 

これは以下を生成しました結果:

sign2int_np: 0.00165325 sec 
sign2int_str: 0.04121902 sec 
int2sign_np: 0.00318024 sec 
int2sign_str: 0.24846984 sec 
0

私はnumpy.packbitsは別の価値があると思うOK。実数値の符号配列aが与えられた場合、numpy.packbits(a > 0)を使用できます。減圧はnumpy.unpackbitsによって行われます。暗黙的に多次元配列を平坦化するので、多次元配列の場合はunpackbitsの後にreshapeにする必要があります。

ビットパッキングと従来の圧縮(zlibやlzmaなど)を組み合わせることができます。データにパターンや偏りがある場合は、有効な圧縮係数が得られるかもしれませんが、偏りのないランダムデータの場合、通常はサイズが適度に増加します。

+0

ありがとう、@ジェッド!いくつかの文脈を与えるために、私はサンプリングを通してハイパーキューブのコーナー上の確率質量関数を推定しています。各サンプルはコーナーであり、各コーナーはビットストリングとして表現できます。私の現在のアプローチは、まず各サンプルをintに変換してから、整数のセットに対してnumpy.uniqueを呼び出してカウントを取得します。この文脈では、 'packbits'は' uint8'によって制限されています(私が知る限り)。だから私は 'packbits'出力を慎重に解析せずに8-dキューブより上に行くことはできませんでした。単純な除算アルゴリズムは私を64ビットintにします。これで十分です。 – user1416125

+0

@ user1416125タプルごとに 'numpy.unique'を各行に使用します。 http://stackoverflow.com/questions/31097247/remove-duplicate-rows-of-a-numpy-arrayあなたは64次元にばかげた制限はありません。また、ハッシュまたはブルームフィルタを使用して(確率的に)一意性をチェックすることもできます。 – Jed

関連する問題