2014-02-20 38 views
3

我已經創建了一個小函數,它作爲輸入的整數,length,並返回所有2**length整數的二進制表示的範圍內[0:2**length-1]一個numpyarray轉換十進制範圍numpy的陣列,每個比特爲一個數組元素

import numpy as np 

def get_bitstrings(length): 
    # We need to binary-fy 2^length numbers. 
    iterations = 2**length 
    # Pre-allocate memory. 
    the_array = np.zeros((iterations, length)) 
    # Go through all decimals in the range [0:iterations-1] 
    for num in range(iterations): 
    # Get binary representation in string format with 'length' zeroes padded 
    t_string = '{f_num:0{f_width}b}'.format(f_num=num, f_width=length) 
    # Convert to a Python list 
    t_list = list(t_string) 
    # Convert to Numpy array and store. 
    the_array[num,:] = np.array(t_list) 

    return the_array 

if __name__ == '__main__': 
    var1 = get_bitstrings(2) 
    var2 = get_bitstrings(3) 
    print('var1:\n{}\n'.format(var1)) 
    print('var2:\n{}\n'.format(var2)) 

其產生:

var1: 
[[ 0. 0.] 
[ 0. 1.] 
[ 1. 0.] 
[ 1. 1.]] 

var2: 
[[ 0. 0. 0.] 
[ 0. 0. 1.] 
[ 0. 1. 0.] 
[ 0. 1. 1.] 
[ 1. 0. 0.] 
[ 1. 0. 1.] 
[ 1. 1. 0.] 
[ 1. 1. 1.]] 

該方法包括獲取每個整數的二進制表示爲一個字符串(以0之前填充它,使得長度是恆定在length),將所述字符串到Python列表中,然後將列表轉換爲numpyarray

我發現這是爲了滿足每個比特是在陣列中的條目的需求的唯一方式 - 即,位串10101x4numpyarray,而不是簡單的在1x1陣列整數。但我相信有更好的選擇,因此這個問題。

可以想象,這個問題是效率低下的。我想知道是否可以通過使用Python/Numpy技巧來改善這一點。

編輯: 我用這個片段執行此操作在MATLAB:

t_length = 5; dc = [0:2^t_length-1]'; bc = rem(floor(dc * pow2( - (t_length-1):0)),2);

但是當談到Python/Numpy時,我是一個完整的noob!也許它會激勵某人。 :-)

+0

也許你可以再商量與進步,但我認爲,即使布爾變量中表示爲numpy的8位...... – deinonychusaur

+2

'np.arange(2 ** k)'會給出一個實際的「bitstring」(即'np.int')和所需的二進制值。你需要做什麼? –

+0

@deinonychusaur我需要的元素是整數或floatz,而不是布爾值。 – sudosensei

回答

4

您可以使用與NumPy的廣播和矢量操作相當有效地做到這一點:

>>> from numpy import arange, newaxis 
>>> powers_of_two = 2**arange(4)[::-1] 
>>> (arange(2**4)[:, newaxis] & powers_of_two)/powers_of_two 
array([[0, 0, 0, 0], 
     [0, 0, 0, 1], 
     [0, 0, 1, 0], 
     [0, 0, 1, 1], 
     [0, 1, 0, 0], 
     [0, 1, 0, 1], 
     [0, 1, 1, 0], 
     [0, 1, 1, 1], 
     [1, 0, 0, 0], 
     [1, 0, 0, 1], 
     [1, 0, 1, 0], 
     [1, 0, 1, 1], 
     [1, 1, 0, 0], 
     [1, 1, 0, 1], 
     [1, 1, 1, 0], 
     [1, 1, 1, 1]]) 

簡要說明:我們取0到15的所有整數(arange(2**4)),然後重塑它以給出一組形狀(16, 1)(即[:, newaxis]切片部分)。然後我們按照從最高到最低(2**arange(4)[::-1])的位數和兩個冪。整形確保按位和操作是作爲一種「外部」操作執行的:我們將arange的每個元素按位和powers_of_two數組的每個元素。這是NumPy的broadcastingslicing在工作。如果沒有明確的Python級for循環,這應該比基於for循環或列表解析的解決方案快得多。

這裏有一個稍微更時尚,而且事實證明,速度更快,沿着相同的路線可供選擇:

>>> from numpy import arange, newaxis 
>>> arange(2**4)[:,newaxis] >> arange(4)[::-1] & 1 
array([[0, 0, 0, 0], 
     [0, 0, 0, 1], 
     [0, 0, 1, 0], 
     [0, 0, 1, 1], 
     [0, 1, 0, 0], 
     [0, 1, 0, 1], 
     [0, 1, 1, 0], 
     [0, 1, 1, 1], 
     [1, 0, 0, 0], 
     [1, 0, 0, 1], 
     [1, 0, 1, 0], 
     [1, 0, 1, 1], 
     [1, 1, 0, 0], 
     [1, 1, 0, 1], 
     [1, 1, 1, 0], 
     [1, 1, 1, 1]]) 

往常一樣,如果效率是一個問題,那麼你應該好好利用了Python提供的工具形式爲timeitprofile模塊。我的機器上length=16時機似乎表明,第二方案比第一顯著快:

taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "arange(1<<16)[:, newaxis] >> arange(16)[::-1] & 1" 
100 loops, best of 3: 4.08 msec per loop 
taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "(arange(1<<16)[:, newaxis] & 2**arange(16)[::-1])/2**arange(16)[::-1]" 
10 loops, best of 3: 21.6 msec per loop 
+0

哇!這是令人難以置信的快速和優雅。謝謝,馬克! – sudosensei

1

一種方法是使用numpy.binary_repr。它會產生一個字符串,但你可以很容易地將它轉換爲一個整數或浮點數組(只需更改dtype參數)。例如:

import numpy as np 

k = 4 
print np.array([list(np.binary_repr(x, k)) for x in range(2**k)], dtype=int) 

這產生了:

[[0 0 0 0] 
[0 0 0 1] 
[0 0 1 0] 
[0 0 1 1] 
[0 1 0 0] 
[0 1 0 1] 
[0 1 1 0] 
[0 1 1 1] 
[1 0 0 0] 
[1 0 0 1] 
[1 0 1 0] 
[1 0 1 1] 
[1 1 0 0] 
[1 1 0 1] 
[1 1 1 0] 
[1 1 1 1]] 

或者,如果你想要一個更可讀的版本:

def bitstrings(k): 
    binary = [np.binary_repr(item, width=k) for item in range(2**k)] 
    return np.array([list(item) for item in binary], dtype=int) 
+0

感謝您的幫助,喬,但我寧願避免純粹迭代的解決方案,因爲這正是我一開始所做的。馬克的答案更接近我所問的。 – sudosensei

+0

是的,馬克的回答很光滑! –

相關問題