Python: convert numpy array of signs to int and back
<p>I'm trying to convert from a numpy array of signs (i.e., a numpy array whose entries are either <code>1.</code> or <code>-1.</code>) to an integer and back through a binary representation. I have something that works, but it's not Pythonic, and I expect it'll be slow.</p>

<pre><code>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
</code></pre>

<p>Then </p>

<pre><code>&gt;&gt;&gt; m = 4
&gt;&gt;&gt; s0 = np.array([1., -1., 1., 1.])
&gt;&gt;&gt; i = sign2int(s0)
&gt;&gt;&gt; print i
11
&gt;&gt;&gt; s = int2sign(i, m)
&gt;&gt;&gt; print s
[ 1. -1.  1.  1.]
</code></pre>

<p>I'm concerned about (1) the for loops in each and (2) having to build an intermediate representation as a string. </p>

<p>Ultimately, I will want something that works with a 2-d numpy array, too---e.g.,</p>

<pre><code>&gt;&gt;&gt; s = np.array([[1., -1., 1.], [1., 1., 1.]])
&gt;&gt;&gt; print sign2int(s)
[5, 7]
</code></pre>

<p>After a bit of testing, the Numpythonic approach of @wwii that doesn't use strings seems to fit what I need best. For the <code>int2sign</code>, I used a for-loop over the exponents with a standard algorithm for the conversion---which will have at most 64 iterations for 64-bit integers. Numpy's broadcasting happens across each integer very efficiently. </p>

<p><code>packbits</code> and <code>unpackbits</code> are restricted to 8-bit integers; otherwise, I suspect that would've been the best (though I didn't try).</p>

<p>Here are the specific implementations I tested that follow the suggestions in the other answers (thanks to everyone!):</p>

<pre><code>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
</code></pre>

<p>And here is my test:</p>

<pre><code>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)
</code></pre>

<p>This produced the following results:</p>

<pre><code>sign2int_np:    0.00165325 sec
sign2int_str:   0.04121902 sec
int2sign_np:    0.00318024 sec
int2sign_str:   0.24846984 sec
</code></pre>

python arrays numpy binary