0

我已经编写了一个脚本,它可以读取不同的文件并在大型 sdf 数据库(每个大约 4.0 GB)中搜索分子 ID。

这个脚本的想法是将每个分子从我的原始数据库中的 id 列表(大约 287212 个分子)复制到一个新数据库中,以使每个分子只有一个副本(在这种情况下,遇到的第一个副本)

我写了这个脚本:

import re
import sys
import os

def sdf_grep (molname,files):
    filin = open(files, 'r')
    filine= filin.readlines()
    for i in range(0,len(filine)):
        if filine[i][0:-1] == molname and filine[i][0:-1] not in past_mol:
            past_mol.append(filine[i][0:-1])
            iterate = 1
            while iterate == 1:
                if filine[i] == "$$$$\n":
                    filout.write(filine[i])
                    iterate = 0
                    break
                else:
                    filout.write(filine[i])
                i = i+1
        else:
            continue
    filin.close()

mol_dock = os.listdir("test")
listmol = []
past_mol = []
imp_listmol = open("consensus_sorted_surflex.txt", 'r')
filout = open('test_ini.sdf','wa')

for line in imp_listmol:
    listmol.append(line.split('\t')[0])
print 'list ready... reading files'
imp_listmol.close()

for f in mol_dock:
    print 'reading '+f
    for molecule in listmol:
        if molecule not in past_mol:
            sdf_grep(molecule , 'test/'+f) 

print len(past_mol)
filout.close()

它工作得很好,但它很慢......对于我需要的东西来说太慢了。有没有办法以可以减少计算时间的方式重写这个脚本?

非常感谢你。

4

2 回答 2

1

让我们past_mol成为一个集合,而不是一个列表。这样会加快

filine[i][0:-1] not in past_mol

因为检查集合中的成员是 O(1),而检查列表中的成员是 O(n)。


尽量不要一次写入一行。相反,将行保存在一个列表中,将它们连接成一个字符串,然后通过调用filout.write.


通常最好不要让函数修改全局变量。sdf_grep修改全局变量past_mol

通过添加past_mol您的参数,sdf_grep使其明确sdf_grep取决于是否存在past_mol(否则,sdf_grep它并不是真正的独立函数)。

如果你past_mol作为第三个参数传入sdf_grep,那么 Python 将创建一个名为的新局部变量past_mol,该变量将指向与全局变量相同的对象past_mol。由于该对象是一个集合,而一个集合是一个可变对象,past_mol.add(sline)因此也会影响全局变量past_mol

作为一个额外的好处,Python 查找局部变量的速度比全局变量快:

def using_local():
    x = set()
    for i in range(10**6):
        x

y = set
def using_global():
    for i in range(10**6):
        y

In [5]: %timeit using_local()
10 loops, best of 3: 33.1 ms per loop

In [6]: %timeit using_global()
10 loops, best of 3: 41 ms per loop

sdf_grepfound如果您使用一个变量(让我们称之为它)来跟踪我们是否在我们想要保留的行块之一内,则可以大大简化。(我所说的“行块”是指以 开头molname和结尾的行"$$$$"):

import re
import sys
import os

def sdf_grep(molname, files, past_mol):
    chunk = []
    found = False
    with open(files, 'r') as filin:
        for line in filin:
            sline = line.rstrip()
            if sline == molname and sline not in past_mol:
                found = True
                past_mol.add(sline)
            elif sline == '$$$$':
                chunk.append(line)                
                found = False
            if found:
                chunk.append(line)
    return '\n'.join(chunk)


def main():
    past_mol = set()
    with open("consensus_sorted_surflex.txt", 'r') as imp_listmol:
        listmol = [line.split('\t')[0] for line in imp_listmol]
        print 'list ready... reading files'

    with open('test_ini.sdf', 'wa') as filout:
        for f in os.listdir("test"):
            print 'reading ' + f
            for molecule in listmol:
                if molecule not in past_mol:
                    filout.write(sdf_grep(molecule, os.path.join('test/', f), past_mol))

        print len(past_mol)

if __name__ == '__main__':
    main()
于 2013-04-02T13:26:34.357 回答
1

主要问题是您有三个嵌套循环:内部循环中的分子文档、分子和文件解析。这闻起来像麻烦——我的意思是,二次复杂度。您应该将解析的大文件移到内部循环之外,并为分子使用集合或字典。像这样的东西:

  1. 对于每个 sdf 文件
  2. 对于每一行,如果是分子定义
  3. 检查未发现分子的字典
  4. 如果存在,对其进行处理并从未找到分子的字典中删除

这样,您将准确地解析每个 sdf 文件一次,并且对于每个找到的分子,速度将进一步提高。

于 2013-04-02T13:48:58.210 回答