用Python比特串测量霍夫曼编码的效率

时间:2011-11-07 23:26:55

标签: python compression huffman-code bitarray bitstring

我有以下字符串,我想将Huffman编码并有效存储到位数组中:

>>> print sequence
GTCAGGACAAGAAAGACAANTCCAATTNACATTATG|

sequence中符号的频率为:

>>> print freqTuples
[(0.40540540540540543, 'A'), (0.1891891891891892, 'T'), (0.16216216216216217, 'C'), (0.16216216216216217, 'G'), (0.05405405405405406, 'N'), (0.02702702702702703, '|')]`

我把它翻译成霍夫曼代码字典:

>>> print codeDict
{'A': '1', 'C': '010', 'G': '001', 'N': '0110', 'T': '000', '|': '0111'}

然后我使用Python bitstring包将字符串逐个字符串转换为BitArray类的实例,我称之为bitArray,其中包含每个字符的位用其各自的霍夫曼代码编码:

>>> print bitArray.bin
0b001000010100100110101100111100110101101100000100101100000001101010100000010000010111

这是以字节为单位的位数组:

>>> print bitArray.tobytes()
!I\254\363[^D\260^Z\240Ap

我必须使用tobytes()而不是bytes,因为我生成的位数组不能均匀地划分为8位段。

当我计算BitArray表示的存储效率(位数组和输入字符串的大小的比率)时,我的性能会比没有编码的输入字符串差得多:

>>> sys.getsizeof(bitArray.tobytes()) / float(len(sequence))
1.2972972973

我是否正确测量存储效率? (如果我编码较长的输入字符串,这个比率会提高,但它似乎接近0.28的渐近极限。我想确认这是否是衡量事物的正确方法。)

修改

以下两种方法产生不同的答案:

>>> print len(bitArray.tobytes()) / float(len(mergedSequence))
0.297297297297

>>> print bitArray.len / (8.*len(mergedSequence))
0.283783783784

我不确定该相信哪一个。但是在将数据写入存储的过程中,我认为我需要字节表示,这使我倾向于选择第一个结果。

3 个答案:

答案 0 :(得分:2)

我不太确定比特币的东西,但你不应该只能这样做:

>>> len(bitArray.tobytes()) / float(len(sequence))

我并不是说这会解决你的问题,但可能是“getizeof”的事情(再次,我并不是那么熟悉的事情)会让你失望。

根据你在那里所写的内容,你看起来有点像将苹果与橙子进行比较。

答案 1 :(得分:2)

>>> sys.getsizeof(bitArray.tobytes()) / float(len(sequence))
1.2972972973

表示编码版本比原始序列长30%

我认为你不想在这里使用getsizeof - 如果你想最小化Python对象的大小,你也应该使用getsizeof(sequence),而不是len }。

相反,如果您希望执行霍夫曼编码要做的事情,并最小化二进制表示,那么您希望在两者上使用len (假设序列表示为一个字节的每一个字符)。

所以,你的真实比例是11/37。

我假设您正在使用霍夫曼编码作为练习,因为这似乎不是一种有效存储仅具有终止字符的四位代码的逻辑方法。至少最好使用算术编码,这将允许您使用base-5编码而不是base-2,这对于5个可能的字符是最佳的。

实际上,我假设在一个足够长的序列中值得压缩,有一个已知比例的G:A:C:T和/或固定长度2位编码同样有效(比率接近1) :1:1:1)因为你真的不需要编码终止字符。

答案 2 :(得分:1)

你知道答案是错误的,因为霍夫曼字典每个字符少于4位,所以真正的答案必须小于.5。如果字典和字符频率对于较长的字符串没有变化,则压缩比不应随着字符串变长而逐渐减小到渐近限制。

来自sys:

的文档
"getsizeof() calls the object’s __sizeof__ method and adds
 an additional garbage collector overhead if the object is
 managed by the garbage collector."

你需要一个函数来返回bitstring本身的长度,而不是bitstring + overhead。 BitString文档说明lenlength属性以位为单位返回长度。所以试着这样做:

bitArray.len / 8.*len(sequence)