更快速地将文件解析为数组,并与第二个文件中的数组进行比较

2 投票
1 回答
1098 浏览
提问于 2025-04-18 13:21

我现在有一个MGF文件,里面包含了MS2光谱数据(文件名是QE_2706_229_sequest_high_conf.mgf)。文件的模板和一个示例片段可以在下面的链接找到:

http://www.matrixscience.com/help/data_file_help.html

BEGIN IONS
TITLE=File3249 Spectrum10594 scans: 11084
PEPMASS=499.59366 927079.3
CHARGE=3+
RTINSECONDS=1710
SCANS=11084
104.053180 3866.360000
110.071530 178805.000000
111.068610 1869.210000
111.074780 10738.600000
112.087240 13117.900000
113.071150 7148.790000
114.102690 4146.490000
115.086840 11835.600000
116.070850 6230.980000
... ...
END IONS

这个没有注释的光谱文件里有成千上万条这样的记录,整个文件的大小大约是150MB。我还有一系列文本文件需要解析。每个文件的格式和上面类似,第一列的数据会被读入一个numpy数组中。然后,我会逐条解析这个没有注释的光谱文件,直到找到与注释文本文件中某个数组匹配的记录。

(文件名是GRPGPVAGHHQMPR)

       m/z            i matches
 104.05318      3866.4
 110.07153    178805.4
 111.06861      1869.2
 111.07478     10738.6
 112.08724     13117.9
 113.07115      7148.8
 114.10269      4146.5
 115.08684     11835.6
 116.07085      6231.0

一旦找到匹配的记录,就会生成一个带注释的MGF文件,这个文件里包含了没有注释文件中完整的记录信息,还会加上一行,说明哪个注释文本文件与这条记录匹配。输出结果如下:

BEGIN IONS
SEQ=GRPGPVAGHHQMPR
TITLE=File3249 Spectrum10594 scans: 11084
PEPMASS=499.59366 927079.3
... ...
END IONS

可能有更省计算资源的方法来进行解析。考虑到有2000个注释文件需要搜索,而上面的那个大文件,当前的解析过程在一个2.6 GHz的四核Intel Haswell处理器上大约需要12小时。

下面是可以工作的代码:

import numpy as np
import sys
from pyteomics import mgf
from glob import glob

def main():
    """
    Usage: python mgf_parser
    """

    pep_files = glob('*.txt')
    mgf_file = 'QE_2706_229_sequest_high_conf.mgf'
    process(mgf_file, pep_files)

def process(mgf_file, pep_files):
    """Parses spectra from annotated text file. Converts m/z values to numpy array.

        If spectra array matches entry in MGF file, writes annotated MGF file.
    """

    ann_arrays = {}
    for ann_spectra in pep_files:
        a = np.genfromtxt(ann_spectra, dtype=float, invalid_raise=False, 
                          usemask=False, filling_values=0.0, usecols=(0))              
        b = np.delete(a, 0)
        ann_arrays[ann_spectra] = b

    with mgf.read(mgf_file) as reader:
        for spectrum in reader:
            for ann_spectra, array in ann_arrays.iteritems():       
                if np.array_equal(array, spectrum['m/z array']):
                    print '> Spectral match found for file {}.txt'.format(ann_spectra[:-4])
                    file_name = '{}.mgf'.format(ann_spectra[:-4])
                    spectrum['params']['seq'] = file_name[52:file_name.find('-') - 1]
                    mgf.write((spectrum,), file_name)        


if __name__ == '__main__':
    main()

这个代码只能一次解析一定数量的文件。有没有更高效的解析方法的建议呢?

1 个回答

1

我觉得你可以改进的地方是,你每次处理小文件时都要重复解析整个MGF文件。如果你能把代码改成只解析一次,可能会让速度快很多。

我会这样调整你的代码,这样就能去掉bash循环,同时使用mgf.write这个函数。虽然它可能比np.savetxt慢一点,但用起来更简单:

from pyteomics import mgf
import sys
import numpy as np

def process(mgf_file, pep_files):
    ann_arrays = {}
    for ann_spectra in pep_files:
        a = np.genfromtxt(ann_spectra, invalid_raise=False,
                          filling_values=0.0, usecols=(0,))              
        b = np.delete(a, 0)
        ann_arrays[ann_spectra] = b

    with mgf.read(mgf_file) as reader:
        for spectrum in reader:
            for ann_spectra, array in ann_arrays.iteritems():
                if np.allclose(array, spectrum['m/z array']):
                # allclose may be better for floats than array_equal
                    file_name = 'DeNovo/good_training_seq/{}.mgf'.format(
                                                      ann_spectra[:-4])
                    spectrum['params']['seq'] = ann_spectra[
                                                      :ann_spectra.find('-') - 1]
                    mgf.write((spectrum,), file_name)    

if __name__ == '__main__':
     pep_files = sys.argv[1:]
     mgf_file = '/DeNovo/QE_2706_229_sequest_high_conf.mgf'
     process(mgf_file, pep_files)

然后为了实现和你的bash循环一样的效果,你可以这样调用它:

python2.7 mgf_parser.py *.txt

如果参数列表太长,你可以用glob来代替bash的展开:

from glob import iglob
pep_files = iglob(sys.argv[1])

然后这样调用它,就可以避免bash的展开了:

python2.7 mgf_parser.py '*.txt'

撰写回答