2013-04-02 28 views
0

我已經寫了一個腳本,它讀取不同的文件並在大型sdf數據庫中搜索分子ID(每個大約4.0 GB)。解析sdf文件,性能問題

這個腳本的想法是從我的原始數據庫中將每個分子從一個id列表(大約287212個分子)複製到一個新的分子,每個分子只有一個單一拷貝(在這種情況下,第一個副本遇到)

我writen這個腳本:

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() 

它的工作原理perfectely,但它是非常緩慢的...我需要什麼太慢。有沒有辦法以一種可以減少計算時間的方式來重寫此腳本?

非常感謝。

回答

1

的主要問題是,你有三個嵌套循環:分子文件,分子和文件中的內部循環解析。聞起來很麻煩 - 我的意思是,quadratic complexity。你應該移動內部循環之外的大文件解析,並使用分子集或字典。 像這樣:

  1. 對於每個SDF文件
  2. 對於每個線路,如果它是分子定義
  3. 登記unfound分子
  4. 如果存在,處理它的字典,並從unfound詞典中刪除分子

這樣,你將會精確地解析每個sdf文件一次,並且對於每個找到的分子,速度會進一步增加。

+0

是的,這是我正在測試的解決方案,它似乎更快... –

0

past_mol是一個集合,而不是一個列表。這將加快

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

由於組成員檢查是O(1),而在列表檢查成員爲O(n)。


儘量不要一次寫入文件一行。相反,保存列表中的行,將它們加入單個字符串,然後用一次性撥打filout.write將其寫出。


通常最好不要讓函數修改全局變量。 sdf_grep修改全局變量past_mol

通過增加past_molsdf_grep論點你把它明文規定sdf_grep取決於past_mol存在(否則,sdf_grep是不是一個真正的獨立的功能)。

如果您在爲第三個參數sdf_grep通過past_mol,那麼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_grep可以,如果你使用的跟蹤變量是否(讓我們稱之爲found)可以大大簡化或者我們不在我們想要保留的大塊線之中。 (通過「線塊」我的意思是一個與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() 
+0

感謝您的建議,它允許我以顯着的方式提高腳本速度。 –