如何从这个庞大的csv文件中快速提取数据?

时间:2017-05-05 17:47:44

标签: python bioinformatics

我有来自16个细胞核的基因组数据。第一列代表细胞核,接下来的两列分别代表支架(基因组的部分)和支架上的位置,最后两列分别代表核苷酸和覆盖。在不同的细胞核中可以有相同的支架和位置。

使用输入作为开始和结束位置(每个的支架和位置),我应该输出一个csv文件,该文件显示从开始到结束范围内每个核的数据(核苷酸和覆盖范围)。我想通过16列(每个核一个),然后从上到下显示数据来做到这一点。最左边的区域将是该范围内的参考基因组,我通过为每个支架创建字典来访问该区域。

在我的代码中,我有一个列表的默认值,因此键是一个组合了脚手架和位置的字符串,而数据是一个列表数组,因此对于每个核,数据可以附加到相同的位置,最后每个位置都有来自每个核心的数据。

当然,这很慢。我该怎么做呢?

代码:

#let's plan this
#input is start and finish - when you hit first, add it and keep going until you hit next or larger
#dictionary of arrays
#loop through everything, output data for each nucleus

import csv
from collections import defaultdict

inrange = 0
start = 'scaffold_41,51335'
end = 'scaffold_41|51457'
locations = defaultdict(list)
count = 0

genome = defaultdict(lambda : defaultdict(dict))
scaffold = ''
for line in open('Allpaths_SL1_corrected.fasta','r'):
    if line[0]=='>':
        scaffold = line[1:].rstrip()
    else:
        genome[scaffold] = line.rstrip()
print('Genome dictionary done.')

with open('automated.csv','rt') as read:
    for line in csv.reader(read,delimiter=','):
        if line[1] + ',' + line[2] == start:
            inrange = 1
        if inrange == 1:
            locations[line[1] + ',' + line[2]].append([line[3],line[4]])
        if line[1] + ',' + line[2] == end:
            inrange = 0
        count += 1
        if count%1000000 == 0:
            print('Checkpoint '+str(count)+'!')

with open('region.csv','w') as fp:
    wr = csv.writer(fp,delimiter=',',lineterminator='\n')
    for key in locations:
        nuclei = []
        for i in range(0,16):
            try:
                nuclei.append(locations[key][i])
            except IndexError:
                nuclei.append(['',''])
        wr.writerow([genome[key[0:key.index(',')][int(key[key.index(',')+1:])-1],key,nuclei])
print('Done!')

文件: https://drive.google.com/file/d/0Bz7WGValdVR-bTdOcmdfRXpUYUE/view?usp=sharing https://drive.google.com/file/d/0Bz7WGValdVR-aFdVVUtTbnI2WHM/view?usp=sharing

1 个答案:

答案 0 :(得分:0)

(仅关注代码中间的CSV部分)

您提供的示例csv文件超过2GB和77,822,354行。在这些行中,您似乎只关注26,804,253行或约1/3。

作为一般性建议,您可以加快速度:

  1. 避免处理您不感兴趣的数据(文件的2/3);
  2. 加快识别您感兴趣的数据;
  3. 避免重复数百万次往往较慢的事情(将每一行作为csv处理,重新组装字符串等);
  4. 当你可以把它分解成块或行时(内存会变紧),避免读取所有数据。
  5. 使用更快的工具,例如numpypandaspypy
  6. 您的数据是面向块的,因此您可以使用FlipFlop类型对象来感知您是否在某个块中。

    你的csv的第一列是数字,所以你可以使用速度更快的Python in运算符来查找块的开头和结尾,而不是将行分开并重新组合两列:

    start = ',scaffold_41,51335,'
    end = ',scaffold_41,51457,'
    
    class FlipFlop: 
        def __init__(self, start_pattern, end_pattern):
            self.patterns = start_pattern, end_pattern
            self.state = False
        def __call__(self, st):
            rtr=True if self.state else False
            if self.patterns[self.state] in st:
                self.state = not self.state
            return self.state or rtr
    
    lines_in_block=0    
    with open('automated.csv') as f:
        ff=FlipFlop(start, end)
        for lc, line in enumerate(f):
            if ff(line):
                lines_in_block+=1
    
    print lines_in_block, lc
    

    打印:

    26804256 77822354
    

    在PyPy中运行大约9秒,在Python 2.7中运行46秒。

    然后,您可以获取读取源csv文件的部分并将其转换为生成器,这样您一次只需要处理一个数据块。

    (当然不正确,因为我花了很长时间试图整体理解你的文件..):

    def csv_bloc(fn, start_pat, end_pat):
        from itertools import ifilter 
        with open(fn) as csv_f:
        ff=FlipFlop(start_pat, end_pat)
        for block in ifilter(ff, csv_f):
            yield block
    

    或者,如果您需要将所有块组合成一个dict:

    def csv_line(fn, start, end):
        with open(fn) as csv_in:
            ff=FlipFlop(start, end)
            for line in csv_in:
                if ff(line):
                    yield line.rstrip().split(",")              
    
    di={}               
    for row in csv_line('/tmp/automated.csv', start, end):
        di.setdefault((row[2],row[3]), []).append([row[3],row[4]])
    

    在PyPy中我的(旧)Mac上大约需要1分钟,在cPython 2.7中大约需要3分钟。

    最佳