簡體   English   中英

如何快速從大量的csv文件提取數據?

[英]How do I quickly extract data from this massive csv file?

我有來自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

(僅關注代碼中間的CSV部分)

您提供的示例csv文件超過2GB和77,822,354行。 在這些行中,您似乎只專注於26,804,253行或大約1/3。

作為一般建議,您可以通過以下方法加快處理速度:

  1. 避免處理您不感興趣的數據(文件的2/3);
  2. 加快識別您感興趣的數據;
  3. 避免重復數百萬次往往會變慢的操作(將每一行作為csv處理,重新組裝字符串等);
  4. 避免將所有數據分解成塊或行時讀取所有數據(內存會變緊)
  5. 使用更快的工具,例如numpypandaspypy

您的數據是面向塊的,因此您可以使用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

或者,如果您需要將所有塊組合成一個字典:

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

在我的(老式)Mac上,在PyPy中執行該操作大約需要1分鍾,而在cPython 2.7中大約需要3分鍾執行。

最好

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM