我正在使用一个数据集,其中包含数万个可变长度的位串,最高可达32768。
数据源自一个列表,然后我将其连接成多个二进制位串,然后将其转换为INT。
演示我的方法:
import array
a = array.array('B', [4,5,13,4,4,9,12,13])
d = dict()
for i in set(a):
d[i] = int('0b' + ''.join([str(int(i is b)) for b in a]), 2)
print d
我的问题是:是否有一种更有效的方法来实现这一点,而不构造一个~32000长度的字符串,然后添加'0b'将其转换为INT? (我假设除了操作的源列表之外,我的方法至少使用了32000个字节)
另外,INT是最便宜的存储方式,假设我希望以允许按位操作的形式存储它们吗?
答案 0 :(得分:6)
如果你需要最大的效率,那么一个可以让你非常接近的包就是bitarray
。它是用c编写的,因此速度很快。
要创建bitarray
,您可以将bitarray
构造函数传递给任何类似布尔值的值。例如:
>>> bitarray.bitarray([1, 1, 0, 1])
bitarray('1101')
>>> bitarray.bitarray([True, True, False, True])
bitarray('1101')
>>> bitarray.bitarray([range(3), [5.829048], [], ['can', 'I', 'help', 'you?']])
bitarray('1101')
我做了一些时间,确实bitarray
对于更长的字符串来说是最快的。但有一些惊喜:
bitarray
仅比int(''.join(bs))
快50%。int(''.join(bs))
比tomasz提出的shift
方法更快,a
长度大于3000,方式更快a
大于30000。len(a)
,使用shift
方法的速度也只提高了几倍。 ''.join()
为较长字符串提供的渐近性能提升大约为几秒或几十秒,而shift
方法仅为小字符串提供毫秒级的增益。因此,使用''.join()
是明显的赢家。因此,如果您不想使用外部库,那么使用''.join()
就像上面那样是最好的解决方案!事实上,在这种情况下使用外部库的好处很小;所以最后,我不会推荐它 - 除非你主要想节省内存。
最后,一个小注:您不必像上面那样将'0b'
附加到字符串。只需调用int(bitstring, 2)
- 基本参数(2
)会使'0b'
多余。
>>> import array
>>> import random
>>> import bitarray
>>>
>>> #### Definitions: ####
>>>
>>> def a_mask_join(a):
..... d = dict()
..... for i in set(a):
..... d[i] = int(''.join([str(int(i is b)) for b in a]), 2)
..... return d
.....
>>> def mask(values, x):
..... m = 0
..... for v in values:
..... m = (m << 1) + (v == x)
..... return m
.....
>>> def a_mask_shift(a):
..... d = dict()
..... for i in set(a):
..... d[i] = mask(a, i)
..... return d
.....
>>> def a_mask_bitarray1(a):
..... d = dict()
..... for i in set(a):
..... d[i] = bitarray.bitarray([int(i is b) for b in a])
..... return d
.....
>>> def a_mask_bitarray2(a):
..... d = dict()
..... for i in set(a):
..... d[i] = int(bitarray.bitarray([int(i is b) for b in a]).to01(), 2)
..... return d
.....
>>> a = array.array('B', [4,5,13,4,4,9,12,13])
>>>
>>> #### Test: ####
>>>
>>> dicts = (f(a) for f in (a_mask_join, a_mask_shift1, a_mask_shift2, a_mask_bitarray2))
>>> sorted_results = (sorted(int(v) for v in d.values()) for d in dicts)
>>> all(r == sorted(a_mask1(a).values()) for r in sorted_results)
True
>>>
>>> #### Timing: ####
>>>
>>> for size in (int(10 ** (e / 2.0)) for e in range(2, 11)):
..... print size
..... a = array.array('B', [random.randrange(0, 30) for _ in range(size)])
..... %timeit a_mask_join(a)
..... %timeit a_mask_shift(a)
..... %timeit a_mask_bitarray1(a)
..... %timeit a_mask_bitarray2(a)
.....
10
10000 loops, best of 3: 61.2 us per loop
100000 loops, best of 3: 17.5 us per loop
10000 loops, best of 3: 38.4 us per loop
10000 loops, best of 3: 46.7 us per loop
31
1000 loops, best of 3: 343 us per loop
10000 loops, best of 3: 97.9 us per loop
1000 loops, best of 3: 212 us per loop
1000 loops, best of 3: 242 us per loop
100
1000 loops, best of 3: 1.45 ms per loop
1000 loops, best of 3: 486 us per loop
1000 loops, best of 3: 825 us per loop
1000 loops, best of 3: 870 us per loop
316
100 loops, best of 3: 4.53 ms per loop
100 loops, best of 3: 2.46 ms per loop
100 loops, best of 3: 2.53 ms per loop
100 loops, best of 3: 2.65 ms per loop
1000
100 loops, best of 3: 14.5 ms per loop
100 loops, best of 3: 10.8 ms per loop
100 loops, best of 3: 7.78 ms per loop
100 loops, best of 3: 8.04 ms per loop
3162
10 loops, best of 3: 47.4 ms per loop
10 loops, best of 3: 71.8 ms per loop
10 loops, best of 3: 24.1 ms per loop
10 loops, best of 3: 25.6 ms per loop
10000
10 loops, best of 3: 137 ms per loop
1 loops, best of 3: 425 ms per loop
10 loops, best of 3: 75.7 ms per loop
10 loops, best of 3: 78 ms per loop
31622
1 loops, best of 3: 430 ms per loop
1 loops, best of 3: 3.25 s per loop
1 loops, best of 3: 241 ms per loop
1 loops, best of 3: 246 ms per loop
100000
1 loops, best of 3: 1.37 s per loop
1 loops, best of 3: 29.7 s per loop
1 loops, best of 3: 805 ms per loop
1 loops, best of 3: 800 ms per loop
答案 1 :(得分:3)
查看bitstring库。您可以操作BitArray
的各个位(以下示例显示了不可变Bits
个实例):
from bitstring import Bits
def bitLen(int_type):
length = 0
while (int_type):
int_type >>= 1
length += 1
return(length)
intList = [4,5,13,4,4,9,12,13]
intSet = set(intList)
for i in intSet:
print i, Bits(int=i, length=bitLen(i)+1).bin
提供:
9 0b01001
4 0b0100
5 0b0101
12 0b01100
13 0b01101
您可以使用Bits.int
将位表示转换为int
,并使用Bits.len
来获取长度。我会把它留给你按照你期望的字典形式工作。
答案 2 :(得分:1)
不确定我的意图是否正确,但我的理解是你想创建一个整数,它掩盖了数组中存在的值。如果是这样,您根本不需要创建字符串,只需手动执行:
def mask(values, x):
m = 0
for v in values:
m = (m << 1) + (v == x) # shift left and flip the lowest bit if needed
return m
行动中:
for i in set(a):
d[i] = mask(a, i)
它比使用连接快得多,并且不使用额外的内存,而是使用所需的整数。我打赌你可以使用itertools
中的一些方法,将它们组合在一起并给周围的人留下深刻印象,但我会坚持使用一种方法。而且我认为你是对的,无法想象存储它的方式比整数更好。