2016-08-27 116 views
2

我試圖從numpy的符號數組(即,其條目爲1.-1.的numpy數組)轉換爲整數並通過二進制表示形式返回。我有一些工作,但它不是Pythonic,我期望它會很慢。Python:將numpy數組的符號轉換爲int並返回

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.] 

我擔心:(1)在每個和(2),其具有建立一箇中間表示爲一個字符串環路。

最終,我想要的東西,有一個2 d numpy的陣列的作品,太---例如,

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

這裏是你的職責一些量化的版本:

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-d陣列的功能,你可以使用map功能:

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

對於一維數組可以使用該一個線性Numpythonic方法,使用np.packbits

>>> 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和解壓縮包。看起來非常有用。 – user1416125

+0

回覆:[您在另一個問題上的刪除答案(與列表交換的基準)](http://stackoverflow.com/a/39168029/224132)。您應該取消刪除,並將其轉化爲有關基準的答案。有趣的是,你的想法跑得慢得多,而且其中一個跑得快得多。 IDK很多關於python的內容,但似乎其他類似的列表操作問題可能會有相似的相對性能的解決方案選擇,所以它可能是一個有用的事情,指出。 –

+0

@PeterCordes其實,那時我很疲倦(醒來〜〜20小時)而我只是想搭上那個可怕的錯誤,但現在我認爲仍然不需要我的解決方案,因爲這很明顯,我的代碼將會採取比其他的更長,我不知道爲什麼我使用2'for'循環(這意味着更多的拆包,複雜性,調用,堆棧作業等),而我在我的答案中寫道其他人正在使用多重迭代,切片等,現在我認爲唯一有趣的事情可能是這三個答案之間的基準。 – Kasramvd

1

我會sig2int開始..從正負數轉換爲二進制

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

然後,你可以簡單地創建的兩個大國的陣列,由二進制和總和乘以。

>>> 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矢量化魔術,我還沒有想到如何使它與一系列int整合,而不是迭代它們並一次一個地轉換它們 - 但時間依然似乎可以接受

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] 

作品上1324:

>>> 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的廣播非常有效地發生在每個整數上。

packbitsunpackbits限於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值得另一個lo好。給定一個實值符號數組a,可以使用numpy.packbits(a > 0)。減壓由numpy.unpackbits完成。這隱含地展平了多維數組,所以如果你有一個多維數組,你需要在unpackbits之後reshape

請注意,您可以將位打包與常規壓縮(例如zlib或lzma)結合使用。如果數據存在模式或偏差,則可能會得到一個有用的壓縮因子,但對於無偏差的隨機數據,您通常會看到適度的增加。

+0

謝謝,@Jed!爲了給出一些背景,我通過採樣來估計超立方體角上的概率質量函數,其中每個採樣都是一個角,而每個角可以表示爲一個比特串。我目前的做法是首先將每個樣本轉換爲一個int,然後調用整數集上的'numpy.unique'來獲得計數。在這種情況下,'packbits'受'uint8'限制(據我所知)。因此,如果不仔細解析'packbits'輸出,我就無法超越8維立方體。簡單的分割算法使我獲得了64位整數,現在已經足夠了。 – user1416125

+0

@ user1416125在每一行的元組上使用'numpy.unique',比較。 http://stackoverflow.com/questions/31097247/remove-duplicate-rows-of-a-numpy-array所以你沒有一個愚蠢的限制64維。也可以使用哈希或布隆過濾器來檢查唯一性(概率上)。 – Jed

相關問題