优化 python 用于计算子字符串出现次数的字典创建
Optimize python dictionary creation for counting substring occurrences
函数接收包含数千行的文件名和一个K值。
该函数必须将文件的每一行分成 K 个序列,并创建一个字典,其中序列是键,值是文件中出现的次数。
问题是耗时较长(75s)
def dictionary_creator(file, k):
dictionary = dict()
for record in SeqIO.parse(file, "fasta"):
for i in range(len(record.seq) - k + 1):
kseq = str(record.seq)[i:i + k]
if kseq in dictionary:
dictionary[kseq] += 1
else:
dictionary[kseq] = 1
return dictionary
示例:
sampleC.fa
D00535:66:CCE77ANXX:5:2306:5138:1999 1:N:0:ATTACTCG+GTACTGAC
AGTCTGAGTAGCGTCGTGGTATTCCTGAAAGGNNNNNNNNNNNTNNNNNNNNNNNTGTTATGTTTACTCCTACGAATNTGATGGCGAAGTGGGCTNTTGCT
D00535:66:CCE77ANXX:5:2306:6815:1999 1:N:0:ATTACTCG+GTACTGAC
GATGATCTGCCGAAGCTCAGGAATTCGGTCGTNNNNNNNNNNNGNNNNNNNNNNNCTCTGGTCCAGCCTTTCCACGTNCTCCACTCGCATGCCGANGATGA
D00535:66:CCE77ANXX:5:2306:17450:1999 1:N:0:ATTACTCG+GTACTGAC
GGCTTCATGCTGCAGCGTGGCCTCCTCCAGGTNNNNNNNNNNNTNNNNNNNNNNNGCCTCTCTCTTCTTGTTCATCTNGATCTGGGCTGAAGTGGNNCCGC
D00535:66:CCE77ANXX:5:2306:18797:2000 1:N:0:ATTACTCG+GTACTGAC
AAGCTGTTAGTGAAATAAATGATCCTATAGAANNNNNNNNNNNTNNNNNNNNGNNAGCATCTGGGTAGTCTGAGTAGNGTCGTGGTATTCCTGAANGGCCC
D00535:66:CCE77ANXX:5:2306:1210:2146 1:N:0:ATTACTCG+GTACTGAC
GTCATGTCTTCCTTTTCAAAAAACTTCAGTTTTGTAGATTTTCGTTGAAACAGCAAGCGAAGCACCAGGTCCTCTCTTCTCATCGGAGCGTCTGCAGATCG
Python:
dictionary_creator('sampleC.fa',2)
输出:
{'AG': 26, 'GT': 31, 'TC': 42, 'CT': 38, 'TG': 32, 'GA': 24, 'TA': 13, 'GC': 25, 'CG': 18, 'GG': 20, 'AT': 22, 'TT': 27, 'CC': 20, 'AA': 23, 'GN': 5, 'NN': 79, 'NT': 6, 'TN': 9, 'AC': 7, 'CA': 19, 'NG': 7, 'NC': 3, 'AN': 3, 'NA': 1}
有一个微不足道的改进:您在内循环中执行 str(record.seq)
。此操作需要 O(n) 时间,其中 n 是字符串的长度,而您正在执行 O(n) 次,因此算法的时间复杂度为 O(n²)。但是每次的字符串都是一样的,所以你应该在外循环中只构建一次:
for record in SeqIO.parse(file, "fasta"):
seq_str = str(record.seq)
for i in range(len(seq_str) - k + 1):
kseq = seq_str[i:i + k]
# ...
应用此修复后,此简单算法的 运行 时间为 O(nk),其中 n 是字符串的长度,k 是您要计算的子字符串的长度。如果这足够好,那就太好了,你可以在这里停止阅读。
使用 suffix automaton, which is the smallest deterministic finite automaton 接受字符串的所有后缀是可能的更有效的解决方案:
Intuitively a suffix automaton can be understood as compressed form of all substrings of a given string. An impressive fact is, that the suffix automaton contains all this information in a highly compressed form. For a string of length n it only requires O(n) memory. Moreover, it can also be build in O(n) time (if we consider the size k of the alphabet as a constant), otherwise both the memory and the time complexity will be O(n log k).
想法是为record.seq
构造一个后缀自动机,然后用它来计算长度为k的每个可能子串的出现次数。
build the suffix automaton, and then O(k) time to count occurrences for each substring需要O(n)的时间。有 a^k 个可能的子字符串,其中 a 是字母表的大小(在您的示例中为 5),因此您的问题的总体 运行 时间将为 O(n + 5^k)。如果 n 比 5^k 大很多,那么这可能是一个显着的加速;但如果 k 非常小,几乎可以肯定不会。
函数接收包含数千行的文件名和一个K值。 该函数必须将文件的每一行分成 K 个序列,并创建一个字典,其中序列是键,值是文件中出现的次数。 问题是耗时较长(75s)
def dictionary_creator(file, k):
dictionary = dict()
for record in SeqIO.parse(file, "fasta"):
for i in range(len(record.seq) - k + 1):
kseq = str(record.seq)[i:i + k]
if kseq in dictionary:
dictionary[kseq] += 1
else:
dictionary[kseq] = 1
return dictionary
示例:
sampleC.fa
D00535:66:CCE77ANXX:5:2306:5138:1999 1:N:0:ATTACTCG+GTACTGAC AGTCTGAGTAGCGTCGTGGTATTCCTGAAAGGNNNNNNNNNNNTNNNNNNNNNNNTGTTATGTTTACTCCTACGAATNTGATGGCGAAGTGGGCTNTTGCT D00535:66:CCE77ANXX:5:2306:6815:1999 1:N:0:ATTACTCG+GTACTGAC GATGATCTGCCGAAGCTCAGGAATTCGGTCGTNNNNNNNNNNNGNNNNNNNNNNNCTCTGGTCCAGCCTTTCCACGTNCTCCACTCGCATGCCGANGATGA D00535:66:CCE77ANXX:5:2306:17450:1999 1:N:0:ATTACTCG+GTACTGAC GGCTTCATGCTGCAGCGTGGCCTCCTCCAGGTNNNNNNNNNNNTNNNNNNNNNNNGCCTCTCTCTTCTTGTTCATCTNGATCTGGGCTGAAGTGGNNCCGC D00535:66:CCE77ANXX:5:2306:18797:2000 1:N:0:ATTACTCG+GTACTGAC AAGCTGTTAGTGAAATAAATGATCCTATAGAANNNNNNNNNNNTNNNNNNNNGNNAGCATCTGGGTAGTCTGAGTAGNGTCGTGGTATTCCTGAANGGCCC D00535:66:CCE77ANXX:5:2306:1210:2146 1:N:0:ATTACTCG+GTACTGAC GTCATGTCTTCCTTTTCAAAAAACTTCAGTTTTGTAGATTTTCGTTGAAACAGCAAGCGAAGCACCAGGTCCTCTCTTCTCATCGGAGCGTCTGCAGATCG
Python:
dictionary_creator('sampleC.fa',2)
输出:
{'AG': 26, 'GT': 31, 'TC': 42, 'CT': 38, 'TG': 32, 'GA': 24, 'TA': 13, 'GC': 25, 'CG': 18, 'GG': 20, 'AT': 22, 'TT': 27, 'CC': 20, 'AA': 23, 'GN': 5, 'NN': 79, 'NT': 6, 'TN': 9, 'AC': 7, 'CA': 19, 'NG': 7, 'NC': 3, 'AN': 3, 'NA': 1}
有一个微不足道的改进:您在内循环中执行 str(record.seq)
。此操作需要 O(n) 时间,其中 n 是字符串的长度,而您正在执行 O(n) 次,因此算法的时间复杂度为 O(n²)。但是每次的字符串都是一样的,所以你应该在外循环中只构建一次:
for record in SeqIO.parse(file, "fasta"):
seq_str = str(record.seq)
for i in range(len(seq_str) - k + 1):
kseq = seq_str[i:i + k]
# ...
应用此修复后,此简单算法的 运行 时间为 O(nk),其中 n 是字符串的长度,k 是您要计算的子字符串的长度。如果这足够好,那就太好了,你可以在这里停止阅读。
使用 suffix automaton, which is the smallest deterministic finite automaton 接受字符串的所有后缀是可能的更有效的解决方案:
Intuitively a suffix automaton can be understood as compressed form of all substrings of a given string. An impressive fact is, that the suffix automaton contains all this information in a highly compressed form. For a string of length n it only requires O(n) memory. Moreover, it can also be build in O(n) time (if we consider the size k of the alphabet as a constant), otherwise both the memory and the time complexity will be O(n log k).
想法是为record.seq
构造一个后缀自动机,然后用它来计算长度为k的每个可能子串的出现次数。
build the suffix automaton, and then O(k) time to count occurrences for each substring需要O(n)的时间。有 a^k 个可能的子字符串,其中 a 是字母表的大小(在您的示例中为 5),因此您的问题的总体 运行 时间将为 O(n + 5^k)。如果 n 比 5^k 大很多,那么这可能是一个显着的加速;但如果 k 非常小,几乎可以肯定不会。