连接和存储长位串的最有效方法是什么?

时间:2012-02-28 22:23:58

标签: python performance

我正在使用一个数据集,其中包含数万个可变长度的位串,最高可达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是最便宜的存储方式,假设我希望以允许按位操作的形式存储它们吗?

3 个答案:

答案 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对于更长的字符串来说是最快的。但有一些惊喜:

    在大多数情况下,
  1. bitarray仅比int(''.join(bs))快50%。
  2. int(''.join(bs))tomasz提出的shift方法更快,a长度大于3000,方式更快a大于30000。
  3. 即使是小len(a),使用shift方法的速度也只提高了几倍。 ''.join()为较长字符串提供的渐近性能提升大约为几秒或几十秒,而shift方法仅为小字符串提供毫秒级的增益。因此,使用''.join()是明显的赢家。
  4. 因此,如果您不想使用外部库,那么使用''.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中的一些方法,将它们组合在一起并给周围的人留下深刻印象,但我会坚持使用一种方法。而且我认为你是对的,无法想象存储它的方式比整数更好。