我正在处理一个数据集,其中包含数万个可变长度的位串,最长可达 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 是不是最便宜的存储方式?
请您参考如下方法:
如果您需要最大效率,一个应该让您非常接近的包是 bitarray .它是用 C 语言编写的,所以速度快如闪电。
要创建bitarray,您可以向bitarray 构造函数传递任何类似 bool 值的序列。例如:
>>> 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方法更快对于大于 3000 的a长度,对于大于 30000 的a长度,方式更快。- 即使对于小的
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
