【问题标题】:Compressing/encoding strings with limited characters in Python在 Python 中压缩/编码具有有限字符的字符串
【发布时间】:2019-10-08 15:58:55
【问题描述】:

我一直在尝试寻找一种方法来编码有限的字符串以压缩数据,并为每个字符串找到唯一的“ID”。

我有几百万个字符串,每个字符串大约有 280~300 个字符,但仅限于四个字母(A、T、C 和 G)。我想知道是否没有更简单的方法来编码它们,使用更少的内存,考虑到它们应该很容易使用“base 4”进行编码,但不知道更简单的方法是什么。我考虑过在 Python 中使用 for 循环,在其中迭代每个字符串,然后使用字典为每个字母找到正确的值,并将其乘以基数为四的值。示例:

base_dict = {
    'A' : 0,
    'T' : 1,
    'C' : 2,
    'G' : 3
} # These are the four bases of DNA, each assigned a different numeric value

strings_list = [ 
'ATCG', 
'TGGGGAATATTGCACAATGGGGGAAACCCTGATGCAGCGACGCCGCGTGAGCGAAGAAGTATTTCGGTATGTAAAGCTCTATCAGCAGGGAAGAAAATGACGGTACCTGACTAAGAAGCCCCGGCTAACTACGTGCCAGCAGCCGCGGTAATACGTAGGGGGCAAGCGTTATCCGGATTTACTGGGTGTAAAGGGAGCGTAGACGGGACAGCAAGTCTGATATGAAAGGCGGGGGCTCAACCCCCGGACTGCATTGGAAACTGCTGGCCTGGAGTACCGGAGG',
'GGGGGGGGGG' 
] # A few sample DNA sequences

for string in strings_list:
    encoded_number = 0
    for i in range(len(string)):
        letter = string[i]
        encoded_number += (4**i) * base_dict[letter]
    print('String {} = {}'.format(string, encoded_number))

它似乎运行良好,将我的字符串编码为二进制格式。问题是我无法将encoded_number 转换为二进制。我能做的最好的就是使用这个:

binary = '{0:b}'.format(encoded_number)

虽然它返回了二进制值,但它会以字符串的形式返回。试图将其转换为二进制总是产生一个错误,因为整数的巨大尺寸的(使用实际280+字符的字符串时),作为长字符串上面将导致巨大的整数(230124923583823837719192000765784020788478094239354720336304458517780079994251890530919145486338353514167796587078005476564902583371606379793061574009099280577109729494013):

bytes(encoded_number) # trying to turn the encoded number into bytes
OverflowError: cannot fit 'int' into an index-sized integer

我想知道这是否是像这样对有限字符串进行编码的最有效方法,或者是否有更好的方法,以及是否有我可以使用任何其他方式进一步压缩此数据,同时仍然能够将最终数字/二进制反转回我的字符串。另外,我真的可以将其转换为二进制格式,而不是整数或字符串吗?这样做是否有助于保存数据?

另外,将整数/二进制减少为人类可读值(到新的、更短的字符串)的最简洁方法是什么?使用整数或二进制似乎可以节省数据,并且我可以使用更少的内存来存储这些字符串(并且还可以更快地传输数据),但是如果我想创建简洁的用户可读字符串,最好的选择是什么?有什么办法可以编码回一个字符串,但是利用整个 ASCII 表来使用更少的字符?

能够将我的 300 个字符串缩减为更小的 86 个字符串(考虑到 ASCII 表有 128 个可用字符,4^300 ~= 128^86)将非常有用。

我正在尝试在 Python 中执行此操作,因为它是我最熟悉的语言,也是我的代码已经使用的语言。

TL;DR,总结了我遇到的几个问题:

  1. 编码有限字符的最有效方法是什么 字符串? (上面的代码中有一个例子,是最好的 方式?)
  2. 还有其他压缩字符串的方法吗? 与有限字符的编码一起使用,以进一步 压缩数据?
  3. 大整数(4^300)能否转换成 二进制而不导致溢出?如何?
  4. 将二进制值、数字或有限字符串(在这种情况下基本相同,因为我正在尝试将一个转换为另一个)转换为小而简洁的字符串(用户可读,所以越小越好)

【问题讨论】:

  • 什么,正是抛出了这个错误:OverflowError: cannot fit 'int' into an index-sized integer?
  • 欢迎来到 StackOverflow。请按照您创建此帐户时的建议阅读并遵循帮助文档中的发布指南。 Minimal, complete, verifiable example 适用于此。在您发布 MCVE 代码并准确说明问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您指定的问题。您发布的代码中没有任何内容会引发您发布的错误片段。
  • 你在二进制输入/输出方面研究了什么?看来您的问题在于写作,而不是转换。您未能发布该代码。
  • 抱歉,已编辑问题。我只是想使用'bytes'类:bytes(encoded_number)

标签: python string encoding character-encoding compression


【解决方案1】:

您所做的转换是显而易见的:因为 4 是 2 的幂,所以对于均匀分布的序列,转换为二进制是尽可能紧凑的。您只需用 2 位序列表示每个字母,就完成了转换。

您的问题似乎在于存储结果。最短的更改可能会升级您的代码using bytes properly

另一个版本是将字符串分解为 8 个字母的块,将每个块转换为 32 位整数;然后写出整数序列(二进制)。

另一个是忘记整个转换;将字符串输入系统的压缩算法,这将利用频繁的氨基酸。

注意您的转换将丢失前导零,例如“AAAAGCTGA”;这将重新构成“GCTGA”。您需要包含预期的字符串长度。


关于做简单的块转换方法,请参考我提供的链接。

对于压缩方法,请研究压缩(我们假设您在此处发布之前已经完成,根据发布指南)。在 Linux 上,使用操作系统提供的文件压缩(可能是 gzip)。

另一种可能性是,如果您的数据中至少有两个氨基酸未出现,则对其他三元组进行编码并使用 base62(在浏览器中搜索文档)——这将使用所有字母数字字符以文本可读的形式编码。

【讨论】:

  • 谢谢,没有意识到前导零。我不太明白如何打破字符串并“编写整数序列”,你的意思是每 8 个字母打破一次,转换为二进制......然后呢?一旦它们都是二进制格式,我如何将一个二进制文件连接到另一个?至于利用频繁的氨基酸,你会建议什么样的压缩算法?我正在使用 Linux,如果有帮助的话。您是否认为使用其他形式的压缩会使上述过程变得太慢?
  • 另外,我尝试在您发布的链接中使用 's = str(n).encode()' 代码,但生成的二进制文件似乎大于整数(sys.getsizeof(n ) 返回的值小于 sys.getsizeof(s))。这是预期的吗?我的印象是变量的大小应该保持不变 - 并且还期望得到等于 '{0:b}'.format(n) 的二进制值,而不仅仅是一个编码字符串。
  • @GuilhermeCopping These 是具有 Python 内置支持的通用压缩算法。如果将这些 300 字节的字符串一一压缩,我不确定这些是否可以具有更好的压缩率。但是,如果它们被压缩成束(例如,100 个或更多字符串连接成束),我认为可能会实现更好的压缩率。至于性能,你可以自己计时,看看是否符合你的需要。
猜你喜欢
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-28
  • 1970-01-01
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
相关资源
最近更新 更多