簡體   English   中英

將二進制 (0|1) numpy 轉換為整數或二進制字符串?

[英]Convert binary (0|1) numpy to integer or binary-string?

是否有將二進制 (0|1) numpy 數組轉換為整數或二進制字符串的快捷方式?

b = np.array([0,0,0,0,0,1,0,1])   
  => b is 5

np.packbits(b)

有效但僅適用於 8 位值..如果 numpy 是 9 個或更多元素,它會生成 2 個或更多 8 位值。 另一種選擇是返回一個字符串 0|1 ...

我目前做的是:

    ba = bitarray()
    ba.pack(b.astype(np.bool).tostring())
    #convert from bitarray 0|1 to integer
    result = int( ba.to01(), 2 )

這是丑陋的!!!

一種方法是將dot-product2-powered range array 一起使用 -

b.dot(2**np.arange(b.size)[::-1])

樣品運行 -

In [95]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])

In [96]: b.dot(2**np.arange(b.size)[::-1])
Out[96]: 1285

或者,我們可以使用按位左移運算符來創建范圍數組,從而獲得所需的輸出,如下所示 -

b.dot(1 << np.arange(b.size)[::-1])

如果感興趣的時間 -

In [148]: b = np.random.randint(0,2,(50))

In [149]: %timeit b.dot(2**np.arange(b.size)[::-1])
100000 loops, best of 3: 13.1 µs per loop

In [150]: %timeit b.dot(1 << np.arange(b.size)[::-1])
100000 loops, best of 3: 7.92 µs per loop

逆向過程

要檢索二進制數組,請使用np.binary_reprnp.fromstring -

In [96]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])

In [97]: num = b.dot(2**np.arange(b.size)[::-1]) # integer

In [98]: np.fromstring(np.binary_repr(num), dtype='S1').astype(int)
Out[98]: array([1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1])

通過使用矢量化矩陣乘法代碼,我擴展了@Divikar 的好點積解決方案,使其在我的主機上運行速度提高了 180 倍。 一次運行一行的原始代碼需要大約 3 分鍾才能在我的 Pandas 數據框中運行 18 列的 100K 行。 好吧,下周我需要從 100K 行升級到 20M 行,所以大約 10 小時的運行時間對我來說不夠快。 首先,新代碼是矢量化的。 這是python代碼中真正的變化。 其次,matmult 通常在您看不到的情況下在多核處理器上並行運行,具體取決於您的主機配置,尤其是當存在 OpenBLAS 或其他 BLAS 供 numpy 用於像 matmult 這樣的矩陣代數時。 所以它可以使用很多處理器和內核,如果你有的話。

新的 - 非常簡單 - 代碼在大約 1 秒 ET 內在我的主機上運行 100K 行 x 18 個二進制列,這對我來說是“任務完成”:

'''
Fast way is vectorized matmult. Pass in all rows and cols in one shot.
'''
def BitsToIntAFast(bits):
  m,n = bits.shape # number of columns is needed, not bits.size
  a = 2**np.arange(n)[::-1]  # -1 reverses array of powers of 2 of same length as bits
  return bits @ a  # this matmult is the key line of code

'''I use it like this:'''
bits = d.iloc[:,4:(4+18)] # read bits from my pandas dataframe
gs = BitsToIntAFast(bits)
print(gs[:5])
gs.shape
...
d['genre'] = np.array(gs)  # add the newly computed column to pandas

希望這可以幫助。

我的時間結果:

b.dot(2**np.arange(b.size)[::-1])
100000 loops, best of 3: 2.48 usec per loop

b.dot(1 << np.arange(b.size)[::-1])
100000 loops, best of 3: 2.24 usec per loop

# Precompute powers-of-2 array with a = 1 << np.arange(b.size)[::-1]
b.dot(a)
100000 loops, best of 3: 0.553 usec per loop

# using gmpy2 is slower
gmpy2.pack(list(map(int,b[::-1])), 1)
100000 loops, best of 3: 10.6 usec per loop

因此,如果您提前知道大小,則預先計算 2 的冪數組會明顯更快。 但如果可能,您應該使用矩陣乘法同時進行所有計算,如 Geoffrey Anderson 的回答。

使用 numpy 進行轉換會將您限制為 64 位有符號二進制結果。 如果您真的想使用 numpy 並且 64 位限制適合您,則使用 numpy 的更快實現是:

import numpy as np
def bin2int(bits):
    return np.right_shift(np.packbits(bits, -1), bits.size).squeeze()

因為通常如果你使用 numpy 你關心速度那么 > 64 位結果的最快實現是:

import gmpy2
def bin2int(bits):
    return gmpy2.pack(list(bits[::-1]), 1)

如果您不想獲取對 gmpy2 的依賴,這會稍微慢一點,但沒有依賴並支持 > 64 位結果:

def bin2int(bits):
    total = 0
    for shift, j in enumerate(bits[::-1]):
        if j:
            total += 1 << shift
    return total

觀察者會注意到最后一個版本與這個問題的其他答案有一些相似之處,主要區別在於使用 << 運算符而不是 **,在我的測試中,這導致了速度的顯着提高。

def binary_converter(arr):
    total = 0
    for index, val in enumerate(reversed(arr)):
        total += (val * 2**index)
    print total


In [14]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])
In [15]: binary_converter(b)
1285
In [9]: b = np.array([0,0,0,0,0,1,0,1])
In [10]: binary_converter(b)
5

或者

b = np.array([1,0,1,0,0,0,0,0,1,0,1])
sum(val * 2**index for index, val in enumerate(reversed(b)))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM