【问题标题】:Strategy for optimizing speed of humungous search优化大搜索速度的策略
【发布时间】:2014-01-15 12:22:28
【问题描述】:

概述:我想找出 50,000 个 3-15 个字符长的“单词”在包含 50 到 1200 个字符长的 1 亿个“句子”的数据库中至少出现一次,没有空格但有换行符。

(为什么?这是一个蛋白质组学项目。“单词”是肽序列,例如 MRQNTWAAV,句子是完整的蛋白质序列,例如 MRQNTWAAVTGGQTNRALI... 有蛋白质组学工具可以进行搜索,但效率会更低,因为它们针对长查询字符串和非精确匹配进行了优化。)

另外,我将在一台 8 GB RAM 的普通 PC 上执行此操作。

我是 python 新手,我是一名科学家,而不是程序员;我写了一个脚本,但它很慢(在我看来)。由于我只想找出哪些术语至少出现一次,我想我可以通过以下方式加快速度:

  • 将参考数据库拆分为 500,000 个句子的 200 个部分
  • 遍历这些部分数据库,使用 mmain 将每个数据库加载到内存中
  • 将查询词列表加载到内存列表中
  • 使用 mmain 的 find(当然不是正则表达式!)遍历列表,并将未找到的术语写入新的查询术语列表中
  • 当循环转到下一个数据库时,创建一个包含较短查询词文件的新列表

这是我的代码:正如我所说,我不是程序员,所以我知道它不是最佳的。它当然适用于精简的样本集。如果有一些基本的设计功能可以帮助它更快地运行(我不在乎它是否需要一夜之间,但我希望它不会需要几天......我承认我还没有系统地计时。)

我立即想到的几件事: - 大于或小于 50 MB 的数据库文件会更优化吗? - 我确定我应该将“未找到”术语列表保留在内存中,仅在进程结束时将其写入磁盘。我这样做是为了在这个设计阶段评估过程。

import os
import mmap
import glob

os.chdir("C:/mysearch/")
searchtermfile = "original_search_terms.txt"

# load list of 50,000 search terms into memory as a list
with open(searchtermfile, 'r') as f:
    searchtermlist = [line.strip() for line in f]
    numberofsearchterms = len(searchtermlist)


#make a list of database files in the directory
dblist = glob.glob('databasepart*.txt') 
sizedblist = len(dblist)

counterdb = 0 #counts the iterations over the database files
countersearchterms = 0 #counts the iterations over the search terms
previousstring = "DUMMY" #a dummy value just for the first time it's used

#iterate first over list of file names
for nameoffile in dblist:
    counterdb += 1
    countersearchterms = 0
    #remove old notfound list, this iteration will make a new, shorter one.
    os.remove("notfound.txt") #returns an error if there is not already a notfound.txt file; I always make sure there's an empty file with that name
    #read current database file (50 MB) into memory
    with open(nameoffile, 'r+b') as f:
        m = mmap.mmap(f.fileno(), 0) #Size 0 reads entire file into memory
        #iterate over search terms
        for searchstring in searchtermlist:
            countersearchterms += 1
            if m.find(searchstring) == -1:
                with open("notfound.txt", "a") as myfile:
                    myfile.write(searchstring + "\n")
            #this print line won't be there in the final code, it's allowing me to see how fast this program runs
            print str(counterdb) + " of " + str(sizedblist) + " & " + str(countersearchterms) + " of " + str(numberofsearchterms)
            previousstring = searchstring
        m.close()
    #reload saved list of not found terms as new search term list
    with open('notfound.txt', 'r') as f:
        searchtermlist = [line.strip() for line in f]
        numberofsearchterms = len(searchtermlist)

【问题讨论】:

  • 因为你说你的代码有效,我已经更正了你明显错误的缩进;请确认您的代码(如此处所示)现在正确反映了您实际使用的内容。
  • 我会先尝试现有的工具。它们可能比您想象的更适合您的用例。
  • 你说你“当然”不使用正则表达式,但实际上我会那样做。编译的正则表达式应该是用于字符串搜索的相当有效的自动机。你需要序列可以重叠吗?如果没有,您可以采用 findall 方式,其优点是成为硬编码循环。
  • 我认为这里最大的速度增益可以通过将算法更改为不是 O(NwordNseq) 的东西来获得。像这样的事情怎么样:选择一个块长度 M。创建一个从块值到父序列的哈希映射映射。然后重复此操作,每个块的开始偏移 1、2、..、M-1。这将花费 O(NseqM)。要搜索给定的单词,请在哈希图中查找其前 M 个字符。这将为您提供要查找的匹配序列列表,该列表有望比完整序列短得多。总性能将类似于 O(Nseq*M + Nword)。
  • @MartijnPieters,谢谢,在复制粘贴制表符和空格的混合时,我的缩进某处发生了奇怪的事情(哎呀)。

标签: python performance optimization


【解决方案1】:

也许你可以尝试使用正则表达式:

>>> searchterms = ["A", "B", "AB", "ABC", "C", "BC"]
>>> # To match longest sequences first, yes need to place them at the beginning
>>> searchterms.sort(key=len, reverse=True)
>>> searchterms
['ABC', 'AB', 'BC', 'A', 'B', 'C']
>>> # Compile a big regex searching all terms together
>>> _regex =re.compile("("+"|".join(searchterms)+")")
>>> _regex.findall("ABCBADCBDACBDACBDCBADCBADBCBCBDACBDACBDACBDABCDABC")
['ABC', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'C', 'B', 'A', 'C', 'B', 'A', 'BC', 'BC', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'ABC', 'ABC']
>>> 

如果您只想计算匹配项,则可以改用finditer

【讨论】:

  • 哇。一个包含 50,000 个术语的编译正则表达式!我没想到。
  • 好吧,你还是想让电脑来做这项工作?而且您将不得不以某种方式迭代您检查的所有字符串的所有术语的所有字符。 :) 这种方法在 DokuWiki 中使用(部分较少,但正则表达式更复杂)。
  • 我尝试在普通 PC 上创建这样一个正则表达式,测试集包含 50,000 个元素,长度为 3 到 15 个字符,耗时 2.35 秒。
  • 我在一个 10MB 的字符串上对其进行了测试,它花了 26 分钟来匹配所有重叠的事件。相比之下,我不知道您自己的代码如何执行。 gist.github.com/anonymous/8438901
【解决方案2】:

我对 python 的经验较少,所以我个人会用 C 或 C++ 来做。 问题很简单,因为您只是在寻找完全匹配。

内循环是所有时间都花在的地方,所以我会专注于此。

首先,我会取出 5e4 个术语的列表,对它们进行排序,将它们放入一个表中进行二分搜索,或者(更好)将它们放入一个 trie 结构中进行逐个字母搜索。

然后,在“句子”中的每个字符位置,调用搜索函数。 它应该很快。 原则上,哈希表的性能为 O(1),但常数因子很重要。 我敢打赌,在这种情况下,特里树仍然会胜过它,而且你可以将其排除在外。

【讨论】:

    猜你喜欢
    • 2016-08-16
    • 2016-10-02
    • 2014-05-25
    • 2015-12-31
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 2018-08-14
    • 1970-01-01
    相关资源
    最近更新 更多