我有几千个位串存储为long。每个位串是1024位。我想创建一个比特数组,每个比特为1。
例如(伪代码):
bs = [
1 0 0 0,
0 1 1 0,
1 1 0 0,
0 0 0 0
]
ratios(bs) => [0.5, 0.5, 0.25 0.0]
我目前的慢代码是:
def mean_signature(bitstrings, bit_count):
means = []
for b in range(bit_count):
m = sum((x >> b) & 1 for x in bitstrings) / len(bitstrings)
means.append(m)
return means
我即将修改代码,因此外部循环超过bitstrings
,但我想我必须遗漏一些东西。也许使用numpy位数组。
答案 0 :(得分:2)
这是你可以做到的一种方式,但它可能不是最有效的方法。
对于演示,我将使用8位整数,但它也适用于1024位整数。
In [28]: bs = [0b11110000, 0b11111100, 0b11000000, 0b11111110, 0b00001100]
In [29]: bs
Out[29]: [240, 252, 192, 254, 12]
In [30]: nbits = 8
In [31]: bits = np.array([list(np.binary_repr(b, width=nbits)) for b in bs], dtype=np.uint8)
In [32]: bits
Out[32]:
array([[1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 0, 0]], dtype=uint8)
bits
是包含每个值的二进制表示位的数组。您想要的比率是列的平均值:
In [33]: bits.mean(axis=0)
Out[33]: array([ 0.8, 0.8, 0.6, 0.6, 0.6, 0.6, 0.2, 0. ])
这些值的顺序是从最高位到最低位。结果索引与通常的位索引匹配可能更自然。为此,只需反转结果:
In [34]: bits.mean(axis=0)[::-1]
Out[34]: array([ 0. , 0.2, 0.6, 0.6, 0.6, 0.6, 0.8, 0.8])
答案 1 :(得分:0)
这里最难的步骤是获取long
s的对象数组,并将它们存储在适合矢量化的numpy容器中。以下函数将long(数组)分解为little-endian单词:
def long_to_multi_word(l, dtype=np.uint64, nwords=None):
dtype = np.dtype(dtype)
l = np.asarray(l, object)
nbits = 8 * dtype.itemsize
if nwords is None:
lmax = l.max()
nwords = 0
while lmax != 0:
lmax >>= nbits
nwords += 1
arr = np.zeros(l.shape + (nwords,), dtype)
mask = (1 << nbits) - 1
for i in range(0, nwords):
arr[...,i] = l & mask
l = l >> nbits
return arr
,并提供:
>>> data = [1, 2, 3, 2**128 + 2**64 + 42] # one of these is too big to fit in a uint64
>>> data_words = long_to_multi_word(data)
>>> data_words
array([[ 1, 0, 0],
[ 2, 0, 0],
[ 3, 0, 0],
[42, 1, 1]], dtype=uint64)
现在的方法就是使用np.unpackbits
:
# could have used long_to_multi_word(data, np.uint8), but would be slower
data_bytes = data_words.view(np.uint8)
data_bits = np.unpackbits(data_bytes, axis=-1)
n_bits = data_bits.sum(axis=0)
可以将其简单地转换为平均值