【发布时间】:2015-02-10 23:57:05
【问题描述】:
我有一个 python 脚本,我在其中初始化了一个包含大约 490 万个键的字典。 Eack 键有一个包含 24 个元素的列表,我将其初始化为零。我需要解析一个包含大约 970 万行(每行 20 列)的文本文件,并根据与字典键的特定匹配,增加键的适当列表整数。
问题是解析非常慢,我的工作被杀死了(集群上最多 24 小时的挂壁时间)。要初始化的字典大小约为 200 Mb,经过一些时间检查后,我发现解析 10,000 行大约需要 16 分钟,因此解析整个 970 万行大约需要 242 小时
简而言之,我只需要对字典键的适当值进行计数和递增。是否有 Python 字典的替代数据结构可以优化此脚本并使其在合理的时间内运行?
def count_dict_init(file):
gff_file = open(file, 'r')
pos_list = []
for line in gff_file:
line_list = line.strip().split('\t')
if line.startswith('chr') and line[0:5] != 'chrmt':
if line_list[2] == 'CDS':
leftpos = int(line_list[3])
rightpos = int(line_list[4])
for position in range(leftpos - 100, rightpos + 101):
pos_list.append(position)
uniq_list = set(pos_list)
sorted_list = list(uniq_list)
sorted_list.sort()
pos_dict = {}
for pos in sorted_list:
pos_dict[pos] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '']
print 'Size of count dicitonary is ', sys.getsizeof(pos_dict)
return pos_dict
def sam_parser(sam_file, count):
dict_count = count
parsed_file = open('Sam_parsed_dict.tab', 'w')
non_cds_file = open('Non_Cds_file', 'w')
for line in sam_file:
if line[0] != '@':
fields = line.split('\t')
if len(fields) > 19:
multi_flag = fields[19].strip()
# If the read has more than one alignment then report it as multiple mapping
if multi_flag != 'NH:i:1':
multi_align = 'Y'
else:
multi_align = 'N'
else:
multi_align = 'N'
non_cds = False
sam_flag = int(fields[1])
chr_num = fields[2]
read_length = len(fields[9])
pos_in_value = (read_length - 27) * 2 #Determines which list position to update
if 27 <= read_length <= 37:
if sam_flag == 0: # Primary alignment on forward strand
five_prime = int(fields[3])
if five_prime in dict_count.keys():
dict_count[five_prime][pos_in_value] += 1
aligner_cis = dict_count[five_prime][22]
if aligner_cis == 'Y':
continue
else:
dict_count[five_prime][22] = multi_align
else:
non_cds = True
if sam_flag == 16: # On reverse strand
five_prime = int(fields[3]) + read_length - 1
if five_prime in dict_count.keys():
dict_count[five_prime][pos_in_value + 1] += 1
aligner_trans = dict_count[five_prime][23]
if aligner_trans == 'Y':
continue
else:
dict_count[five_prime][23] = multi_align
else:
non_cds = True
if sam_flag == 256: # Not primary alignment
five_prime = int(fields[3])
if five_prime in dict_count.keys():
aligner_cis = dict_count[five_prime][22]
if aligner_cis == 'Y':
continue
else:
dict_count[five_prime][22] = multi_align
else:
non_cds = True
if sam_flag == 272: # Not primary alignment and on reverse strand
five_prime = int(fields[3]) + read_length - 1
if five_prime in dict_count.keys():
aligner_trans = dict_count[five_prime][23]
if aligner_trans == 'Y':
continue
else:
dict_count[five_prime][23] = multi_align
else:
non_cds = True
if non_cds:
non_cds_file.write(str(chr_num)+'\t'+str(fields[3])+'\n')
for pos, counts in dict_count.iteritems():
parsed_file.write(str(pos)+'\t'+'\t'.join(map(str, counts))+'\n')
parsed_file.close()
non_cds_file.close()
if __name__ == "__main__":
# Parse arguments from commandline
arguments = parse_arguments()
GFF = arguments.gfffile
chrnum = arguments.chrnum
initial_count_dict = count_dict_init(GFF)
SAM = open(arguments.inputPath)
sam_parser(SAM, initial_count_dict)
【问题讨论】:
-
你试过
collections.Counter -
使用B-Tree,只将必要的项目带入内存。在决定从磁盘将什么带入内存时,B-Tree 将加快查找速度。它还假定您不会触摸 490 万个按键中的每一个。你也可以考虑一种更高效的语言(让圣战开始吧……)。
-
慢的部分是解析还是递增?如果查找/递增不是很慢,那么新的数据结构将无济于事。
-
哇。这是可怕的表现。 200MB、490 万键、970 万行现在已经不是那么大了。
-
@Joran 我不确定
collections.Counter是否合适,因为 1. dict 值是一个列表 2. 列表的最后两个值是根据字符串匹配更新的字符串 3。只有在满足几个条件后才会增加计数
标签: python optimization dictionary data-structures